Merge "mw.Upload.BookletLayout: Don't explode when the API call fails with 'exception'"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Fri, 22 Jan 2016 19:54:32 +0000 (19:54 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Fri, 22 Jan 2016 19:54:32 +0000 (19:54 +0000)
816 files changed:
CREDITS
Gruntfile.js
HISTORY
README
RELEASE-NOTES-1.27
autoload.php
composer.json
composer.local.json-sample [new file with mode: 0644]
docs/extension.schema.json
docs/hooks.txt
includes/AjaxDispatcher.php
includes/AjaxResponse.php
includes/Block.php
includes/CategoryViewer.php
includes/DefaultSettings.php
includes/DerivativeRequest.php [new file with mode: 0644]
includes/Export.php [deleted file]
includes/FauxRequest.php [new file with mode: 0644]
includes/GitInfo.php
includes/GlobalFunctions.php
includes/Import.php [deleted file]
includes/Linker.php
includes/MediaWiki.php
includes/OutputPage.php
includes/PHPVersionCheck.php
includes/PageProps.php [new file with mode: 0644]
includes/Preferences.php
includes/Revision.php
includes/Sanitizer.php
includes/Setup.php
includes/Title.php
includes/WebRequest.php
includes/WebRequestUpload.php
includes/WebResponse.php
includes/WebStart.php
includes/Xml.php
includes/ZhConversion.php
includes/actions/Action.php
includes/actions/DeleteAction.php
includes/actions/EditAction.php
includes/actions/FormAction.php
includes/actions/InfoAction.php
includes/actions/MarkpatrolledAction.php
includes/actions/ProtectAction.php
includes/actions/PurgeAction.php
includes/actions/RawAction.php
includes/actions/RevertAction.php
includes/actions/RevisiondeleteAction.php
includes/actions/RollbackAction.php
includes/actions/SpecialPageAction.php
includes/actions/SubmitAction.php
includes/actions/UnprotectAction.php
includes/actions/UnwatchAction.php
includes/actions/WatchAction.php
includes/api/ApiBase.php
includes/api/ApiBlock.php
includes/api/ApiCreateAccount.php
includes/api/ApiFormatRaw.php
includes/api/ApiHelp.php
includes/api/ApiLogin.php
includes/api/ApiLogout.php
includes/api/ApiMain.php
includes/api/ApiParamInfo.php
includes/api/ApiQuery.php
includes/api/ApiQueryBlocks.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryLogEvents.php
includes/api/ApiQueryMyStashedFiles.php [new file with mode: 0644]
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryUserContributions.php
includes/api/ApiQueryUsers.php
includes/api/ApiQueryWatchlist.php
includes/api/ApiResult.php
includes/api/ApiRevisionDelete.php
includes/api/ApiRollback.php
includes/api/ApiTag.php
includes/api/ApiUndelete.php
includes/api/ApiUserrights.php
includes/api/i18n/ast.json
includes/api/i18n/bg.json [new file with mode: 0644]
includes/api/i18n/ce.json
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/es.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/lb.json
includes/api/i18n/lki.json
includes/api/i18n/my.json [new file with mode: 0644]
includes/api/i18n/nap.json
includes/api/i18n/nl.json
includes/api/i18n/ps.json
includes/api/i18n/pt-br.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/sr-ec.json
includes/api/i18n/te.json
includes/api/i18n/tr.json
includes/api/i18n/tt-cyrl.json [new file with mode: 0644]
includes/api/i18n/uk.json
includes/api/i18n/vi.json
includes/api/i18n/zh-hans.json
includes/api/i18n/zh-hant.json
includes/cache/HTMLFileCache.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/OldChangesList.php
includes/changes/RecentChange.php
includes/changetags/ChangeTags.php
includes/compat/normal/UtfNormalUtil.php
includes/context/ContextSource.php
includes/context/RequestContext.php
includes/db/DBConnRef.php
includes/db/Database.php
includes/db/DatabaseMysqlBase.php
includes/db/IDatabase.php
includes/db/loadbalancer/LBFactory.php
includes/db/loadbalancer/LBFactoryMulti.php
includes/db/loadbalancer/LBFactorySimple.php
includes/db/loadbalancer/LBFactorySingle.php
includes/db/loadbalancer/LoadBalancer.php
includes/db/loadbalancer/LoadMonitorMySQL.php
includes/debug/MWDebug.php
includes/debug/logger/LegacyLogger.php
includes/debug/logger/LegacySpi.php
includes/debug/logger/LoggerFactory.php
includes/debug/logger/MonologSpi.php
includes/debug/logger/NullSpi.php
includes/debug/logger/monolog/AvroFormatter.php
includes/debug/logger/monolog/LegacyHandler.php
includes/debug/logger/monolog/LineFormatter.php
includes/debug/logger/monolog/SyslogHandler.php
includes/deferred/AtomicSectionUpdate.php [new file with mode: 0644]
includes/deferred/CallableUpdate.php
includes/deferred/DeferredUpdates.php
includes/diff/DairikiDiff.php
includes/diff/DifferenceEngine.php
includes/exception/MWExceptionHandler.php
includes/export/Dump7ZipOutput.php [new file with mode: 0644]
includes/export/DumpBZip2Output.php [new file with mode: 0644]
includes/export/DumpDBZip2Output.php [new file with mode: 0644]
includes/export/DumpFileOutput.php [new file with mode: 0644]
includes/export/DumpFilter.php [new file with mode: 0644]
includes/export/DumpGZipOutput.php [new file with mode: 0644]
includes/export/DumpLatestFilter.php [new file with mode: 0644]
includes/export/DumpMultiWriter.php [new file with mode: 0644]
includes/export/DumpNamespaceFilter.php [new file with mode: 0644]
includes/export/DumpNotalkFilter.php [new file with mode: 0644]
includes/export/DumpOutput.php [new file with mode: 0644]
includes/export/DumpPipeOutput.php [new file with mode: 0644]
includes/export/WikiExporter.php [new file with mode: 0644]
includes/export/XmlDumpWriter.php [new file with mode: 0644]
includes/filebackend/FSFileBackend.php
includes/filebackend/FileOpBatch.php
includes/filerepo/FileRepo.php
includes/filerepo/ForeignAPIRepo.php
includes/filerepo/file/File.php
includes/filerepo/file/ForeignAPIFile.php
includes/filerepo/file/ForeignDBFile.php
includes/filerepo/file/LocalFile.php
includes/htmlform/HTMLButtonField.php
includes/htmlform/HTMLForm.php
includes/htmlform/HTMLFormField.php
includes/htmlform/OOUIHTMLForm.php
includes/import/ImportSource.php [new file with mode: 0644]
includes/import/ImportStreamSource.php [new file with mode: 0644]
includes/import/ImportStringSource.php [new file with mode: 0644]
includes/import/UploadSourceAdapter.php [new file with mode: 0644]
includes/import/WikiImporter.php [new file with mode: 0644]
includes/import/WikiRevision.php [new file with mode: 0644]
includes/installer/Installer.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/WebInstallerComplete.php [new file with mode: 0644]
includes/installer/WebInstallerCopying.php [new file with mode: 0644]
includes/installer/WebInstallerDBConnect.php [new file with mode: 0644]
includes/installer/WebInstallerDBSettings.php [new file with mode: 0644]
includes/installer/WebInstallerDocument.php [new file with mode: 0644]
includes/installer/WebInstallerExistingWiki.php [new file with mode: 0644]
includes/installer/WebInstallerInstall.php [new file with mode: 0644]
includes/installer/WebInstallerLanguage.php [new file with mode: 0644]
includes/installer/WebInstallerName.php [new file with mode: 0644]
includes/installer/WebInstallerOptions.php [new file with mode: 0644]
includes/installer/WebInstallerPage.php
includes/installer/WebInstallerReadme.php [new file with mode: 0644]
includes/installer/WebInstallerReleaseNotes.php [new file with mode: 0644]
includes/installer/WebInstallerRestart.php [new file with mode: 0644]
includes/installer/WebInstallerUpgrade.php [new file with mode: 0644]
includes/installer/WebInstallerUpgradeDoc.php [new file with mode: 0644]
includes/installer/WebInstallerWelcome.php [new file with mode: 0644]
includes/installer/i18n/ast.json
includes/installer/i18n/be-tarask.json
includes/installer/i18n/bg.json
includes/installer/i18n/ce.json
includes/installer/i18n/cs.json
includes/installer/i18n/de.json
includes/installer/i18n/el.json
includes/installer/i18n/en.json
includes/installer/i18n/es.json
includes/installer/i18n/fa.json
includes/installer/i18n/fr.json
includes/installer/i18n/gl.json
includes/installer/i18n/hr.json
includes/installer/i18n/it.json
includes/installer/i18n/ko.json
includes/installer/i18n/ksh.json
includes/installer/i18n/lki.json
includes/installer/i18n/mk.json
includes/installer/i18n/nl.json
includes/installer/i18n/qqq.json
includes/installer/i18n/ru.json
includes/installer/i18n/sv.json
includes/installer/i18n/tt-cyrl.json
includes/installer/i18n/uk.json
includes/installer/i18n/wuu.json
includes/installer/i18n/zh-hans.json
includes/interwiki/Interwiki.php
includes/jobqueue/JobQueue.php
includes/jobqueue/JobQueueMemory.php [new file with mode: 0644]
includes/jobqueue/JobQueueRedis.php
includes/jobqueue/JobRunner.php
includes/jobqueue/jobs/UploadFromUrlJob.php
includes/libs/composer/ComposerJson.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/logging/LogEntry.php
includes/logging/LogPage.php
includes/objectcache/ObjectCacheSessionHandler.php [deleted file]
includes/page/Article.php
includes/page/WikiPage.php
includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/parser/Preprocessor_DOM.php
includes/parser/Preprocessor_Hash.php
includes/rcfeed/IRCColourfulRCFeedFormatter.php
includes/registration/ExtensionProcessor.php
includes/registration/ExtensionRegistry.php
includes/registration/Processor.php
includes/resourceloader/ResourceLoader.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderStartUpModule.php
includes/resourceloader/ResourceLoaderUserCSSPrefsModule.php
includes/search/SearchEngine.php
includes/search/SearchNearMatchResultSet.php [new file with mode: 0644]
includes/search/SearchResultSet.php
includes/search/SqlSearchResultSet.php [new file with mode: 0644]
includes/session/BotPasswordSessionProvider.php [new file with mode: 0644]
includes/session/CookieSessionProvider.php [new file with mode: 0644]
includes/session/ImmutableSessionProviderWithCookie.php [new file with mode: 0644]
includes/session/PHPSessionHandler.php [new file with mode: 0644]
includes/session/Session.php [new file with mode: 0644]
includes/session/SessionBackend.php [new file with mode: 0644]
includes/session/SessionId.php [new file with mode: 0644]
includes/session/SessionInfo.php [new file with mode: 0644]
includes/session/SessionManager.php [new file with mode: 0644]
includes/session/SessionManagerInterface.php [new file with mode: 0644]
includes/session/SessionProvider.php [new file with mode: 0644]
includes/session/SessionProviderInterface.php [new file with mode: 0644]
includes/session/UserInfo.php [new file with mode: 0644]
includes/site/MediaWikiPageNameNormalizer.php [new file with mode: 0644]
includes/site/MediaWikiSite.php
includes/skins/BaseTemplate.php
includes/skins/Skin.php
includes/skins/SkinTemplate.php
includes/specialpage/ChangesListSpecialPage.php
includes/specialpage/QueryPage.php
includes/specialpage/SpecialPage.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialActiveusers.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialBlock.php
includes/specials/SpecialBlockList.php
includes/specials/SpecialBotPasswords.php [new file with mode: 0644]
includes/specials/SpecialBrokenRedirects.php
includes/specials/SpecialChangeContentModel.php
includes/specials/SpecialChangeEmail.php
includes/specials/SpecialChangePassword.php
includes/specials/SpecialComparePages.php
includes/specials/SpecialConfirmemail.php
includes/specials/SpecialContributions.php
includes/specials/SpecialCreateAccount.php
includes/specials/SpecialDeletedContributions.php
includes/specials/SpecialEditTags.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialEmailInvalidate.php [new file with mode: 0644]
includes/specials/SpecialEmailuser.php
includes/specials/SpecialExpandTemplates.php
includes/specials/SpecialExport.php
includes/specials/SpecialFileDuplicateSearch.php
includes/specials/SpecialImport.php
includes/specials/SpecialJavaScriptTest.php
includes/specials/SpecialListDuplicatedFiles.php
includes/specials/SpecialListfiles.php
includes/specials/SpecialListgrants.php [new file with mode: 0644]
includes/specials/SpecialListgrouprights.php
includes/specials/SpecialLockdb.php
includes/specials/SpecialLog.php
includes/specials/SpecialMediaStatistics.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialNewimages.php
includes/specials/SpecialPageLanguage.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialRandomrootpage.php [new file with mode: 0644]
includes/specials/SpecialRecentchanges.php
includes/specials/SpecialRedirect.php
includes/specials/SpecialResetTokens.php
includes/specials/SpecialRevisiondelete.php
includes/specials/SpecialRunJobs.php
includes/specials/SpecialShortpages.php
includes/specials/SpecialUnblock.php
includes/specials/SpecialUndelete.php
includes/specials/SpecialUnlockdb.php
includes/specials/SpecialUpload.php
includes/specials/SpecialUploadStash.php
includes/specials/SpecialUserlogin.php
includes/specials/SpecialUserlogout.php
includes/specials/SpecialUserrights.php
includes/specials/SpecialVersion.php
includes/specials/SpecialWatchlist.php
includes/templates/EnhancedChangesListGroup.mustache [new file with mode: 0644]
includes/upload/UploadFromStash.php
includes/upload/UploadFromUrl.php
includes/user/BotPassword.php [new file with mode: 0644]
includes/user/CentralIdLookup.php
includes/user/User.php
includes/user/UserNamePrefixSearch.php [new file with mode: 0644]
includes/utils/MWCryptHKDF.php
includes/utils/MWGrants.php [new file with mode: 0644]
includes/utils/MWRestrictions.php [new file with mode: 0644]
includes/utils/UIDGenerator.php
languages/FakeConverter.php
languages/Language.php
languages/LanguageConverter.php
languages/classes/LanguageOs.php
languages/i18n/ar.json
languages/i18n/arq.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/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bgn.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/crh-cyrl.json
languages/i18n/crh-latn.json
languages/i18n/cs.json
languages/i18n/cu.json
languages/i18n/cv.json
languages/i18n/da.json
languages/i18n/de.json
languages/i18n/diq.json
languages/i18n/el.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es.json
languages/i18n/et.json
languages/i18n/eu.json
languages/i18n/ext.json
languages/i18n/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/frr.json
languages/i18n/gd.json
languages/i18n/gl.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hif-latn.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/id.json
languages/i18n/ilo.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/ka.json
languages/i18n/khw.json
languages/i18n/kk-cyrl.json
languages/i18n/kn.json
languages/i18n/ko.json
languages/i18n/krc.json
languages/i18n/ksh.json
languages/i18n/ku-latn.json
languages/i18n/la.json
languages/i18n/lb.json
languages/i18n/lij.json
languages/i18n/lki.json
languages/i18n/lrc.json
languages/i18n/lt.json
languages/i18n/lv.json
languages/i18n/mai.json
languages/i18n/mk.json
languages/i18n/ml.json
languages/i18n/mr.json
languages/i18n/my.json
languages/i18n/nah.json
languages/i18n/nan.json
languages/i18n/nap.json
languages/i18n/nb.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/olo.json
languages/i18n/or.json
languages/i18n/pam.json
languages/i18n/pl.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/rm.json
languages/i18n/ro.json
languages/i18n/ru.json
languages/i18n/sa.json
languages/i18n/sd.json
languages/i18n/sgs.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/ta.json
languages/i18n/te.json
languages/i18n/tg-cyrl.json
languages/i18n/th.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/ug-arab.json
languages/i18n/uk.json
languages/i18n/ur.json
languages/i18n/vep.json
languages/i18n/vi.json
languages/i18n/war.json
languages/i18n/wuu.json
languages/i18n/xmf.json
languages/i18n/yi.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
languages/messages/MessagesAr.php
languages/messages/MessagesArz.php
languages/messages/MessagesBgn.php
languages/messages/MessagesDe.php
languages/messages/MessagesDiq.php
languages/messages/MessagesEn.php
languages/messages/MessagesEt.php
languages/messages/MessagesFa.php
languages/messages/MessagesGlk.php
languages/messages/MessagesJbo.php [new file with mode: 0644]
languages/messages/MessagesKo.php
languages/messages/MessagesLv.php
languages/messages/MessagesMk.php
languages/messages/MessagesNl.php
languages/messages/MessagesVi.php
languages/messages/MessagesZh_hans.php
languages/messages/MessagesZh_hant.php
load.php
maintenance/Maintenance.php
maintenance/archives/patch-bot_passwords.sql [new file with mode: 0644]
maintenance/archives/upgradeLogging.php
maintenance/attachLatest.php
maintenance/backup.inc
maintenance/backupTextPass.inc [deleted file]
maintenance/benchmarks/Benchmarker.php
maintenance/benchmarks/bench_delete_truncate.php
maintenance/benchmarks/benchmarkParse.php
maintenance/checkBadRedirects.php
maintenance/checkImages.php
maintenance/checkUsernames.php
maintenance/cleanupAncientTables.php
maintenance/cleanupBlocks.php
maintenance/cleanupImages.php
maintenance/cleanupPreferences.php
maintenance/cleanupRemovedModules.php
maintenance/cleanupSpam.php
maintenance/cleanupTable.inc
maintenance/cleanupTitles.php
maintenance/cleanupWatchlist.php
maintenance/clearInterwikiCache.php
maintenance/commandLine.inc
maintenance/convertExtensionToRegistration.php
maintenance/convertLinks.php
maintenance/convertUserOptions.php
maintenance/deleteArchivedFiles.php
maintenance/deleteBatch.php
maintenance/deleteDefaultMessages.php
maintenance/deleteEqualMessages.php
maintenance/deleteOldRevisions.php
maintenance/deleteOrphanedRevisions.php
maintenance/deleteRevision.php
maintenance/deleteSelfExternals.php
maintenance/doMaintenance.php
maintenance/dumpBackup.php
maintenance/dumpLinks.php
maintenance/dumpTextPass.php
maintenance/dumpUploads.php
maintenance/eraseArchivedFile.php
maintenance/fetchText.php
maintenance/findDeprecated.php
maintenance/fixDefaultJsonContentPages.php
maintenance/fixDoubleRedirects.php
maintenance/fixExtLinksProtocolRelative.php
maintenance/fixTimestamps.php
maintenance/fixUserRegistration.php
maintenance/generateLocalAutoload.php
maintenance/generateSitemap.php
maintenance/getSlaveServer.php
maintenance/getText.php
maintenance/importDump.php
maintenance/importImages.inc
maintenance/importImages.php
maintenance/importSites.php
maintenance/importTextFiles.php [new file with mode: 0644]
maintenance/initEditCount.php
maintenance/initSiteStats.php
maintenance/language/zhtable/simp2trad.manual
maintenance/language/zhtable/toCN.manual
maintenance/language/zhtable/toHK.manual
maintenance/language/zhtable/toTW.manual
maintenance/language/zhtable/toTrad.manual
maintenance/language/zhtable/tradphrases.manual
maintenance/language/zhtable/tradphrases_exclude.manual
maintenance/migrateUserGroup.php
maintenance/moveBatch.php
maintenance/namespaceDupes.php
maintenance/nukeNS.php
maintenance/nukePage.php
maintenance/oracle/alterSharedConstraints.php
maintenance/orphans.php
maintenance/pageExists.php
maintenance/patchSql.php
maintenance/populateCategory.php
maintenance/populateContentModel.php
maintenance/populateFilearchiveSha1.php
maintenance/populateImageSha1.php
maintenance/populateLogUsertext.php
maintenance/populateParentId.php
maintenance/populateRevisionLength.php
maintenance/populateRevisionSha1.php
maintenance/postgres/archives/patch-bot_passwords.sql [new file with mode: 0644]
maintenance/postgres/tables.sql
maintenance/purgeList.php
maintenance/reassignEdits.php
maintenance/rebuildFileCache.php
maintenance/rebuildImages.php
maintenance/rebuildall.php
maintenance/rebuildrecentchanges.php
maintenance/rebuildtextindex.php
maintenance/refreshFileHeaders.php
maintenance/refreshImageMetadata.php
maintenance/refreshLinks.php
maintenance/removeUnusedAccounts.php
maintenance/renameDbPrefix.php
maintenance/resetUserTokens.php
maintenance/rollbackEdits.php
maintenance/runBatchedQuery.php
maintenance/showSiteStats.php
maintenance/sqlite.php
maintenance/storage/blob_tracking.sql
maintenance/storage/compressOld.php
maintenance/storage/dumpRev.php
maintenance/storage/fixBug20757.php
maintenance/storage/orphanStats.php
maintenance/storage/recompressTracked.php
maintenance/storage/storageTypeStats.php
maintenance/storage/testCompression.php
maintenance/tables.sql
maintenance/tidyUpBug37714.php
maintenance/update.php
maintenance/updateArticleCount.php
maintenance/updateCollation.php
maintenance/updateDoubleWidthSearch.php
maintenance/updateRestrictions.php
maintenance/updateSearchIndex.php
maintenance/updateSpecialPages.php
maintenance/waitForSlave.php [deleted file]
maintenance/wrapOldPasswords.php
package.json
phpcs.xml
resources/Resources.php
resources/lib/jquery/jquery.validate.js [deleted file]
resources/lib/oojs-ui/i18n/be-tarask.json
resources/lib/oojs-ui/i18n/ka.json
resources/lib/oojs-ui/i18n/kk-cyrl.json
resources/lib/oojs-ui/i18n/lki.json
resources/lib/oojs-ui/i18n/vep.json [new file with mode: 0644]
resources/lib/oojs-ui/i18n/war.json [new file with mode: 0644]
resources/lib/oojs-ui/oojs-ui-apex-noimages.css
resources/lib/oojs-ui/oojs-ui-apex.js
resources/lib/oojs-ui/oojs-ui-mediawiki-noimages.css
resources/lib/oojs-ui/oojs-ui-mediawiki.js
resources/lib/oojs-ui/oojs-ui.js
resources/lib/oojs-ui/themes/apex/icons-content.json [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/icons-editing-advanced.json
resources/lib/oojs-ui/themes/apex/icons-editing-styling.json
resources/lib/oojs-ui/themes/apex/icons.json
resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/apex/images/icons/language.png [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/language.svg [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.png [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.svg [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.png [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.svg [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.png [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.svg [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.png [deleted file]
resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/icons-content.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-advanced.json
resources/lib/oojs-ui/themes/mediawiki/icons-editing-styling.json
resources/lib/oojs-ui/themes/mediawiki/icons.json
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.svg [new file with mode: 0644]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/language.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.svg [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.png [deleted file]
resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.svg [deleted file]
resources/src/jquery.tipsy/jquery.tipsy.css
resources/src/jquery.tipsy/jquery.tipsy.js
resources/src/jquery/jquery.accessKeyLabel.js
resources/src/jquery/jquery.farbtastic.css
resources/src/mediawiki.action/mediawiki.action.edit.preview.js
resources/src/mediawiki.action/mediawiki.action.view.filepage.css
resources/src/mediawiki.legacy/commonPrint.css
resources/src/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.less/mediawiki.mixins.rotation.less
resources/src/mediawiki.less/mediawiki.ui/variables.less
resources/src/mediawiki.messagePoster/mediawiki.messagePoster.MessagePoster.js
resources/src/mediawiki.messagePoster/mediawiki.messagePoster.factory.js
resources/src/mediawiki.skinning/content.parsoid.less
resources/src/mediawiki.skinning/elements.css
resources/src/mediawiki.special/mediawiki.special.blocklist.css [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.comparepages.styles.less [new file with mode: 0644]
resources/src/mediawiki.special/mediawiki.special.import.js
resources/src/mediawiki.special/mediawiki.special.javaScriptTest.js [deleted file]
resources/src/mediawiki.special/mediawiki.special.preferences.js
resources/src/mediawiki.special/mediawiki.special.preferences.styles.css
resources/src/mediawiki.ui/components/anchors.less
resources/src/mediawiki.ui/components/buttons.less
resources/src/mediawiki.ui/components/checkbox.less
resources/src/mediawiki.ui/components/forms.less
resources/src/mediawiki.ui/components/icons.less
resources/src/mediawiki.ui/components/inputs.less
resources/src/mediawiki.ui/components/radio.less
resources/src/mediawiki.ui/components/text.less
resources/src/mediawiki.widgets.datetime/CalendarWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/CalendarWidget.less [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/DiscordianDateTimeFormatter.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less [new file with mode: 0644]
resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js [new file with mode: 0644]
resources/src/mediawiki.widgets/mw.widgets.CategorySelector.js
resources/src/mediawiki/bookletlayout/option2/ccbysa.svg
resources/src/mediawiki/bookletlayout/option2/noderiv.svg
resources/src/mediawiki/bookletlayout/option2/ownwork.svg
resources/src/mediawiki/bookletlayout/option2/useful.svg
resources/src/mediawiki/bookletlayout/option4/camera.svg
resources/src/mediawiki/bookletlayout/option4/graphics.svg
resources/src/mediawiki/bookletlayout/option4/search-ltr.svg
resources/src/mediawiki/bookletlayout/option4/search-rtl.svg
resources/src/mediawiki/bookletlayout/option4/website-ltr.svg
resources/src/mediawiki/bookletlayout/option4/website-rtl.svg
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.ForeignUpload.js
resources/src/mediawiki/mediawiki.Title.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.js
resources/src/mediawiki/mediawiki.checkboxtoggle.css [new file with mode: 0644]
resources/src/mediawiki/mediawiki.checkboxtoggle.js [new file with mode: 0644]
resources/src/mediawiki/mediawiki.feedback.js
resources/src/mediawiki/mediawiki.hlist.js [deleted file]
resources/src/mediawiki/mediawiki.htmlform.ooui.css
resources/src/mediawiki/mediawiki.inspect.js
resources/src/mediawiki/mediawiki.js
resources/src/mediawiki/mediawiki.template.mustache.js
resources/src/mediawiki/page/ready.js
resources/src/startup.js
tests/TestsAutoLoader.php
tests/parser/parserTest.inc
tests/parser/parserTests.txt
tests/phpunit/MediaWikiTestCase.php
tests/phpunit/data/gitinfo/extension/gitinfo.json [new file with mode: 0644]
tests/phpunit/data/parser/320x240.ogv [new file with mode: 0644]
tests/phpunit/data/templates/conds.mustache [new file with mode: 0644]
tests/phpunit/includes/ExportTest.php [new file with mode: 0644]
tests/phpunit/includes/GitInfoTest.php
tests/phpunit/includes/ImportLinkCacheIntegrationTest.php [deleted file]
tests/phpunit/includes/ImportTest.php [deleted file]
tests/phpunit/includes/PagePropsTest.php [new file with mode: 0644]
tests/phpunit/includes/TestLogger.php [new file with mode: 0644]
tests/phpunit/includes/api/ApiLoginTest.php
tests/phpunit/includes/api/ApiMainTest.php
tests/phpunit/includes/api/ApiResultTest.php
tests/phpunit/includes/api/ApiTestCase.php
tests/phpunit/includes/api/ApiTestCaseUpload.php
tests/phpunit/includes/changes/RCCacheEntryFactoryTest.php
tests/phpunit/includes/context/RequestContextTest.php
tests/phpunit/includes/db/DatabaseMysqlBaseTest.php
tests/phpunit/includes/db/LBFactoryTest.php
tests/phpunit/includes/debug/logger/monolog/KafkaHandlerTest.php
tests/phpunit/includes/exception/HttpErrorTest.php
tests/phpunit/includes/import/ImportLinkCacheIntegrationTest.php [new file with mode: 0644]
tests/phpunit/includes/import/ImportTest.php [new file with mode: 0644]
tests/phpunit/includes/jobqueue/JobQueueMemoryTest.php [new file with mode: 0644]
tests/phpunit/includes/libs/MemoizedCallableTest.php
tests/phpunit/includes/libs/objectcache/BagOStuffTest.php
tests/phpunit/includes/logging/ProtectLogFormatterTest.php
tests/phpunit/includes/media/MediaWikiMediaTestCase.php
tests/phpunit/includes/media/XCFTest.php
tests/phpunit/includes/page/WikiPageTest.php
tests/phpunit/includes/parser/NewParserTest.php
tests/phpunit/includes/parser/PreprocessorTest.php
tests/phpunit/includes/registration/ExtensionProcessorTest.php
tests/phpunit/includes/registration/ExtensionRegistryTest.php
tests/phpunit/includes/session/BotPasswordSessionProviderTest.php [new file with mode: 0644]
tests/phpunit/includes/session/CookieSessionProviderTest.php [new file with mode: 0644]
tests/phpunit/includes/session/ImmutableSessionProviderWithCookieTest.php [new file with mode: 0644]
tests/phpunit/includes/session/PHPSessionHandlerTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionBackendTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionIdTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionInfoTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionManagerTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionProviderTest.php [new file with mode: 0644]
tests/phpunit/includes/session/SessionTest.php [new file with mode: 0644]
tests/phpunit/includes/session/TestBagOStuff.php [new file with mode: 0644]
tests/phpunit/includes/session/TestUtils.php [new file with mode: 0644]
tests/phpunit/includes/session/UserInfoTest.php [new file with mode: 0644]
tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php [new file with mode: 0644]
tests/phpunit/includes/upload/UploadBaseTest.php
tests/phpunit/includes/upload/UploadFromUrlTest.php
tests/phpunit/includes/user/BotPasswordTest.php [new file with mode: 0644]
tests/phpunit/includes/user/UserTest.php
tests/phpunit/includes/utils/BatchRowUpdateTest.php
tests/phpunit/includes/utils/MWCryptHKDFTest.php
tests/phpunit/includes/utils/MWGrantsTest.php [new file with mode: 0644]
tests/phpunit/includes/utils/MWRestrictionsTest.php [new file with mode: 0644]
tests/phpunit/maintenance/MaintenanceTest.php
tests/phpunit/maintenance/backupTextPassTest.php
tests/phpunit/maintenance/backup_LogTest.php
tests/phpunit/maintenance/backup_PageTest.php
tests/phpunit/mocks/session/DummySessionBackend.php [new file with mode: 0644]
tests/phpunit/mocks/session/DummySessionProvider.php [new file with mode: 0644]
tests/phpunit/phpunit.php
tests/phpunit/structure/ApiDocumentationTest.php [new file with mode: 0644]
tests/phpunit/suites/UploadFromUrlTestSuite.php
tests/phpunit/tests/MediaWikiTestCaseTest.php
tests/qunit/QUnitTestResources.php
tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.messagePoster.factory.test.js
tests/qunit/suites/resources/mediawiki/mediawiki.template.mustache.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
tests/qunit/suites/resources/startup.test.js
thumb.php

diff --git a/CREDITS b/CREDITS
index 76d2107..fe7b81e 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -169,6 +169,7 @@ following names for their contribution to the product.
 * Louperivois
 * Lucas Garczewski
 * Luigi Corsaro
+* Luke Faraone
 * Lupo
 * Madman
 * Manuel Menal
index 946b652..354f048 100644 (file)
@@ -56,8 +56,9 @@ module.exports = function ( grunt ) {
                                        included: true,
                                        served: false
                                } ],
+                               logLevel: 'DEBUG',
                                frameworks: [ 'qunit' ],
-                               reporters: [ 'dots' ],
+                               reporters: [ 'progress' ],
                                singleRun: true,
                                autoWatch: false,
                                // Some tests in extensions don't yield for more than the default 10s (T89075)
diff --git a/HISTORY b/HISTORY
index 0410bd5..e57d346 100644 (file)
--- a/HISTORY
+++ b/HISTORY
@@ -1,6 +1,14 @@
 Change notes from older releases. For current info see RELEASE-NOTES-1.27.
 
-== MediaWiki 1.26 ==
+= MediaWiki 1.26 =
+
+== MediaWiki 1.26.2 ==
+
+This is a maintenance release of the MediaWiki 1.26 branch.
+
+=== Changes since 1.26.1 ===
+* (T121892) Fix fatal error on some Special pages, introduced in 1.26.1.
+
 == MediaWiki 1.26.1 ==
 
 This is a maintenance release of the MediaWiki 1.26 branch.
@@ -26,6 +34,8 @@ This is a maintenance release of the MediaWiki 1.26 branch.
 * Fix issue that breaks HHVM Repo Authorative mode.
 * (T120267) Work around APCu memory corruption bug
 
+== MediaWiki 1.26.0 ==
+
 === Configuration changes in 1.26 ===
 * $wgPasswordResetRoutes['email'] = true by default.
 * $wgEnableParserCache was deprecated, set $wgParserCacheType to CACHE_NONE
@@ -115,7 +125,7 @@ This is a maintenance release of the MediaWiki 1.26 branch.
   documentation for mw.Upload.Dialog, mw.Upload.BookletLayout and its
   subclasses for more information.
 
-== extension.json changes in 1.26 ==
+=== extension.json changes in 1.26 ===
 * (T99344) The extension.json schema is now versioned. All extensions
   and skins should set a "manifest_version" property corresponding to
   the schema version they were written for. The only supported version
@@ -268,7 +278,14 @@ changes to languages because of Phabricator reports.
 * $wgDeferredUpdateList was removed.
 * DeferredUpdates::addHTMLCacheUpdate() was removed.
 
-== MediaWiki 1.25 ==
+= MediaWiki 1.25 =
+
+== MediaWiki 1.25.5 ==
+
+This is a maintenance release of the MediaWiki 1.25 branch.
+
+=== Changes since 1.25.4 ===
+* (T121892) Fix fatal error on some Special pages, introduced in 1.25.4.
 
 == MediaWiki 1.25.4 ==
 
@@ -348,6 +365,8 @@ This is a bug fix release of the MediaWiki 1.25 branch.
 === Changes since 1.25 ===
 * (T100351) Fix syntax errors in extension.json of ConfirmEdit extension
 
+== MediaWiki 1.25.0 ==
+
 === Configuration changes in 1.25 ===
 * $wgPageShowWatchingUsers was removed.
 * $wgLocalVirtualHosts has been added to replace $wgConf->localVHosts.
@@ -850,55 +869,20 @@ changes to languages because of Bugzilla reports.
   loadedScripts object, from wikibits.js (deprecated since 1.17) now emit
   warnings through mw.log.warn when accessed.
 
+= MediaWiki 1.24 =
 
-== Compatibility ==
-
-MediaWiki 1.25 requires PHP 5.3.3 or later. There is experimental support for
-HHVM 3.3.0.
-
-MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
-support for them is somewhat less mature. There is experimental support for
-Oracle and Microsoft SQL Server.
+== MediaWiki 1.24.6 ==
 
-The supported versions are:
+This is a maintenance release of the MediaWiki 1.24 branch.
 
-* MySQL 5.0.3 or later
-* PostgreSQL 8.3 or later
-* SQLite 3.3.7 or later
-* Oracle 9.0.1 or later
-* Microsoft SQL Server 2005 (9.00.1399)
-
-== Upgrading ==
-
-1.25 has several database changes since 1.24, and will not work without schema
-updates. Note that due to changes to some very large tables like the revision
-table, the schema update may take quite long (minutes on a medium sized site,
-many hours on a large site).
-
-If upgrading from before 1.11, and you are using a wiki as a commons
-repository, make sure that it is updated as well. Otherwise, errors may arise
-due to database schema changes.
-
-If upgrading from before 1.7, you may want to run refreshLinks.php to ensure
-new database fields are filled with data.
-
-If you are upgrading from MediaWiki 1.4.x or earlier, you should upgrade to
-1.5 first. The upgrade script maintenance/upgrade1_5.php has been removed
-with MediaWiki 1.21.
-
-Don't forget to always back up your database before upgrading!
-
-See the file UPGRADE for more detailed upgrade instructions.
-
-For notes on 1.24.x and older releases, see HISTORY.
-
-== MediaWiki 1.24 ==
+=== Changes since 1.24.5 ===
+* (T121892) Fix fatal error on some Special pages, introduced in 1.24.5.
 
 == MediaWiki 1.24.5 ==
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.24.4 ==
+=== Changes since 1.24.4 ===
 * (T117899) SECURITY: $wgArticlePath can no longer be set to relative paths
   that do not begin with a slash. This enabled trivial XSS attacks.
   Configuration values such as "http://my.wiki.com/wiki/$1" are fine, as are
@@ -920,7 +904,7 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
 
 This is a security and maintenance release of the MediaWiki 1.24 branch.
 
-== Changes since 1.24.3 ==
+=== Changes since 1.24.3 ===
 
 * (T91653) Minimal PSR-3 debug logger to support backports from 1.25+.
 * (T68650) Fix indexing of moved pages with PostgreSQL. Requires running
@@ -935,7 +919,7 @@ This is a security and maintenance release of the MediaWiki 1.24 branch.
 
 This is a security and maintenance release of the MediaWiki 1.24 branch.
 
-== Changes since 1.24.2 ==
+=== Changes since 1.24.2 ===
 
 * (T94116) SECURITY: Compare API watchlist token in constant time
 * (T97391) SECURITY: Escape error message strings in thumb.php
@@ -949,7 +933,7 @@ This is a security and maintenance release of the MediaWiki 1.24 branch.
 
 This is a security and maintenance release of the MediaWiki 1.24 branch.
 
-== Changes since 1.24.1 ==
+=== Changes since 1.24.1 ===
 
 * (T85848, T71210) SECURITY: Don't parse XMP blocks that contain XML entities,
   to prevent various DoS attacks.
@@ -973,7 +957,7 @@ This is a security and maintenance release of the MediaWiki 1.24 branch.
 
 This is a security and maintenance release of the MediaWiki 1.24 branch.
 
-== Changes since 1.24.0 ==
+=== Changes since 1.24.0 ===
 
 * (bug T76686) [SECURITY] thumb.php outputs wikitext message as raw HTML, which
   could lead to xss. Permission to edit MediaWiki namespace is required to
@@ -986,6 +970,8 @@ This is a security and maintenance release of the MediaWiki 1.24 branch.
 * (bug T76168) OutputPage: Add accessors for some protected properties.
 * (bug T74834) Make 1.24 branch directly installable under PostgreSQL.
 
+== MediaWiki 1.24.0 ==
+
 === Configuration changes in 1.24 ===
 * MediaWiki will no longer run if register_globals is enabled. It has been
   deprecated for 5 years now, and was removed in PHP 5.4. For more information
@@ -1678,14 +1664,20 @@ of files that are no longer available follows.
 * skins/common/images/icons/fileicon.png
 * skins/common/images/ksh/button_S_italic.png
 
+= MediaWiki 1.23 =
+
+== MediaWiki 1.23.13 ==
 
-== MediaWiki 1.23 ==
+This is a maintenance release of the MediaWiki 1.23 branch.
+
+=== Changes since 1.23.12 ===
+* (T121892) Fix fatal errors on some Special pages, introduced in 1.23.12.
 
 == MediaWiki 1.23.12 ==
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.23.11 ==
+=== Changes since 1.23.11 ===
 * (T117899) SECURITY: $wgArticlePath can no longer be set to relative paths
   that do not begin with a slash. This enabled trivial XSS attacks.
   Configuration values such as "http://my.wiki.com/wiki/$1" are fine, as are
@@ -1706,7 +1698,7 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.23.10 ==
+=== Changes since 1.23.10 ===
 
 * (T91850) SECURITY: Add throttle check in ApiUpload and SpecialUpload
 * (T91203, T91205) SECURITY: API: Improve validation in chunked uploading
@@ -1716,7 +1708,7 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.23.9 ==
+=== Changes since 1.23.9 ===
 
 * (T94116) SECURITY: Compare API watchlist token in constant time
 * (T97391) SECURITY: Escape error message strings in thumb.php
@@ -1731,7 +1723,7 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.23.8 ==
+=== Changes since 1.23.8 ===
 
 * (T85848, T71210) SECURITY: Don't parse XMP blocks that contain XML entities,
   to prevent various DoS attacks.
@@ -1744,14 +1736,14 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
   prevent XSS and protect viewer's privacy.
 * (bug T68650) Fix indexing of moved pages with PostgreSQL. Requires running
   update.php to fix.
-* (bug T70087) Fix Special:ActiveUsers page for installations using 
+* (bug T70087) Fix Special:ActiveUsers page for installations using
   PostgreSQL.
 
 == MediaWiki 1.23.8 ==
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.23.7 ==
+=== Changes since 1.23.7 ===
 
 * (bug T76686) [SECURITY] thumb.php outputs wikitext message as raw HTML, which
   could lead to xss. Permission to edit MediaWiki namespace is required to
@@ -1765,7 +1757,7 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
 
 This is a security and maintenance release of the MediaWiki 1.23 branch.
 
-== Changes since 1.23.6 ==
+=== Changes since 1.23.6 ===
 
 * (bugs 66776, 71478) SECURITY:  User PleaseStand reported a way to inject code
   into API clients that used format=php to process pages that underwent flash
@@ -1869,6 +1861,7 @@ This is a security and maintenance release of the MediaWiki 1.23 branch.
   like only extracting the tail of the file partially or not at all.
 * (bug 66182) Removed -x flag on some php files.
 
+== MediaWiki 1.23.0 ==
 
 === Configuration changes in 1.23 ===
 * (bug 13250) Restored method for clearing a watchlist in web UI
@@ -2337,7 +2330,7 @@ changes to languages because of Bugzilla reports.
 ==== Removed globals ====
 * $wgBetterDirectionality (deprecated in 1.18)
 
-== MediaWiki 1.22 ==
+= MediaWiki 1.22 =
 
 == MediaWiki 1.22.15 ==
 
@@ -2493,6 +2486,8 @@ This is a security and maintenance release of the MediaWiki 1.22 branch.
 * (bug 47055) Changed FOR UPDATE handling in Postgresql
 * (bug 57026) Avoid extra parsing in prepareContentForEdit()
 
+== MediaWiki 1.22.0 ==
+
 === Configuration changes in 1.22 ===
 * $wgRedirectScript was removed. It was unused.
 * Removed $wgLocalMessageCacheSerialized, it is now always true.
@@ -2902,7 +2897,7 @@ This is a security and maintenance release of the MediaWiki 1.22 branch.
   file repositories, and related ForeignAPIRepo methods getInfo and getApiUrl.
 * The new query module list=allfileusages to enumerate file usages was added.
 
-=== Languages updated in 1.22===
+=== Languages updated in 1.22 ===
 
 MediaWiki supports over 350 languages. Many localisations are updated
 regularly. Below only new and removed languages are listed, as well as
@@ -3020,7 +3015,7 @@ changes to languages because of Bugzilla reports.
 * mediawiki.util: mw.util.wikiGetlink has been renamed to getUrl. (The old name
   still works, but is deprecated.)
 
-== MediaWiki 1.21 ==
+= MediaWiki 1.21 =
 
 == MediaWiki 1.21.11 ==
 This is a security and maintenance release of the MediaWiki 1.21 branch.
@@ -3108,6 +3103,8 @@ This is a maintenance release of the MediaWiki 1.21 branch.
 * A problem with the Oracle SQL table creation was fixed.
 * (PdfHandler extension) Fix warning if pdfinfo fails but pdftext succeeds.
 
+== MediaWiki 1.21.0 ==
+
 === Configuration changes in 1.21 ===
 * (bug 29374) $wgVectorUseSimpleSearch is now enabled by default.
 * Deprecated $wgAllowRealName is removed. Use $wgHiddenPrefs[] = 'realname'
@@ -3436,7 +3433,7 @@ changes to languages because of Bugzilla reports.
 * BREAKING CHANGE: (bug 38244) Removed the mediawiki.api.titleblacklist module
   and moved it to the TitleBlacklist extension.
 
-== MediaWiki 1.20 ==
+= MediaWiki 1.20 =
 
 == MediaWiki 1.20.8 ==
 This is a security release of the MediaWiki 1.20 branch.
@@ -3489,7 +3486,7 @@ This is a security release of the MediaWiki 1.20 branch.
 == MediaWiki 1.20.3 ==
 This is a security and maintenance release of the MediaWiki 1.20 branch.
 
-== MediaWiki 1.20.2 ==
+=== Changes since MediaWiki 1.20.2 ===
 * New preference type - 'api'. Preferences of this type are not shown on Special:Preferences, but are still available via the action=options API. (Unbreaks MLEB.)
 * (bug 44010) Context is passed to UserGetLanguageObject.
 * The recursion guard on RequestContext::getLanguage() was weakened.
@@ -3503,14 +3500,14 @@ This is a security and maintenance release of the MediaWiki 1.20 branch.
 == MediaWiki 1.20.2 ==
 This is a maintenance release of the MediaWiki 1.20 branch
 
-== MediaWiki 1.20.1 ==
+=== Changes since MediaWiki 1.20.1 ===
 * (bug 42638) Fix API action=options&reset=1 & unit tests.
 * (bug 42370) Fixed backport of 60cc060 to use mDoneWrites — caused * (bug 42592) User rights, preferences and other things are not saving in 1.20.1.
 
 == MediaWiki 1.20.1 ==
 This is a security release of the MediaWiki 1.20 branch
 
-Changes since 1.20
+=== Changes since 1.20.0 ===
 * (bug 42202) Validate options to prevent html injection
 * (bug 40995) Prevent session fixation in Special:UserLogin (CVE-2012-5391)
 * (bug 41400) Prevent linker regex from exceeding PCRE backtrack limit
@@ -3518,9 +3515,7 @@ Changes since 1.20
 * (bug 40632) Remove CleanupPresentationalAttributes feature
 * [Database] Fixed case where trx idle callbacks might be lost.
 
-
-
-== MediaWiki 1.20 ==
+== MediaWiki 1.20.0 ==
 
 === PHP 5.3 now required ===
 Since 1.20, the lowest supported version of PHP is now 5.3.2. Please
@@ -3887,7 +3882,7 @@ changes to languages because of Bugzilla reports.
 == MediaWiki 1.19.21 ==
 This is a maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.20===
+=== Changes since 1.19.20 ===
 * (bug 67440) Allow classes to be registered properly from installer.
 * (bug 47281) Fixed a dumpBackup.php error with --uploads --include-filesoptions: Unable to find the wrapper "mwstore". * System administrators are encouraged to upgrade to this release or 1.22+ and produce a full data dump. https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Backing_up_a_wiki
 * (bug 63049) Removed anonymous functions from ApiFormatBase, added in1.19.13 as part of the fix for bug 61362, for PHP 5.2 compatibility.
@@ -3895,73 +3890,73 @@ This is a maintenance release of the MediaWiki 1.19 branch.
 == MediaWiki 1.19.20 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.19===
+=== Changes since 1.19.19 ===
 * (bug 70672) SECURITY: OutputPage: Remove separation of css and js module allowance.
 
 == MediaWiki 1.19.19 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.18===
+=== Changes since 1.19.18 ===
 * (bug 69008) SECURITY: Enhance CSS filtering in SVG files. Filter <style> elements; normalize style elements and attributes before filtering; add checks for attributes that contain css; add unit tests for html5sec and reported bugs.
 
 == MediaWiki 1.19.18 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.17===
+=== Changes since 1.19.17 ===
 * (bug 68187) SECURITY: Prepend jsonp callback with comment.
 * (bug 65778) SECURITY: Copy prevent-clickjacking between OutputPage and ParserOutput.
 
 == MediaWiki 1.19.17 ==
 This is a security and maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.16===
+=== Changes since 1.19.16 ===
 * (bug 65839) SECURITY: Prevent external resources in SVG files.
 * (bug 66428) MimeMagic: Don't seek before BOF. This has weird side effects like only extracting the tail of the file partially or not at all.
 
 == MediaWiki 1.19.16 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.15===
+=== Changes since 1.19.15 ===
 * (bug 65501) SECURITY: Don't parse usernames as wikitext on Special:PasswordReset.
 
 == MediaWiki 1.19.15 ==
 This is a security and maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.14===
+=== Changes since 1.19.14 ===
 Fixed resetting passwords.
 * (bug 58640) Fixed a compatibility issue with PCRE 8.34 that caused pages to appear blank or with missing text.
 
 == MediaWiki 1.19.14 ==
 This is a security and maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.13===
+=== Changes since 1.19.13 ===
 * (bug 62497) SECURITY: Add CSRF token on Special:ChangePassword.
 * (bug 62467) Set a title for the context during import on the cli.
 
 == MediaWiki 1.19.13 ==
 This is a security and maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.12===
+=== Changes since 1.19.12 ===
 * (bug 61362) SECURITY: API: Don't find links in the middle of api.php links.
 * Use the correct branch of the extensions' git repositories.
 
 == MediaWiki 1.19.12 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.11===
+=== Changes since 1.19.11 ===
 * (bug 60771) SECURITY: Disallow uploading SVG files using non-whitelisted namespaces. Also disallow iframe elements. * User will get an error including the namespace name if they use a non- whitelisted namespace.
 * (bug 61346) SECURITY: Make token comparison use constant time. It seems like our token comparison would be vulnerable to timing attacks. This will take constant time.
 
 == MediaWiki 1.19.11 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.10===
+=== Changes since 1.19.10 ===
 * (bug 60339) SECURITY: Sanitize shell arguments to DjVu files, and other media formats
 
 == MediaWiki 1.19.10 ==
 This is a security release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.9===
+=== Changes since 1.19.9 ===
 * (bug 57550) SECURITY: Disallow stylesheets in SVG Uploads
 * (bug 58088) SECURITY: Don't normalize U+FF3C to \ in CSS Checks
 * (bug 58472) SECURITY: Disallow -o-link in styles
@@ -3971,7 +3966,7 @@ This is a security release of the MediaWiki 1.19 branch.
 == MediaWiki 1.19.9 ==
 This is a security and maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.8===
+=== Changes since 1.19.8 ===
 * (bug 53032) SECURITY: Don't cache when a call could autocreate
 * (bug 55332) SECURITY: Improve css javascript detection
 * (bug 49717) Fix behaviour $wgVerifyMimeType = false; in Upload
@@ -3982,7 +3977,7 @@ This is a security and maintenance release of the MediaWiki 1.19 branch.
 
 This is a security and maintenance release of the MediaWiki 1.19 branch.
 
-=== Changes since 1.19.7===
+=== Changes since 1.19.7 ===
 * SECURITY: Sanitize ResourceLoader exception messages
 * SECURITY: Token-getting functions will fail when using jsonp callbacks.
 * SECURITY: Fix extension detection with 2 .'s
@@ -3995,7 +3990,7 @@ This is a security and maintenance release of the MediaWiki 1.19 branch.
 
 This is a security release of the MediaWiki 1.19 branch
 
-=== Changes since 1.19.6===
+=== Changes since 1.19.6 ===
 * (bug 48306) SECURITY: Run file validation checks on chunked uploads, and chunks of upload, during the upload process.
 
 == MediaWiki 1.19.6 ==
@@ -4003,7 +3998,7 @@ This is a security release of the MediaWiki 1.19 branch
 
 This is a security and maintenance release of the MediaWiki 1.19 branch
 
-=== Changes since 1.19.5===
+=== Changes since 1.19.5 ===
 * (bug 47304) SECURITY: Check SVG xml encoding against whitelist
 * (bug 46590) Added AbortChangePassword hook to allow extensions to abort password changes from Special:ChangePassword
 * Localisation updates from http://translatewiki.net.
@@ -4016,7 +4011,7 @@ This is a security and maintenance release of the MediaWiki 1.19 branch
 
 This is a security and maintenance release of the MediaWiki 1.19 branch
 
-=== Changes since 1.19.4===
+=== Changes since 1.19.4 ===
 * (bug 47251) SECURITY: Disable external entities in Import
 * (bug 46859) SECURITY: Disable external entities in XMLReader
 * (bug 46084) SECURITY: Sanitize $limitReport before outputting
@@ -4028,7 +4023,7 @@ This is a security and maintenance release of the MediaWiki 1.19 branch
 
 This is a security release of the MediaWiki 1.19 branch
 
-=== Changes since 1.19.3===
+=== Changes since 1.19.3 ===
 * New preference type - 'api'. Preferences of this type are not shown on Special:Preferences, but are still available via the action=options API.
 * (bug 44010) Context is passed to UserGetLanguageObject.
 * The recursion guard on RequestContext::getLanguage() was weakened.
@@ -4040,7 +4035,7 @@ This is a security release of the MediaWiki 1.19 branch
 
 This is a security release of the MediaWiki 1.19 branch
 
-=== Changes since 1.19.2===
+=== Changes since 1.19.2 ===
 * (bug 40995) Prevent session fixation in Special:UserLogin (CVE-2012-5391)
 * (bug 41400) Prevent linker regex from exceeding PCRE backtrack limit
 * Increase permitted runtime for testParserTest (only used for continuous integration).
diff --git a/README b/README
index 29577bc..6352e62 100644 (file)
--- a/README
+++ b/README
@@ -1,10 +1,10 @@
 == MediaWiki ==
 
 MediaWiki is a free and open-source wiki software package written in PHP. It
-serves as the platform for Wikipedia and the other projects of the Wikimedia
-Foundation, which deliver content in over 280 languages to more than half a
-billion people each month. MediaWiki's reliability and robust feature set have
-earned it a large and vibrant community of third-party users and developers.
+serves as the platform for Wikipedia and the other Wikimedia projects, used
+by hundreds of millions of people each month. MediaWiki is localised in over
+350 languages and its reliability and robust feature set have earned it a large
+and vibrant community of third-party users and developers.
 
 MediaWiki is:
 
index f674d59..def80ea 100644 (file)
@@ -53,14 +53,39 @@ production.
 * (T48998) $wgArticlePath must now be either a full url, or start with a "/".
 * $wgRateLimitLog was removed; use $wgDebugLogGroups['ratelimit'] instead.
 * Deprecated API formats dbg, txt, and yaml have been removed.
-* CLDRPluralRule* classes have been replaced with wikimedia/cldr-plural-rule-parser.
+* CLDRPluralRule* classes have been replaced with
+  wikimedia/cldr-plural-rule-parser.
 * Removed $wgProfilePerHost, $wgUDPProfilerHost, $wgUDPProfilerPort,
-  $wgUDPProfilerFormatString, $wgStatsMethod, $wgAggregateStatsID, $wgStatsFormatString,
-  and $wgProfileCallTree (deprecated since 1.20).
+  $wgUDPProfilerFormatString, $wgStatsMethod, $wgAggregateStatsID,
+  $wgStatsFormatString, and $wgProfileCallTree (deprecated since 1.20).
 * For proper operation of LocalIdLookup with shared user tables, ensure that
   $wgSharedDB and $wgSharedTables are properly set even on the "central" wiki
   that all others are sharing from and that $wgLocalDatabases is set to the
   full list of sharing wikis on all those wikis.
+* Massive overhaul to session handling:
+** $wgSessionsInObjectCache is no longer supported and must be true, due to
+   MediaWiki\Session\SessionManager. $wgSessionHandler is similarly no longer
+   used.
+** ObjectCacheSessionHandler is removed, replaced with
+   MediaWiki\Session\PhpSessionHandler.
+** PHP session handling in general ($_SESSION, session_id(), and so on) is
+   deprecated. Use MediaWiki\Session\SessionManager instead. A new config
+   variable, $wgPHPSessionHandling, is available to cause use of $_SESSION to
+   issue a deprecation warning or to cause most PHP session handling to throw
+   exceptions.
+** Deprecated UserSetCookies hook. Session-handling extensions should generally
+   be creating a custom subclass of CookieSessionProvider. Other extensions
+   messing with cookies can no longer count on user data being saved in cookies
+   versus other methods.
+** Deprecated UserLoadFromSession hook, extensions should create a
+   MediaWiki\Session\SessionProvider.
+** The User cannot be loaded from session until after Setup.php completes.
+   Attempts to do so will be ignored and the User will remain unloaded.
+* MediaWiki will now auto-create users as necessary, removing the need for
+  extensions to do so. An 'autocreateaccount' right is added to allow
+  auto-creation when 'createaccount' is not granted to all users.
+* Deprecated AuthPluginAutoCreate hook in favor of LocalUserCreated.
+* Most cookie-handling methods in User are deprecated.
 
 === New features in 1.27 ===
 * $wgDataCenterId and $wgDataCenterRoles where added, which will serve as
@@ -96,11 +121,32 @@ production.
   authentication extensions.
 * $wgMaxUserDBWriteDuration added to limit huge user-generated transactions.
   Regular web request transactions that takes longer than this are aborted.
-* Added a new hook, 'TitleMoveCompleting', which runs before a page move is committed.
+* Added a new hook, 'TitleMoveCompleting', which runs before a page move is
+  committed.
 * $wgCdnReboundPurgeDelay was added to provide secondary delayed purges of URLs
   from CDN to mitigate DB replication lag and WAN cache purge lag.
+* (T49162) Installer will default to setting CACHE_ACCEL as the main cache type
+  if it is available.
+* It is now possible to patrol file uploads (both for new files and new versions
+  of existing files). Special:NewFiles has gained an option to filter by patrol
+  status. This functionality can be disabled using $wgUseFilePatrol.
+* MediaWiki\Session infrastructure allows for easier use of session mechanisms
+  other than the usual cookies.
+** SessionMetadata and SessionCheckInfo hooks allow for setting and checking
+   custom session metadata.
+* Added MWGrants and associated configuration settings $wgGrantPermissions and
+  $wgGrantPermissionGroups to hold configuration for authentication features
+  such as OAuth that want to allow restricting the user rights a user may make
+  use of.
+** If you're already using the OAuth extension, these new variables are
+   identical to (and will replace) $wgMWOAuthGrantPermissions and
+   $wgMWOAuthGrantPermissionGroups.
+* Added MWRestrictions as a class to check restrictions on a WebRequest, e.g.
+  to assert that the request comes from a particular IP range.
+* Added bot passwords, a rights-restricted login mechanism for API-using bots.
 
 === External library changes in 1.27 ===
+
 ==== Upgraded external libraries ====
 * Updated oojs/oojs-ui from v0.12.12 to v0.13.3.
 * Updated composer/semver from v1.0.0 to v1.2.0.
@@ -111,6 +157,7 @@ production.
 * Added wikimedia/cldr-plural-rule-parser v1.0.0.
 * Added wikimedia/relpath v1.0.3.
 * Added wikimedia/running-stat v1.1.0.
+* Added wikimedia/php-session-serializer v1.0.3.
 
 ==== Removed and replaced external libraries ====
 
@@ -133,6 +180,9 @@ production.
 * The following response properties from action=login are deprecated, and may
   be removed in the future: lgtoken, cookieprefix, sessionid. Clients should
   handle cookies to properly manage session state.
+* action=login transparently allows login using bot passwords. Clients should
+  merely need to change the username and password used after setting up a bot
+  password.
 
 === Action API internal changes in 1.27 ===
 * ApiQueryORM removed.
@@ -144,6 +194,8 @@ production.
   ApiQueryBase::keyPartToTitle() all removed (deprecated since 1.24).
 * ApiQueryBase::checkRowCount() was removed (deprecated since 1.24).
 * ApiQueryBase::getDirectionDescription() was removed (deprecated since 1.25).
+* ApiQuery::getModules() was removed (deprecated since 1.21).
+* ApiMain::getModules() was removed (deprecated since 1.21).
 
 === Languages updated in 1.27 ===
 
@@ -159,14 +211,15 @@ changes to languages because of Phabricator reports.
   ignore the 2nd and 3rd arguments (formerly $id and $commit).
 * Removed "loaderScripts" option from ResourceLoaderFileModule class.
 * Removed ORM-like wrapper added in 1.20.
-* LinkCache::getGoodLinks and LinkCache::getBadLinks were removed (deprecated in 1.26).
+* LinkCache::getGoodLinks and LinkCache::getBadLinks were removed
+  (deprecated in 1.26).
 * WikiPage::doQuickEdit() was removed (deprecated since 1.21).
 * Removed SiteObject and SiteArray classes (deprecated in 1.21).
 * MessageBlobStore::getInstance() was removed (deprecated since 1.25).
 * (T84937) Free external links ("autolinked" urls) will now be terminated
   by &nbsp; and HTML entity encodings of &nbsp, <, and >.
-* (T36948) The default file revert message's timestamp is now in $wgLocaltimezone,
-  instead of UTC.
+* (T36948) The default file revert message's timestamp is now in
+  $wgLocaltimezone, instead of UTC.
 * The default name of the 'suppress' group page has been changed from
   'Project:Oversight' to 'Project:Suppress'.
 * DatabaseBase::resultObject() is now protected (use outside Database classes
@@ -175,7 +228,8 @@ changes to languages because of Phabricator reports.
   ResourceLoaderContext instance is deprecated.
 * ResourceLoader::getLessCompiler() now takes an optional parameter of
   additional LESS variables to set for the compiler.
-* wfBaseConvert() marked as deprecated, use Wikimedia\base_convert() directly instead.
+* wfBaseConvert() marked as deprecated, use Wikimedia\base_convert() directly
+  instead.
 * Obsolete maintenance scripts clearCacheStats.php and showCacheStats.php
   were removed. The underlying data is sent to StatsD (see $wgStatsdServer).
 * Removed msg_resource_links database table and associated code.
@@ -190,11 +244,54 @@ changes to languages because of Phabricator reports.
 * OutputPage::loginToUse() was removed (deprecated since 1.19).
 * Article::loadContent() was removed (deprecated since 1.19).
 * User::editToken() was removed (deprecated since 1.19).
+* Removed --force-normal option of dumpBackup.php, as it no longer served
+  any useful purpose since 1.22.
+* The functions processOption() and processArgs() on the BackupDumper and
+  TextPassDumper classes have been removed.
+* The maintenance/backupTextPass.inc file was deleted. You should include
+  maintenance/dumpTextPass.php instead.
+* WikiPage::getUsedTemplates() was removed (deprecated since 1.19).
+* wfEmptyMsg() was removed (deprecated since 1.18).
+* OutputPage::permissionRequired() was removed (deprecated since 1.18).
+* OutputPage::blockedPage() was removed (deprecated since 1.18).
+* User::getSkin() was removed (deprecated since 1.18).
+* OutputPage::includeJQuery() was removed (deprecated since 1.17).
+* WikiPage::updateRestrictions() was removed (deprecated since 1.19).
+* WikiPage::testPreSaveTransform() was removed (deprecated since 1.19).
+* LogPage::logName() was removed (deprecated since 1.19).
+* LogPage::logHeader() was removed (deprecated since 1.19).
+* wfCheckLimits() was removed (deprecated since 1.24).
+* Linker::makeKnownLinkObj() was removed (deprecated since 1.16).
+* Linker::makeLinkObj() was removed (deprecated since 1.16).
+* wfMsgForContentNoTrans() was removed (deprecated since 1.18).
+* ChangesList::usePatrol was removed (deprecated since 1.22).
+* wfMsgNoTrans() was removed (deprecated since 1.18).
+* Linker::makeImageLink2 was removed (deprecated since 1.20).
+* Title::userIsWatching() was removed (deprecated since 1.20).
+* Removed WaitForSlave maintenance script; use SELECT MASTER_POS_WAIT()
+  database function directly instead.
+* wfMsg() was removed (deprecated since 1.18).
+* wfMsgForContent() was removed (deprecated since 1.18).
+* wfMsgReal() was removed (deprecated since 1.18).
+* wfMsgGetKey() was removed (deprecated since 1.18).
+* wfMsgHtml() was removed (deprecated since 1.18).
+* wfMsgWikiHtml() was removed (deprecated since 1.18).
+* wfMsgExt() was removed (deprecated since 1.18).
+* Language::armourMath() was removed (deprecated since 1.22).
+* LanguageConverter::armourMath() was removed (deprecated since 1.22).
+* FakeConverter::armourMath() was removed (deprecated since 1.22).
+* The unused jquery.validate ResourceLoader module was removed.
+* FileRepo::getRootUrl() was removed (deprecated since 1.20).
+* User::generateToken() was removed (deprecated since 1.20).
+* WikiPage::getRawText() was removed (deprecated since 1.21).
+* ParserOutput::hasCustomDataUpdates() was removed (deprecated since 1.25).
+* ParserOutput::addSecondaryDataUpdate() was removed (deprecated since 1.25).
+* ParserOutput::getSecondaryDataUpdates() was removed (deprecated since 1.25).
 
 == Compatibility ==
 
 MediaWiki 1.27 requires PHP 5.3.3 or later. There is experimental support for
-HHVM 3.3.0.
+HHVM 3.6.5 or later.
 
 MySQL is the recommended DBMS. PostgreSQL or SQLite can also be used, but
 support for them is somewhat less mature. There is experimental support for
index 8c5ec81..24a4d72 100644 (file)
@@ -72,6 +72,7 @@ $wgAutoloadLocalClasses = array(
        'ApiQueryAllMessages' => __DIR__ . '/includes/api/ApiQueryAllMessages.php',
        'ApiQueryAllPages' => __DIR__ . '/includes/api/ApiQueryAllPages.php',
        'ApiQueryAllRevisions' => __DIR__ . '/includes/api/ApiQueryAllRevisions.php',
+       'ApiQueryMyStashedFiles' => __DIR__ . '/includes/api/ApiQueryMyStashedFiles.php',
        'ApiQueryAllUsers' => __DIR__ . '/includes/api/ApiQueryAllUsers.php',
        'ApiQueryBacklinks' => __DIR__ . '/includes/api/ApiQueryBacklinks.php',
        'ApiQueryBacklinksprop' => __DIR__ . '/includes/api/ApiQueryBacklinksprop.php',
@@ -140,6 +141,7 @@ $wgAutoloadLocalClasses = array(
        'Article' => __DIR__ . '/includes/page/Article.php',
        'AssembleUploadChunksJob' => __DIR__ . '/includes/jobqueue/jobs/AssembleUploadChunksJob.php',
        'AtomFeed' => __DIR__ . '/includes/Feed.php',
+       'AtomicSectionUpdate' => __DIR__ . '/includes/deferred/AtomicSectionUpdate.php',
        'AttachLatest' => __DIR__ . '/maintenance/attachLatest.php',
        'AuthPlugin' => __DIR__ . '/includes/AuthPlugin.php',
        'AuthPluginUser' => __DIR__ . '/includes/AuthPlugin.php',
@@ -179,6 +181,7 @@ $wgAutoloadLocalClasses = array(
        'BlockListPager' => __DIR__ . '/includes/specials/SpecialBlockList.php',
        'BlockLogFormatter' => __DIR__ . '/includes/logging/BlockLogFormatter.php',
        'BmpHandler' => __DIR__ . '/includes/media/BMP.php',
+       'BotPassword' => __DIR__ . '/includes/user/BotPassword.php',
        'BrokenRedirectsPage' => __DIR__ . '/includes/specials/SpecialBrokenRedirects.php',
        'BufferingStatsdDataFactory' => __DIR__ . '/includes/libs/BufferingStatsdDataFactory.php',
        'CLIParser' => __DIR__ . '/maintenance/parse.php',
@@ -289,6 +292,7 @@ $wgAutoloadLocalClasses = array(
        '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',
        'DBSiteStore' => __DIR__ . '/includes/site/DBSiteStore.php',
        'DBTransactionError' => __DIR__ . '/includes/db/DatabaseError.php',
        'DBUnexpectedError' => __DIR__ . '/includes/db/DatabaseError.php',
@@ -331,7 +335,7 @@ $wgAutoloadLocalClasses = array(
        'DeprecatedGlobal' => __DIR__ . '/includes/DeprecatedGlobal.php',
        'DeprecatedInterfaceFinder' => __DIR__ . '/maintenance/findDeprecated.php',
        'DerivativeContext' => __DIR__ . '/includes/context/DerivativeContext.php',
-       'DerivativeRequest' => __DIR__ . '/includes/WebRequest.php',
+       'DerivativeRequest' => __DIR__ . '/includes/DerivativeRequest.php',
        'DerivativeResourceLoaderContext' => __DIR__ . '/includes/resourceloader/DerivativeResourceLoaderContext.php',
        'DescribeFileOp' => __DIR__ . '/includes/filebackend/FileOp.php',
        'Diff' => __DIR__ . '/includes/diff/DairikiDiff.php',
@@ -352,21 +356,22 @@ $wgAutoloadLocalClasses = array(
        'DoubleReplacer' => __DIR__ . '/includes/libs/replacers/DoubleReplacer.php',
        'DummyLinker' => __DIR__ . '/includes/Linker.php',
        'DummyTermColorer' => __DIR__ . '/maintenance/term/MWTerm.php',
-       'Dump7ZipOutput' => __DIR__ . '/includes/Export.php',
-       'DumpBZip2Output' => __DIR__ . '/includes/Export.php',
-       'DumpDBZip2Output' => __DIR__ . '/maintenance/backup.inc',
-       'DumpFileOutput' => __DIR__ . '/includes/Export.php',
-       'DumpFilter' => __DIR__ . '/includes/Export.php',
-       'DumpGZipOutput' => __DIR__ . '/includes/Export.php',
+       'Dump7ZipOutput' => __DIR__ . '/includes/export/Dump7ZipOutput.php',
+       'DumpBZip2Output' => __DIR__ . '/includes/export/DumpBZip2Output.php',
+       'DumpBackup' => __DIR__ . '/maintenance/dumpBackup.php',
+       'DumpDBZip2Output' => __DIR__ . '/includes/export/DumpDBZip2Output.php',
+       'DumpFileOutput' => __DIR__ . '/includes/export/DumpFileOutput.php',
+       'DumpFilter' => __DIR__ . '/includes/export/DumpFilter.php',
+       'DumpGZipOutput' => __DIR__ . '/includes/export/DumpGZipOutput.php',
        'DumpIterator' => __DIR__ . '/maintenance/dumpIterator.php',
-       'DumpLatestFilter' => __DIR__ . '/includes/Export.php',
+       'DumpLatestFilter' => __DIR__ . '/includes/export/DumpLatestFilter.php',
        'DumpLinks' => __DIR__ . '/maintenance/dumpLinks.php',
        'DumpMessages' => __DIR__ . '/maintenance/language/dumpMessages.php',
-       'DumpMultiWriter' => __DIR__ . '/includes/Export.php',
-       'DumpNamespaceFilter' => __DIR__ . '/includes/Export.php',
-       'DumpNotalkFilter' => __DIR__ . '/includes/Export.php',
-       'DumpOutput' => __DIR__ . '/includes/Export.php',
-       'DumpPipeOutput' => __DIR__ . '/includes/Export.php',
+       'DumpMultiWriter' => __DIR__ . '/includes/export/DumpMultiWriter.php',
+       'DumpNamespaceFilter' => __DIR__ . '/includes/export/DumpNamespaceFilter.php',
+       'DumpNotalkFilter' => __DIR__ . '/includes/export/DumpNotalkFilter.php',
+       'DumpOutput' => __DIR__ . '/includes/export/DumpOutput.php',
+       'DumpPipeOutput' => __DIR__ . '/includes/export/DumpPipeOutput.php',
        'DumpRenderer' => __DIR__ . '/maintenance/renderDump.php',
        'DumpRev' => __DIR__ . '/maintenance/storage/dumpRev.php',
        'DuplicateJob' => __DIR__ . '/includes/jobqueue/jobs/DuplicateJob.php',
@@ -376,7 +381,7 @@ $wgAutoloadLocalClasses = array(
        'EditWatchlistCheckboxSeriesField' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php',
        'EditWatchlistNormalHTMLForm' => __DIR__ . '/includes/specials/SpecialEditWatchlist.php',
        'EmailConfirmation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php',
-       'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialConfirmemail.php',
+       'EmailInvalidation' => __DIR__ . '/includes/specials/SpecialEmailInvalidate.php',
        'EmailNotification' => __DIR__ . '/includes/mail/EmailNotification.php',
        'EmaillingJob' => __DIR__ . '/includes/jobqueue/jobs/EmaillingJob.php',
        'EmptyBagOStuff' => __DIR__ . '/includes/libs/objectcache/EmptyBagOStuff.php',
@@ -416,7 +421,7 @@ $wgAutoloadLocalClasses = array(
        'FakeResultWrapper' => __DIR__ . '/includes/db/DatabaseUtility.php',
        'Fallback' => __DIR__ . '/includes/Fallback.php',
        'FatalError' => __DIR__ . '/includes/exception/FatalError.php',
-       'FauxRequest' => __DIR__ . '/includes/WebRequest.php',
+       'FauxRequest' => __DIR__ . '/includes/FauxRequest.php',
        'FauxResponse' => __DIR__ . '/includes/WebResponse.php',
        'FeedItem' => __DIR__ . '/includes/Feed.php',
        'FeedUtils' => __DIR__ . '/includes/FeedUtils.php',
@@ -571,9 +576,10 @@ $wgAutoloadLocalClasses = array(
        'ImportReporter' => __DIR__ . '/includes/specials/SpecialImport.php',
        'ImportSiteScripts' => __DIR__ . '/maintenance/importSiteScripts.php',
        'ImportSites' => __DIR__ . '/maintenance/importSites.php',
-       'ImportSource' => __DIR__ . '/includes/Import.php',
-       'ImportStreamSource' => __DIR__ . '/includes/Import.php',
-       'ImportStringSource' => __DIR__ . '/includes/Import.php',
+       'ImportSource' => __DIR__ . '/includes/import/ImportSource.php',
+       'ImportStreamSource' => __DIR__ . '/includes/import/ImportStreamSource.php',
+       'ImportStringSource' => __DIR__ . '/includes/import/ImportStringSource.php',
+       'ImportTextFiles' => __DIR__ . '/maintenance/importTextFiles.php',
        'ImportTitleFactory' => __DIR__ . '/includes/title/ImportTitleFactory.php',
        'IncludableSpecialPage' => __DIR__ . '/includes/specialpage/IncludableSpecialPage.php',
        'IndexPager' => __DIR__ . '/includes/pager/IndexPager.php',
@@ -608,6 +614,7 @@ $wgAutoloadLocalClasses = array(
        'JobQueueError' => __DIR__ . '/includes/jobqueue/JobQueue.php',
        'JobQueueFederated' => __DIR__ . '/includes/jobqueue/JobQueueFederated.php',
        'JobQueueGroup' => __DIR__ . '/includes/jobqueue/JobQueueGroup.php',
+       'JobQueueMemory' => __DIR__ . '/includes/jobqueue/JobQueueMemory.php',
        'JobQueueRedis' => __DIR__ . '/includes/jobqueue/JobQueueRedis.php',
        'JobRunner' => __DIR__ . '/includes/jobqueue/JobRunner.php',
        'JobSpecification' => __DIR__ . '/includes/jobqueue/JobSpecification.php',
@@ -728,11 +735,13 @@ $wgAutoloadLocalClasses = array(
        'MWDocGen' => __DIR__ . '/maintenance/mwdocgen.php',
        'MWException' => __DIR__ . '/includes/exception/MWException.php',
        'MWExceptionHandler' => __DIR__ . '/includes/exception/MWExceptionHandler.php',
+       'MWGrants' => __DIR__ . '/includes/utils/MWGrants.php',
        'MWHttpRequest' => __DIR__ . '/includes/HttpFunctions.php',
        'MWMemcached' => __DIR__ . '/includes/compat/MemcachedClientCompat.php',
        'MWMessagePack' => __DIR__ . '/includes/libs/MWMessagePack.php',
        'MWNamespace' => __DIR__ . '/includes/MWNamespace.php',
        'MWOldPassword' => __DIR__ . '/includes/password/MWOldPassword.php',
+       'MWRestrictions' => __DIR__ . '/includes/utils/MWRestrictions.php',
        'MWSaltedPassword' => __DIR__ . '/includes/password/MWSaltedPassword.php',
        'MWTidy' => __DIR__ . '/includes/parser/MWTidy.php',
        'MWTimestamp' => __DIR__ . '/includes/MWTimestamp.php',
@@ -776,6 +785,20 @@ $wgAutoloadLocalClasses = array(
        'MediaWiki\\Logger\\Monolog\\WikiProcessor' => __DIR__ . '/includes/debug/logger/monolog/WikiProcessor.php',
        'MediaWiki\\Logger\\NullSpi' => __DIR__ . '/includes/debug/logger/NullSpi.php',
        'MediaWiki\\Logger\\Spi' => __DIR__ . '/includes/debug/logger/Spi.php',
+       'MediaWiki\\Session\\BotPasswordSessionProvider' => __DIR__ . '/includes/session/BotPasswordSessionProvider.php',
+       'MediaWiki\\Session\\CookieSessionProvider' => __DIR__ . '/includes/session/CookieSessionProvider.php',
+       'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' => __DIR__ . '/includes/session/ImmutableSessionProviderWithCookie.php',
+       'MediaWiki\\Session\\PHPSessionHandler' => __DIR__ . '/includes/session/PHPSessionHandler.php',
+       'MediaWiki\\Session\\Session' => __DIR__ . '/includes/session/Session.php',
+       'MediaWiki\\Session\\SessionBackend' => __DIR__ . '/includes/session/SessionBackend.php',
+       'MediaWiki\\Session\\SessionId' => __DIR__ . '/includes/session/SessionId.php',
+       'MediaWiki\\Session\\SessionInfo' => __DIR__ . '/includes/session/SessionInfo.php',
+       'MediaWiki\\Session\\SessionManager' => __DIR__ . '/includes/session/SessionManager.php',
+       'MediaWiki\\Session\\SessionManagerInterface' => __DIR__ . '/includes/session/SessionManagerInterface.php',
+       'MediaWiki\\Session\\SessionProvider' => __DIR__ . '/includes/session/SessionProvider.php',
+       'MediaWiki\\Session\\SessionProviderInterface' => __DIR__ . '/includes/session/SessionProviderInterface.php',
+       'MediaWiki\\Session\\UserInfo' => __DIR__ . '/includes/session/UserInfo.php',
+       'MediaWiki\\Site\\MediaWikiPageNameNormalizer' => __DIR__ . '/includes/site/MediaWikiPageNameNormalizer.php',
        'MediaWiki\\Tidy\\Html5Depurate' => __DIR__ . '/includes/tidy/Html5Depurate.php',
        'MediaWiki\\Tidy\\RaggettBase' => __DIR__ . '/includes/tidy/RaggettBase.php',
        'MediaWiki\\Tidy\\RaggettExternal' => __DIR__ . '/includes/tidy/RaggettExternal.php',
@@ -859,7 +882,6 @@ $wgAutoloadLocalClasses = array(
        'ORAField' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ORAResult' => __DIR__ . '/includes/db/DatabaseOracle.php',
        'ObjectCache' => __DIR__ . '/includes/objectcache/ObjectCache.php',
-       'ObjectCacheSessionHandler' => __DIR__ . '/includes/objectcache/ObjectCacheSessionHandler.php',
        'ObjectFactory' => __DIR__ . '/includes/libs/ObjectFactory.php',
        'ObjectFileCache' => __DIR__ . '/includes/cache/ObjectFileCache.php',
        'OldChangesList' => __DIR__ . '/includes/changes/OldChangesList.php',
@@ -902,6 +924,7 @@ $wgAutoloadLocalClasses = array(
        'PageExists' => __DIR__ . '/maintenance/pageExists.php',
        'PageLangLogFormatter' => __DIR__ . '/includes/logging/PageLangLogFormatter.php',
        'PageLinkRenderer' => __DIR__ . '/includes/title/PageLinkRenderer.php',
+       'PageProps' => __DIR__ . '/includes/PageProps.php',
        'PageQueryPage' => __DIR__ . '/includes/specialpage/PageQueryPage.php',
        'Pager' => __DIR__ . '/includes/pager/Pager.php',
        'ParameterizedPassword' => __DIR__ . '/includes/password/ParameterizedPassword.php',
@@ -1100,7 +1123,7 @@ $wgAutoloadLocalClasses = array(
        'SearchHighlighter' => __DIR__ . '/includes/search/SearchHighlighter.php',
        'SearchMssql' => __DIR__ . '/includes/search/SearchMssql.php',
        'SearchMySQL' => __DIR__ . '/includes/search/SearchMySQL.php',
-       'SearchNearMatchResultSet' => __DIR__ . '/includes/search/SearchResultSet.php',
+       'SearchNearMatchResultSet' => __DIR__ . '/includes/search/SearchNearMatchResultSet.php',
        'SearchOracle' => __DIR__ . '/includes/search/SearchOracle.php',
        'SearchPostgres' => __DIR__ . '/includes/search/SearchPostgres.php',
        'SearchResult' => __DIR__ . '/includes/search/SearchResult.php',
@@ -1143,6 +1166,7 @@ $wgAutoloadLocalClasses = array(
        'SpecialBlock' => __DIR__ . '/includes/specials/SpecialBlock.php',
        'SpecialBlockList' => __DIR__ . '/includes/specials/SpecialBlockList.php',
        'SpecialBookSources' => __DIR__ . '/includes/specials/SpecialBooksources.php',
+       'SpecialBotPasswords' => __DIR__ . '/includes/specials/SpecialBotPasswords.php',
        'SpecialCachedPage' => __DIR__ . '/includes/specials/SpecialCachedPage.php',
        'SpecialCategories' => __DIR__ . '/includes/specials/SpecialCategories.php',
        'SpecialChangeContentModel' => __DIR__ . '/includes/specials/SpecialChangeContentModel.php',
@@ -1163,6 +1187,7 @@ $wgAutoloadLocalClasses = array(
        'SpecialListAdmins' => __DIR__ . '/includes/specials/SpecialListusers.php',
        'SpecialListBots' => __DIR__ . '/includes/specials/SpecialListusers.php',
        'SpecialListFiles' => __DIR__ . '/includes/specials/SpecialListfiles.php',
+       'SpecialListGrants' => __DIR__ . '/includes/specials/SpecialListgrants.php',
        'SpecialListGroupRights' => __DIR__ . '/includes/specials/SpecialListgrouprights.php',
        'SpecialListUsers' => __DIR__ . '/includes/specials/SpecialListusers.php',
        'SpecialLockdb' => __DIR__ . '/includes/specials/SpecialLockdb.php',
@@ -1188,6 +1213,7 @@ $wgAutoloadLocalClasses = array(
        'SpecialProtectedtitles' => __DIR__ . '/includes/specials/SpecialProtectedtitles.php',
        'SpecialRandomInCategory' => __DIR__ . '/includes/specials/SpecialRandomInCategory.php',
        'SpecialRandomredirect' => __DIR__ . '/includes/specials/SpecialRandomredirect.php',
+       'SpecialRandomrootpage' => __DIR__ . '/includes/specials/SpecialRandomrootpage.php',
        'SpecialRecentChanges' => __DIR__ . '/includes/specials/SpecialRecentchanges.php',
        'SpecialRecentChangesLinked' => __DIR__ . '/includes/specials/SpecialRecentchangeslinked.php',
        'SpecialRedirect' => __DIR__ . '/includes/specials/SpecialRedirect.php',
@@ -1212,7 +1238,7 @@ $wgAutoloadLocalClasses = array(
        'SpecialWhatLinksHere' => __DIR__ . '/includes/specials/SpecialWhatlinkshere.php',
        'SqlBagOStuff' => __DIR__ . '/includes/objectcache/SqlBagOStuff.php',
        'SqlDataUpdate' => __DIR__ . '/includes/deferred/SqlDataUpdate.php',
-       'SqlSearchResultSet' => __DIR__ . '/includes/search/SearchResultSet.php',
+       'SqlSearchResultSet' => __DIR__ . '/includes/search/SqlSearchResultSet.php',
        'Sqlite' => __DIR__ . '/maintenance/sqlite.inc',
        'SqliteInstaller' => __DIR__ . '/includes/installer/SqliteInstaller.php',
        'SqliteMaintenance' => __DIR__ . '/maintenance/sqlite.php',
@@ -1252,7 +1278,7 @@ $wgAutoloadLocalClasses = array(
        'TestFileOpPerformance' => __DIR__ . '/maintenance/fileOpPerfTest.php',
        'TextContent' => __DIR__ . '/includes/content/TextContent.php',
        'TextContentHandler' => __DIR__ . '/includes/content/TextContentHandler.php',
-       'TextPassDumper' => __DIR__ . '/maintenance/backupTextPass.inc',
+       'TextPassDumper' => __DIR__ . '/maintenance/dumpTextPass.php',
        'TextStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'TgConverter' => __DIR__ . '/languages/classes/LanguageTg.php',
        'ThrottledError' => __DIR__ . '/includes/exception/ThrottledError.php',
@@ -1314,7 +1340,7 @@ $wgAutoloadLocalClasses = array(
        'UploadFromUrl' => __DIR__ . '/includes/upload/UploadFromUrl.php',
        'UploadFromUrlJob' => __DIR__ . '/includes/jobqueue/jobs/UploadFromUrlJob.php',
        'UploadLogFormatter' => __DIR__ . '/includes/logging/UploadLogFormatter.php',
-       'UploadSourceAdapter' => __DIR__ . '/includes/Import.php',
+       'UploadSourceAdapter' => __DIR__ . '/includes/import/UploadSourceAdapter.php',
        'UploadSourceField' => __DIR__ . '/includes/specials/SpecialUpload.php',
        'UploadStash' => __DIR__ . '/includes/upload/UploadStash.php',
        'UploadStashBadPathException' => __DIR__ . '/includes/upload/UploadStash.php',
@@ -1336,6 +1362,7 @@ $wgAutoloadLocalClasses = array(
        'UserCache' => __DIR__ . '/includes/cache/UserCache.php',
        'UserDupes' => __DIR__ . '/maintenance/userDupes.inc',
        'UserMailer' => __DIR__ . '/includes/mail/UserMailer.php',
+       'UserNamePrefixSearch' => __DIR__ . '/includes/user/UserNamePrefixSearch.php',
        'UserNotLoggedIn' => __DIR__ . '/includes/exception/UserNotLoggedIn.php',
        'UserOptions' => __DIR__ . '/maintenance/userOptions.inc',
        'UserPasswordPolicy' => __DIR__ . '/includes/password/UserPasswordPolicy.php',
@@ -1352,7 +1379,6 @@ $wgAutoloadLocalClasses = array(
        'VirtualRESTService' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTService.php',
        'VirtualRESTServiceClient' => __DIR__ . '/includes/libs/virtualrest/VirtualRESTServiceClient.php',
        'WANObjectCache' => __DIR__ . '/includes/libs/objectcache/WANObjectCache.php',
-       'WaitForSlave' => __DIR__ . '/maintenance/waitForSlave.php',
        'WantedCategoriesPage' => __DIR__ . '/includes/specials/SpecialWantedcategories.php',
        'WantedFilesPage' => __DIR__ . '/includes/specials/SpecialWantedfiles.php',
        'WantedPagesPage' => __DIR__ . '/includes/specials/SpecialWantedpages.php',
@@ -1362,37 +1388,37 @@ $wgAutoloadLocalClasses = array(
        'WatchedItem' => __DIR__ . '/includes/WatchedItem.php',
        'WatchlistCleanup' => __DIR__ . '/maintenance/cleanupWatchlist.php',
        'WebInstaller' => __DIR__ . '/includes/installer/WebInstaller.php',
-       'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerCopying' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerDBConnect' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerDBSettings' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerDocument' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerExistingWiki' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerInstall' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerLanguage' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerName' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerOptions' => __DIR__ . '/includes/installer/WebInstallerPage.php',
+       'WebInstallerComplete' => __DIR__ . '/includes/installer/WebInstallerComplete.php',
+       'WebInstallerCopying' => __DIR__ . '/includes/installer/WebInstallerCopying.php',
+       'WebInstallerDBConnect' => __DIR__ . '/includes/installer/WebInstallerDBConnect.php',
+       'WebInstallerDBSettings' => __DIR__ . '/includes/installer/WebInstallerDBSettings.php',
+       'WebInstallerDocument' => __DIR__ . '/includes/installer/WebInstallerDocument.php',
+       'WebInstallerExistingWiki' => __DIR__ . '/includes/installer/WebInstallerExistingWiki.php',
+       'WebInstallerInstall' => __DIR__ . '/includes/installer/WebInstallerInstall.php',
+       'WebInstallerLanguage' => __DIR__ . '/includes/installer/WebInstallerLanguage.php',
+       'WebInstallerName' => __DIR__ . '/includes/installer/WebInstallerName.php',
+       'WebInstallerOptions' => __DIR__ . '/includes/installer/WebInstallerOptions.php',
        'WebInstallerOutput' => __DIR__ . '/includes/installer/WebInstallerOutput.php',
        'WebInstallerPage' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerReadme' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerReleaseNotes' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerRestart' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerPage.php',
-       'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerPage.php',
+       'WebInstallerReadme' => __DIR__ . '/includes/installer/WebInstallerReadme.php',
+       'WebInstallerReleaseNotes' => __DIR__ . '/includes/installer/WebInstallerReleaseNotes.php',
+       'WebInstallerRestart' => __DIR__ . '/includes/installer/WebInstallerRestart.php',
+       'WebInstallerUpgrade' => __DIR__ . '/includes/installer/WebInstallerUpgrade.php',
+       'WebInstallerUpgradeDoc' => __DIR__ . '/includes/installer/WebInstallerUpgradeDoc.php',
+       'WebInstallerWelcome' => __DIR__ . '/includes/installer/WebInstallerWelcome.php',
        'WebPHandler' => __DIR__ . '/includes/media/WebP.php',
        'WebRequest' => __DIR__ . '/includes/WebRequest.php',
        'WebRequestUpload' => __DIR__ . '/includes/WebRequestUpload.php',
        'WebResponse' => __DIR__ . '/includes/WebResponse.php',
        'WikiCategoryPage' => __DIR__ . '/includes/page/WikiCategoryPage.php',
        'WikiDiff3' => __DIR__ . '/includes/diff/WikiDiff3.php',
-       'WikiExporter' => __DIR__ . '/includes/Export.php',
+       'WikiExporter' => __DIR__ . '/includes/export/WikiExporter.php',
        'WikiFilePage' => __DIR__ . '/includes/page/WikiFilePage.php',
-       'WikiImporter' => __DIR__ . '/includes/Import.php',
+       'WikiImporter' => __DIR__ . '/includes/import/WikiImporter.php',
        'WikiMap' => __DIR__ . '/includes/WikiMap.php',
        'WikiPage' => __DIR__ . '/includes/page/WikiPage.php',
        'WikiReference' => __DIR__ . '/includes/WikiMap.php',
-       'WikiRevision' => __DIR__ . '/includes/Import.php',
+       'WikiRevision' => __DIR__ . '/includes/import/WikiRevision.php',
        'WikiStatsOutput' => __DIR__ . '/maintenance/language/StatOutputs.php',
        'WikitextContent' => __DIR__ . '/includes/content/WikitextContent.php',
        'WikitextContentHandler' => __DIR__ . '/includes/content/WikitextContentHandler.php',
@@ -1408,7 +1434,7 @@ $wgAutoloadLocalClasses = array(
        'XMPValidate' => __DIR__ . '/includes/media/XMPValidate.php',
        'Xhprof' => __DIR__ . '/includes/libs/Xhprof.php',
        'Xml' => __DIR__ . '/includes/Xml.php',
-       'XmlDumpWriter' => __DIR__ . '/includes/Export.php',
+       'XmlDumpWriter' => __DIR__ . '/includes/export/XmlDumpWriter.php',
        'XmlJsCode' => __DIR__ . '/includes/Xml.php',
        'XmlSelect' => __DIR__ . '/includes/XmlSelect.php',
        'XmlTypeCheck' => __DIR__ . '/includes/libs/XmlTypeCheck.php',
index 9775823..94de20e 100644 (file)
@@ -21,7 +21,7 @@
                "ext-iconv": "*",
                "liuggio/statsd-php-client": "1.0.18",
                "mediawiki/at-ease": "1.1.0",
-               "oojs/oojs-ui": "0.14.1",
+               "oojs/oojs-ui": "0.15.0",
                "oyejorge/less.php": "1.7.0.9",
                "php": ">=5.3.3",
                "psr/log": "1.0.0",
                "wikimedia/cldr-plural-rule-parser": "1.0.0",
                "wikimedia/composer-merge-plugin": "1.3.0",
                "wikimedia/ip-set": "1.0.1",
+               "wikimedia/php-session-serializer": "1.0.3",
                "wikimedia/relpath": "1.0.3",
                "wikimedia/running-stat": "1.1.0",
                "wikimedia/utfnormal": "1.0.3",
                "wikimedia/wrappedstring": "2.0.0",
-               "zordius/lightncandy": "0.21"
+               "zordius/lightncandy": "0.23"
        },
        "require-dev": {
-               "jakub-onderka/php-parallel-lint": "0.9",
+               "jakub-onderka/php-parallel-lint": "0.9.2",
                "justinrainbow/json-schema": "~1.3",
-               "mediawiki/mediawiki-codesniffer": "0.4.0",
+               "mediawiki/mediawiki-codesniffer": "0.5.1",
                "monolog/monolog": "~1.17.2",
                "nikic/php-parser": "1.4.1",
-               "nmred/kafka-php": "0.1.4",
+               "nmred/kafka-php": "0.1.5",
                "phpunit/phpunit": "3.7.37",
                "wikimedia/avro": "1.7.7"
        },
diff --git a/composer.local.json-sample b/composer.local.json-sample
new file mode 100644 (file)
index 0000000..1315afc
--- /dev/null
@@ -0,0 +1,9 @@
+{
+       "extra": {
+               "merge-plugin": {
+                       "include": [
+                               "extensions/example/composer.json"
+                       ]
+               }
+       }
+}
\ No newline at end of file
index b635467..4218e8a 100644 (file)
                        "type": "object",
                        "description": "Registry of factory functions to create Config objects"
                },
+               "CentralIdLookupProviders": {
+                       "type": "object",
+                       "description": "Central ID lookup providers"
+               },
                "namespaces": {
                        "type": "array",
                        "description": "Method to add extra namespaces",
                "ValidSkinNames": {
                        "type": "object"
                },
+               "FeedClasses": {
+                       "type": "object",
+                       "description": "Available feeds objects"
+               },
                "SkinOOUIThemes": {
                        "type": "object"
                },
                "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 c928aae..2b5e1e0 100644 (file)
@@ -308,6 +308,11 @@ $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.
+&$feedLinks: Array of created feed links
+
 'AfterFinalPageOutput': Nearly at the end of OutputPage::output() but
 before OutputPage::sendCacheControl() and final ob_end_flush() which
 will send the buffered output to the client. This allows for last-minute
@@ -736,8 +741,9 @@ viewing.
 redirect was followed.
 &$article: target article (object)
 
-'AuthPluginAutoCreate': Called when creating a local account for an user logged
-in from an external authentication method.
+'AuthPluginAutoCreate': DEPRECATED! Use the 'LocalUserCreated' hook instead.
+Called when creating a local account for an user logged in from an external
+authentication method.
 $user: User object created locally
 
 'AuthPluginSetup': Update or replace authentication plugin object ($wgAuth).
@@ -1640,6 +1646,11 @@ Return false to stop further processing of the tag
 $reader: XMLReader object
 $revisionInfo: Array of information
 
+'ImportLogInterwikiLink': Hook to change the interwiki link used in log entries
+and edit summaries for transwiki imports.
+&$fullInterwikiPrefix: Interwiki prefix, may contain colons.
+&$pageTitle: String that contains page title.
+
 'ImportSources': Called when reading from the $wgImportSources configuration
 variable. Can be used to lazy-load the import sources list.
 &$importSources: The value of $wgImportSources. Modify as necessary. See the
@@ -2569,8 +2580,29 @@ $targetUser: the user whom to send watchlist email notification
 $title: the page title
 $enotif: EmailNotification object
 
+'SessionCheckInfo': Validate a MediaWiki\Session\SessionInfo as it's being
+loaded from storage. Return false to prevent it from being used.
+&$reason: String rejection reason to be logged
+$info: MediaWiki\Session\SessionInfo being validated
+$request: WebRequest being loaded from
+$metadata: Array|false Metadata array for the MediaWiki\Session\Session
+$data: Array|false Data array for the MediaWiki\Session\Session
+
+'SessionMetadata': Add metadata to a session being saved.
+$backend: MediaWiki\Session\SessionBackend being saved.
+&$metadata: Array Metadata to be stored. Add new keys here.
+$requests: Array of WebRequests potentially being saved to. Generally 0-1 real
+  request and 0+ FauxRequests.
+
 'SetupAfterCache': Called in Setup.php, after cache objects are set
 
+'ShortPagesQuery': Allow extensions to modify the query used by
+Special:ShortPages.
+&$tables: tables to join in the query
+&$conds: conditions for the query
+&$joinConds: join conditions for the query
+&$options: options for the query
+
 'ShowMissingArticle': Called when generating the output for a non-existent page.
 $article: The article object corresponding to the page
 
@@ -3280,8 +3312,9 @@ $name: user name
 $user: user object
 &$s: database query object
 
-'UserLoadFromSession': Called to authenticate users on external/environmental
-means; occurs before session is loaded.
+'UserLoadFromSession': DEPRECATED! Create a MediaWiki\Session\SessionProvider instead.
+Called to authenticate users on external/environmental means; occurs before
+session is loaded.
 $user: user object being loaded
 &$result: set this to a boolean value to abort the normal authentication
   process
@@ -3372,9 +3405,13 @@ $user: User object
 'UserSaveSettings': Called when saving user settings.
 $user: User object
 
-'UserSetCookies': Called when setting user cookies.
+'UserSetCookies': DEPRECATED! If you're trying to replace core session cookie
+handling, you want to create a subclass of MediaWiki\Session\CookieSessionProvider
+instead. Otherwise, you can no longer count on user data being saved to cookies
+versus some other mechanism.
+Called when setting user cookies.
 $user: User object
-&$session: session array, will be added to $_SESSION
+&$session: session array, will be added to the session
 &$cookies: cookies array mapping cookie name to its value
 
 'UserSetEmail': Called when changing user email address.
index 96892d7..c2ea582 100644 (file)
@@ -135,6 +135,9 @@ class AjaxDispatcher {
                                                $result = new AjaxResponse( $result );
                                        }
 
+                                       // Make sure DB commit succeeds before sending a response
+                                       wfGetLBFactory()->commitMasterChanges( __METHOD__ );
+
                                        $result->sendHeaders();
                                        $result->printText();
 
index db989a4..6c2782c 100644 (file)
@@ -223,12 +223,12 @@ class AjaxResponse {
                $fname = 'AjaxResponse::checkLastModified';
 
                if ( !$timestamp || $timestamp == '19700101000000' ) {
-                       wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP\n", 'log' );
+                       wfDebug( "$fname: CACHE DISABLED, NO TIMESTAMP", 'private' );
                        return false;
                }
 
                if ( !$wgCachePages ) {
-                       wfDebug( "$fname: CACHE DISABLED\n", 'log' );
+                       wfDebug( "$fname: CACHE DISABLED", 'private' );
                        return false;
                }
 
@@ -242,8 +242,8 @@ class AjaxResponse {
                        $modsince = preg_replace( '/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"] );
                        $modsinceTime = strtotime( $modsince );
                        $ismodsince = wfTimestamp( TS_MW, $modsinceTime ? $modsinceTime : 1 );
-                       wfDebug( "$fname: -- client send If-Modified-Since: " . $modsince . "\n", 'log' );
-                       wfDebug( "$fname: --  we might send Last-Modified : $lastmod\n", 'log' );
+                       wfDebug( "$fname: -- client send If-Modified-Since: $modsince", 'private' );
+                       wfDebug( "$fname: --  we might send Last-Modified : $lastmod", 'private' );
 
                        if ( ( $ismodsince >= $timestamp )
                                && $wgUser->validateCache( $ismodsince ) &&
@@ -255,16 +255,16 @@ class AjaxResponse {
                                $this->mLastModified = $lastmod;
 
                                wfDebug( "$fname: CACHED client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
-                                       "page: $timestamp ; site $wgCacheEpoch\n", 'log' );
+                                       "page: $timestamp ; site $wgCacheEpoch", 'private' );
 
                                return true;
                        } else {
                                wfDebug( "$fname: READY  client: $ismodsince ; user: {$wgUser->getTouched()} ; " .
-                                       "page: $timestamp ; site $wgCacheEpoch\n", 'log' );
+                                       "page: $timestamp ; site $wgCacheEpoch", 'private' );
                                $this->mLastModified = $lastmod;
                        }
                } else {
-                       wfDebug( "$fname: client did not send If-Modified-Since header\n", 'log' );
+                       wfDebug( "$fname: client did not send If-Modified-Since header", 'private' );
                        $this->mLastModified = $lastmod;
                }
                return false;
@@ -284,12 +284,12 @@ class AjaxResponse {
                if ( $mcvalue ) {
                        # Check to see if the value has been invalidated
                        if ( $touched <= $mcvalue['timestamp'] ) {
-                               wfDebug( "Got $mckey from cache\n" );
+                               wfDebug( "Got $mckey from cache" );
                                $this->mText = $mcvalue['value'];
 
                                return true;
                        } else {
-                               wfDebug( "$mckey has expired\n" );
+                               wfDebug( "$mckey has expired" );
                        }
                }
 
index 4729f50..c017c6c 100644 (file)
@@ -296,7 +296,7 @@ class Block {
                        $block = self::newFromRow( $row );
 
                        # Don't use expired blocks
-                       if ( $block->deleteIfExpired() ) {
+                       if ( $block->isExpired() ) {
                                continue;
                        }
 
@@ -1021,12 +1021,17 @@ class Block {
                        return;
                }
 
-               $method = __METHOD__;
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->onTransactionIdle( function () use ( $dbw, $method ) {
-                       $dbw->delete( 'ipblocks',
-                               array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ), $method );
-               } );
+               DeferredUpdates::addUpdate( new AtomicSectionUpdate(
+                       wfGetDB( DB_MASTER ),
+                       __METHOD__,
+                       function ( IDatabase $dbw, $fname ) {
+                               $dbw->delete(
+                                       'ipblocks',
+                                       array( 'ipb_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
+                                       $fname
+                               );
+                       }
+               ) );
        }
 
        /**
@@ -1140,7 +1145,7 @@ class Block {
                $blocks = array();
                foreach ( $rows as $row ) {
                        $block = self::newFromRow( $row );
-                       if ( !$block->deleteIfExpired() ) {
+                       if ( !$block->isExpired() ) {
                                $blocks[] = $block;
                        }
                }
index e2c31a6..2ceeed2 100644 (file)
@@ -713,7 +713,7 @@ class CategoryViewer extends ContextSource {
                        // quick due to the small number of entries.
                        $totalcnt = $rescnt;
                        $category = $this->cat;
-                       wfGetDB( DB_MASTER )->onTransactionIdle( function () use ( $category ) {
+                       DeferredUpdates::addCallableUpdate( function () use ( $category ) {
                                $category->refreshCounts();
                        } );
                } else {
index 54216fd..9e945ea 100644 (file)
@@ -1003,7 +1003,6 @@ $wgJpegTran = '/usr/bin/jpegtran';
  */
 $wgExiv2Command = '/usr/bin/exiv2';
 
-
 /**
  * Path to exiftool binary. Used for lossless ICC profile swapping.
  *
@@ -1470,7 +1469,6 @@ $wgDjvuOutputExtension = 'jpg';
  * @{
  */
 
-
 /**
  * Site admin email address.
  *
@@ -2183,7 +2181,7 @@ $wgMessageCacheType = CACHE_ANYTHING;
 $wgParserCacheType = CACHE_ANYTHING;
 
 /**
- * The cache type for storing session data. Used if $wgSessionsInObjectCache is true.
+ * The cache type for storing session data.
  *
  * For available types see $wgMainCacheType.
  */
@@ -2318,30 +2316,29 @@ $wgParserCacheExpireTime = 86400;
  *
  * @deprecated since 1.20; Use $wgSessionsInObjectCache
  */
-$wgSessionsInMemcached = false;
+$wgSessionsInMemcached = true;
 
 /**
- * Store sessions in an object cache, configured by $wgSessionCacheType. This
- * can be useful to improve performance, or to avoid the locking behavior of
- * PHP's default session handler, which tends to prevent multiple requests for
- * the same user from acting concurrently.
+ * @deprecated since 1.27, session data is always stored in object cache.
  */
-$wgSessionsInObjectCache = false;
+$wgSessionsInObjectCache = true;
 
 /**
- * The expiry time to use for session storage when $wgSessionsInObjectCache is
- * enabled, in seconds.
+ * The expiry time to use for session storage, in seconds.
  */
 $wgObjectCacheSessionExpiry = 3600;
 
 /**
- * This is used for setting php's session.save_handler. In practice, you will
- * almost never need to change this ever. Other options might be 'user' or
- * 'session_mysql.' Setting to null skips setting this entirely (which might be
- * useful if you're doing cross-application sessions, see bug 11381)
+ * @deprecated since 1.27, MediaWiki\\Session\\SessionManager doesn't use PHP session storage.
  */
 $wgSessionHandler = null;
 
+/**
+ * Whether to use PHP session handling ($_SESSION and session_*() functions)
+ * @var string 'enable', 'warn', or 'disable'
+ */
+$wgPHPSessionHandling = 'enable';
+
 /**
  * If enabled, will send MemCached debugging information to $wgDebugLogFile
  */
@@ -2609,7 +2606,14 @@ $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.
+ * 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.
+ *
+ * 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,
+ * it will populate the response from the stale cdn1 value. When cdn1 gets the purge, cdn2
+ * will still be stale. If the rebound purge delay is safely higher than the time to relay
+ * a purge to all nodes, then the rebound puge will clear cdn2 after cdn1 was cleared.
  *
  * @since 1.27
  */
@@ -3884,10 +3888,13 @@ $wgInterwikiExpiry = 10800;
  */
 
 /**
- *$wgInterwikiCache specifies path to constant database file.
+ * Interwiki cache, either as an associative array or a path to a constant
+ * database (.cdb) file.
+ *
+ * This data structure database is generated by the `dumpInterwiki` maintenance
+ * script (which lives in the WikimediaMaintenance repository) and has key
+ * formats such as the following:
  *
- * This cdb database is generated by dumpInterwiki from maintenance and has
- * such key formats:
  *  - dbname:key - a simple key (e.g. enwiki:meta)
  *  - _sitename:key - site-scope key (e.g. wiktionary:meta)
  *  - __global:key - global-scope key (e.g. __global:meta)
@@ -3895,6 +3902,8 @@ $wgInterwikiExpiry = 10800;
  *
  * Sites mapping just specifies site name, other keys provide "local url"
  * data layout.
+ *
+ * @var bool|array|string
  */
 $wgInterwikiCache = false;
 
@@ -4422,7 +4431,6 @@ $wgPasswordPolicy = array(
        ),
 );
 
-
 /**
  * For compatibility with old installations set to false
  * @deprecated since 1.24 will be removed in future
@@ -4651,6 +4659,30 @@ $wgUserrightsInterwikiDelimiter = '@';
  */
 $wgSecureLogin = false;
 
+/**
+ * MediaWiki\Session\SessionProvider configuration.
+ *
+ * Value is an array of ObjectFactory specifications for the SessionProviders
+ * to be used. Keys in the array are ignored. Order is not significant.
+ *
+ * @since 1.27
+ */
+$wgSessionProviders = array(
+       'MediaWiki\\Session\\CookieSessionProvider' => array(
+               'class' => 'MediaWiki\\Session\\CookieSessionProvider',
+               'args' => array( array(
+                       'priority' => 30,
+                       'callUserSetCookiesHook' => true,
+               ) ),
+       ),
+       'MediaWiki\\Session\\BotPasswordSessionProvider' => array(
+               'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider',
+               'args' => array( array(
+                       'priority' => 40,
+               ) ),
+       ),
+);
+
 /** @} */ # end user accounts }
 
 /************************************************************************//**
@@ -4862,7 +4894,6 @@ $wgGroupPermissions['sysop']['patrol'] = true;
 $wgGroupPermissions['sysop']['autopatrol'] = true;
 $wgGroupPermissions['sysop']['protect'] = true;
 $wgGroupPermissions['sysop']['editprotected'] = true;
-$wgGroupPermissions['sysop']['proxyunbannable'] = true;
 $wgGroupPermissions['sysop']['rollback'] = true;
 $wgGroupPermissions['sysop']['upload'] = true;
 $wgGroupPermissions['sysop']['reupload'] = true;
@@ -5353,6 +5384,164 @@ $wgQueryPageDefaultLimit = 50;
  */
 $wgPasswordAttemptThrottle = array( 'count' => 5, 'seconds' => 300 );
 
+/**
+ * @var Array Map of (grant => right => boolean)
+ * Users authorize consumers (like Apps) to act on their behalf but only with
+ * a subset of the user's normal account rights (signed off on by the user).
+ * The possible rights to grant to a consumer are bundled into groups called
+ * "grants". Each grant defines some rights it lets consumers inherit from the
+ * account they may act on behalf of. Note that a user granting a right does
+ * nothing if that user does not actually have that right to begin with.
+ * @since 1.27
+ */
+$wgGrantPermissions = array();
+
+// @TODO: clean up grants
+// @TODO: auto-include read/editsemiprotected rights?
+
+$wgGrantPermissions['basic']['autoconfirmed'] = true;
+$wgGrantPermissions['basic']['autopatrol'] = true;
+$wgGrantPermissions['basic']['autoreview'] = true;
+$wgGrantPermissions['basic']['editsemiprotected'] = true;
+$wgGrantPermissions['basic']['ipblock-exempt'] = true;
+$wgGrantPermissions['basic']['nominornewtalk'] = true;
+$wgGrantPermissions['basic']['patrolmarks'] = true;
+$wgGrantPermissions['basic']['purge'] = true;
+$wgGrantPermissions['basic']['read'] = true;
+$wgGrantPermissions['basic']['skipcaptcha'] = true;
+$wgGrantPermissions['basic']['torunblocked'] = true;
+$wgGrantPermissions['basic']['writeapi'] = true;
+
+$wgGrantPermissions['highvolume']['bot'] = true;
+$wgGrantPermissions['highvolume']['apihighlimits'] = true;
+$wgGrantPermissions['highvolume']['noratelimit'] = true;
+$wgGrantPermissions['highvolume']['markbotedits'] = true;
+
+$wgGrantPermissions['editpage']['edit'] = true;
+$wgGrantPermissions['editpage']['minoredit'] = true;
+$wgGrantPermissions['editpage']['applychangetags'] = true;
+$wgGrantPermissions['editpage']['changetags'] = true;
+
+$wgGrantPermissions['editprotected'] = $wgGrantPermissions['editpage'];
+$wgGrantPermissions['editprotected']['editprotected'] = true;
+
+$wgGrantPermissions['editmycssjs'] = $wgGrantPermissions['editpage'];
+$wgGrantPermissions['editmycssjs']['editmyusercss'] = true;
+$wgGrantPermissions['editmycssjs']['editmyuserjs'] = true;
+
+$wgGrantPermissions['editmyoptions']['editmyoptions'] = true;
+
+$wgGrantPermissions['editinterface'] = $wgGrantPermissions['editpage'];
+$wgGrantPermissions['editinterface']['editinterface'] = true;
+$wgGrantPermissions['editinterface']['editusercss'] = true;
+$wgGrantPermissions['editinterface']['edituserjs'] = true;
+
+$wgGrantPermissions['createeditmovepage'] = $wgGrantPermissions['editpage'];
+$wgGrantPermissions['createeditmovepage']['createpage'] = true;
+$wgGrantPermissions['createeditmovepage']['createtalk'] = true;
+$wgGrantPermissions['createeditmovepage']['move'] = true;
+$wgGrantPermissions['createeditmovepage']['move-rootuserpages'] = true;
+$wgGrantPermissions['createeditmovepage']['move-subpages'] = true;
+$wgGrantPermissions['createeditmovepage']['move-categorypages'] = true;
+
+$wgGrantPermissions['uploadfile']['upload'] = true;
+$wgGrantPermissions['uploadfile']['reupload-own'] = true;
+
+$wgGrantPermissions['uploadeditmovefile'] = $wgGrantPermissions['uploadfile'];
+$wgGrantPermissions['uploadeditmovefile']['reupload'] = true;
+$wgGrantPermissions['uploadeditmovefile']['reupload-shared'] = true;
+$wgGrantPermissions['uploadeditmovefile']['upload_by_url'] = true;
+$wgGrantPermissions['uploadeditmovefile']['movefile'] = true;
+$wgGrantPermissions['uploadeditmovefile']['suppressredirect'] = true;
+
+$wgGrantPermissions['patrol']['patrol'] = true;
+
+$wgGrantPermissions['rollback']['rollback'] = true;
+
+$wgGrantPermissions['blockusers']['block'] = true;
+$wgGrantPermissions['blockusers']['blockemail'] = true;
+
+$wgGrantPermissions['viewdeleted']['browsearchive'] = true;
+$wgGrantPermissions['viewdeleted']['deletedhistory'] = true;
+$wgGrantPermissions['viewdeleted']['deletedtext'] = true;
+
+$wgGrantPermissions['delete'] = $wgGrantPermissions['editpage'] +
+       $wgGrantPermissions['viewdeleted'];
+$wgGrantPermissions['delete']['delete'] = true;
+$wgGrantPermissions['delete']['bigdelete'] = true;
+$wgGrantPermissions['delete']['deletelogentry'] = true;
+$wgGrantPermissions['delete']['deleterevision'] = true;
+$wgGrantPermissions['delete']['undelete'] = true;
+
+$wgGrantPermissions['protect'] = $wgGrantPermissions['editprotected'];
+$wgGrantPermissions['protect']['protect'] = true;
+
+$wgGrantPermissions['viewmywatchlist']['viewmywatchlist'] = true;
+
+$wgGrantPermissions['editmywatchlist']['editmywatchlist'] = true;
+
+$wgGrantPermissions['sendemail']['sendemail'] = true;
+
+$wgGrantPermissions['createaccount']['createaccount'] = true;
+
+/**
+ * @var Array Map of grants to their UI grouping
+ * @since 1.27
+ */
+$wgGrantPermissionGroups = array(
+       // Hidden grants are implicitly present
+       'basic'            => 'hidden',
+
+       'editpage'            => 'page-interaction',
+       'createeditmovepage'  => 'page-interaction',
+       'editprotected'       => 'page-interaction',
+       'patrol'              => 'page-interaction',
+
+       'uploadfile'          => 'file-interaction',
+       'uploadeditmovefile'  => 'file-interaction',
+
+       'sendemail'           => 'email',
+
+       'viewmywatchlist'     => 'watchlist-interaction',
+       'editviewmywatchlist' => 'watchlist-interaction',
+
+       'editmycssjs'         => 'customization',
+       'editmyoptions'       => 'customization',
+
+       'editinterface'       => 'administration',
+       'rollback'            => 'administration',
+       'blockusers'          => 'administration',
+       'delete'              => 'administration',
+       'viewdeleted'         => 'administration',
+       'protect'             => 'administration',
+       'createaccount'       => 'administration',
+
+       'highvolume'          => 'high-volume',
+);
+
+/**
+ * @var bool Whether to enable bot passwords
+ * @since 1.27
+ */
+$wgEnableBotPasswords = true;
+
+/**
+ * Cluster for the bot_passwords table
+ * @var string|bool If false, the normal cluster will be used
+ * @since 1.27
+ */
+$wgBotPasswordsCluster = false;
+
+/**
+ * Database name for the bot_passwords table
+ *
+ * To use a database with a table prefix, set this variable to
+ * "{$database}-{$prefix}".
+ * @var string|bool If false, the normal database will be used
+ * @since 1.27
+ */
+$wgBotPasswordsDatabase = false;
+
 /** @} */ # end of user rights settings
 
 /************************************************************************//**
@@ -5547,6 +5736,11 @@ $wgTrxProfilerLimits = array(
                'writeQueryTime' => 1,
                'maxAffected' => 500
        ),
+       'POST-nonwrite' => array(
+               'masterConns' => 0,
+               'writes' => 0,
+               'readQueryTime' => 5
+       ),
        // Background job runner
        'JobRunner' => array(
                'readQueryTime' => 30,
@@ -6164,7 +6358,8 @@ $wgRCEngines = array(
 $wgRCWatchCategoryMembership = false;
 
 /**
- * Use RC Patrolling to check for vandalism
+ * Use RC Patrolling to check for vandalism (from recent changes and watchlists)
+ * New pages and new files are included.
  */
 $wgUseRCPatrol = true;
 
@@ -6173,6 +6368,13 @@ $wgUseRCPatrol = true;
  */
 $wgUseNPPatrol = true;
 
+/**
+ * Use file patrolling to check new files on Special:Newfiles
+ *
+ * @since 1.27
+ */
+$wgUseFilePatrol = true;
+
 /**
  * Log autopatrol actions to the log table
  */
@@ -7333,6 +7535,7 @@ $wgUseAjax = true;
 /**
  * List of Ajax-callable functions.
  * Extensions acting as Ajax callbacks must register here
+ * @deprecated (officially) since 1.27; use the API instead
  */
 $wgAjaxExportList = array();
 
diff --git a/includes/DerivativeRequest.php b/includes/DerivativeRequest.php
new file mode 100644 (file)
index 0000000..4c149ae
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Deal with importing all those nasty globals and things
+ *
+ * Copyright © 2003 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
+ */
+
+/**
+ * Similar to FauxRequest, but only fakes URL parameters and method
+ * (POST or GET) and use the base request for the remaining stuff
+ * (cookies, session and headers).
+ *
+ * @ingroup HTTP
+ * @since 1.19
+ */
+class DerivativeRequest extends FauxRequest {
+       private $base;
+
+       /**
+        * @param WebRequest $base
+        * @param array $data Array of *non*-urlencoded key => value pairs, the
+        *   fake GET/POST values
+        * @param bool $wasPosted Whether to treat the data as POST
+        */
+       public function __construct( WebRequest $base, $data, $wasPosted = false ) {
+               $this->base = $base;
+               parent::__construct( $data, $wasPosted );
+       }
+
+       public function getCookie( $key, $prefix = null, $default = null ) {
+               return $this->base->getCookie( $key, $prefix, $default );
+       }
+
+       public function checkSessionCookie() {
+               return $this->base->checkSessionCookie();
+       }
+
+       public function getHeader( $name, $flags = 0 ) {
+               return $this->base->getHeader( $name, $flags );
+       }
+
+       public function getAllHeaders() {
+               return $this->base->getAllHeaders();
+       }
+
+       public function getSession() {
+               return $this->base->getSession();
+       }
+
+       public function getSessionData( $key ) {
+               return $this->base->getSessionData( $key );
+       }
+
+       public function setSessionData( $key, $data ) {
+               $this->base->setSessionData( $key, $data );
+       }
+
+       public function getAcceptLang() {
+               return $this->base->getAcceptLang();
+       }
+
+       public function getIP() {
+               return $this->base->getIP();
+       }
+
+       public function getProtocol() {
+               return $this->base->getProtocol();
+       }
+
+       public function getElapsedTime() {
+               return $this->base->getElapsedTime();
+       }
+}
diff --git a/includes/Export.php b/includes/Export.php
deleted file mode 100644 (file)
index b4d7737..0000000
+++ /dev/null
@@ -1,1549 +0,0 @@
-<?php
-/**
- * Base classes for dumps and export
- *
- * Copyright © 2003, 2005, 2006 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
- */
-
-/**
- * @defgroup Dump Dump
- */
-
-/**
- * @ingroup SpecialPage Dump
- */
-class WikiExporter {
-       /** @var bool Return distinct author list (when not returning full history) */
-       public $list_authors = false;
-
-       /** @var bool */
-       public $dumpUploads = false;
-
-       /** @var bool */
-       public $dumpUploadFileContents = false;
-
-       /** @var string */
-       public $author_list = "";
-
-       const FULL = 1;
-       const CURRENT = 2;
-       const STABLE = 4; // extension defined
-       const LOGS = 8;
-       const RANGE = 16;
-
-       const BUFFER = 0;
-       const STREAM = 1;
-
-       const TEXT = 0;
-       const STUB = 1;
-
-       /** @var int */
-       public $buffer;
-
-       /** @var int */
-       public $text;
-
-       /** @var DumpOutput */
-       public $sink;
-
-       /**
-        * Returns the export schema version.
-        * @return string
-        */
-       public static function schemaVersion() {
-               return "0.10";
-       }
-
-       /**
-        * If using WikiExporter::STREAM to stream a large amount of data,
-        * provide a database connection which is not managed by
-        * LoadBalancer to read from: some history blob types will
-        * make additional queries to pull source data while the
-        * main query is still running.
-        *
-        * @param IDatabase $db
-        * @param int|array $history One of WikiExporter::FULL, WikiExporter::CURRENT,
-        *   WikiExporter::RANGE or WikiExporter::STABLE, or an associative array:
-        *   - offset: non-inclusive offset at which to start the query
-        *   - limit: maximum number of rows to return
-        *   - dir: "asc" or "desc" timestamp order
-        * @param int $buffer One of WikiExporter::BUFFER or WikiExporter::STREAM
-        * @param int $text One of WikiExporter::TEXT or WikiExporter::STUB
-        */
-       function __construct( $db, $history = WikiExporter::CURRENT,
-                       $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
-               $this->db = $db;
-               $this->history = $history;
-               $this->buffer = $buffer;
-               $this->writer = new XmlDumpWriter();
-               $this->sink = new DumpOutput();
-               $this->text = $text;
-       }
-
-       /**
-        * Set the DumpOutput or DumpFilter object which will receive
-        * various row objects and XML output for filtering. Filters
-        * can be chained or used as callbacks.
-        *
-        * @param DumpOutput $sink
-        */
-       public function setOutputSink( &$sink ) {
-               $this->sink =& $sink;
-       }
-
-       public function openStream() {
-               $output = $this->writer->openStream();
-               $this->sink->writeOpenStream( $output );
-       }
-
-       public function closeStream() {
-               $output = $this->writer->closeStream();
-               $this->sink->writeCloseStream( $output );
-       }
-
-       /**
-        * Dumps a series of page and revision records for all pages
-        * in the database, either including complete history or only
-        * the most recent version.
-        */
-       public function allPages() {
-               $this->dumpFrom( '' );
-       }
-
-       /**
-        * Dumps a series of page and revision records for those pages
-        * in the database falling within the page_id range given.
-        * @param int $start Inclusive lower limit (this id is included)
-        * @param int $end Exclusive upper limit (this id is not included)
-        *   If 0, no upper limit.
-        */
-       public function pagesByRange( $start, $end ) {
-               $condition = 'page_id >= ' . intval( $start );
-               if ( $end ) {
-                       $condition .= ' AND page_id < ' . intval( $end );
-               }
-               $this->dumpFrom( $condition );
-       }
-
-       /**
-        * Dumps a series of page and revision records for those pages
-        * in the database with revisions falling within the rev_id range given.
-        * @param int $start Inclusive lower limit (this id is included)
-        * @param int $end Exclusive upper limit (this id is not included)
-        *   If 0, no upper limit.
-        */
-       public function revsByRange( $start, $end ) {
-               $condition = 'rev_id >= ' . intval( $start );
-               if ( $end ) {
-                       $condition .= ' AND rev_id < ' . intval( $end );
-               }
-               $this->dumpFrom( $condition );
-       }
-
-       /**
-        * @param Title $title
-        */
-       public function pageByTitle( $title ) {
-               $this->dumpFrom(
-                       'page_namespace=' . $title->getNamespace() .
-                       ' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
-       }
-
-       /**
-        * @param string $name
-        * @throws MWException
-        */
-       public function pageByName( $name ) {
-               $title = Title::newFromText( $name );
-               if ( is_null( $title ) ) {
-                       throw new MWException( "Can't export invalid title" );
-               } else {
-                       $this->pageByTitle( $title );
-               }
-       }
-
-       /**
-        * @param array $names
-        */
-       public function pagesByName( $names ) {
-               foreach ( $names as $name ) {
-                       $this->pageByName( $name );
-               }
-       }
-
-       public function allLogs() {
-               $this->dumpFrom( '' );
-       }
-
-       /**
-        * @param int $start
-        * @param int $end
-        */
-       public function logsByRange( $start, $end ) {
-               $condition = 'log_id >= ' . intval( $start );
-               if ( $end ) {
-                       $condition .= ' AND log_id < ' . intval( $end );
-               }
-               $this->dumpFrom( $condition );
-       }
-
-       /**
-        * Generates the distinct list of authors of an article
-        * Not called by default (depends on $this->list_authors)
-        * Can be set by Special:Export when not exporting whole history
-        *
-        * @param array $cond
-        */
-       protected function do_list_authors( $cond ) {
-               $this->author_list = "<contributors>";
-               // rev_deleted
-
-               $res = $this->db->select(
-                       array( 'page', 'revision' ),
-                       array( 'DISTINCT rev_user_text', 'rev_user' ),
-                       array(
-                               $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
-                               $cond,
-                               'page_id = rev_id',
-                       ),
-                       __METHOD__
-               );
-
-               foreach ( $res as $row ) {
-                       $this->author_list .= "<contributor>" .
-                               "<username>" .
-                               htmlentities( $row->rev_user_text ) .
-                               "</username>" .
-                               "<id>" .
-                               $row->rev_user .
-                               "</id>" .
-                               "</contributor>";
-               }
-               $this->author_list .= "</contributors>";
-       }
-
-       /**
-        * @param string $cond
-        * @throws MWException
-        * @throws Exception
-        */
-       protected function dumpFrom( $cond = '' ) {
-               # For logging dumps...
-               if ( $this->history & self::LOGS ) {
-                       $where = array( 'user_id = log_user' );
-                       # Hide private logs
-                       $hideLogs = LogEventsList::getExcludeClause( $this->db );
-                       if ( $hideLogs ) {
-                               $where[] = $hideLogs;
-                       }
-                       # Add on any caller specified conditions
-                       if ( $cond ) {
-                               $where[] = $cond;
-                       }
-                       # Get logging table name for logging.* clause
-                       $logging = $this->db->tableName( 'logging' );
-
-                       if ( $this->buffer == WikiExporter::STREAM ) {
-                               $prev = $this->db->bufferResults( false );
-                       }
-                       $result = null; // Assuring $result is not undefined, if exception occurs early
-                       try {
-                               $result = $this->db->select( array( 'logging', 'user' ),
-                                       array( "{$logging}.*", 'user_name' ), // grab the user name
-                                       $where,
-                                       __METHOD__,
-                                       array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) )
-                               );
-                               $this->outputLogStream( $result );
-                               if ( $this->buffer == WikiExporter::STREAM ) {
-                                       $this->db->bufferResults( $prev );
-                               }
-                       } catch ( Exception $e ) {
-                               // Throwing the exception does not reliably free the resultset, and
-                               // would also leave the connection in unbuffered mode.
-
-                               // Freeing result
-                               try {
-                                       if ( $result ) {
-                                               $result->free();
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Putting database back in previous buffer mode
-                               try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
-                                               $this->db->bufferResults( $prev );
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Inform caller about problem
-                               throw $e;
-                       }
-               # For page dumps...
-               } else {
-                       $tables = array( 'page', 'revision' );
-                       $opts = array( 'ORDER BY' => 'page_id ASC' );
-                       $opts['USE INDEX'] = array();
-                       $join = array();
-                       if ( is_array( $this->history ) ) {
-                               # Time offset/limit for all pages/history...
-                               $revJoin = 'page_id=rev_page';
-                               # Set time order
-                               if ( $this->history['dir'] == 'asc' ) {
-                                       $op = '>';
-                                       $opts['ORDER BY'] = 'rev_timestamp ASC';
-                               } else {
-                                       $op = '<';
-                                       $opts['ORDER BY'] = 'rev_timestamp DESC';
-                               }
-                               # Set offset
-                               if ( !empty( $this->history['offset'] ) ) {
-                                       $revJoin .= " AND rev_timestamp $op " .
-                                               $this->db->addQuotes( $this->db->timestamp( $this->history['offset'] ) );
-                               }
-                               $join['revision'] = array( 'INNER JOIN', $revJoin );
-                               # Set query limit
-                               if ( !empty( $this->history['limit'] ) ) {
-                                       $opts['LIMIT'] = intval( $this->history['limit'] );
-                               }
-                       } elseif ( $this->history & WikiExporter::FULL ) {
-                               # Full history dumps...
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
-                       } elseif ( $this->history & WikiExporter::CURRENT ) {
-                               # Latest revision dumps...
-                               if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
-                                       $this->do_list_authors( $cond );
-                               }
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
-                       } elseif ( $this->history & WikiExporter::STABLE ) {
-                               # "Stable" revision dumps...
-                               # Default JOIN, to be overridden...
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
-                               # One, and only one hook should set this, and return false
-                               if ( Hooks::run( 'WikiExporter::dumpStableQuery', array( &$tables, &$opts, &$join ) ) ) {
-                                       throw new MWException( __METHOD__ . " given invalid history dump type." );
-                               }
-                       } elseif ( $this->history & WikiExporter::RANGE ) {
-                               # Dump of revisions within a specified range
-                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
-                               $opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' );
-                       } else {
-                               # Unknown history specification parameter?
-                               throw new MWException( __METHOD__ . " given invalid history dump type." );
-                       }
-                       # Query optimization hacks
-                       if ( $cond == '' ) {
-                               $opts[] = 'STRAIGHT_JOIN';
-                               $opts['USE INDEX']['page'] = 'PRIMARY';
-                       }
-                       # Build text join options
-                       if ( $this->text != WikiExporter::STUB ) { // 1-pass
-                               $tables[] = 'text';
-                               $join['text'] = array( 'INNER JOIN', 'rev_text_id=old_id' );
-                       }
-
-                       if ( $this->buffer == WikiExporter::STREAM ) {
-                               $prev = $this->db->bufferResults( false );
-                       }
-
-                       $result = null; // Assuring $result is not undefined, if exception occurs early
-                       try {
-                               Hooks::run( 'ModifyExportQuery',
-                                               array( $this->db, &$tables, &$cond, &$opts, &$join ) );
-
-                               # Do the query!
-                               $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
-                               # Output dump results
-                               $this->outputPageStream( $result );
-
-                               if ( $this->buffer == WikiExporter::STREAM ) {
-                                       $this->db->bufferResults( $prev );
-                               }
-                       } catch ( Exception $e ) {
-                               // Throwing the exception does not reliably free the resultset, and
-                               // would also leave the connection in unbuffered mode.
-
-                               // Freeing result
-                               try {
-                                       if ( $result ) {
-                                               $result->free();
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Putting database back in previous buffer mode
-                               try {
-                                       if ( $this->buffer == WikiExporter::STREAM ) {
-                                               $this->db->bufferResults( $prev );
-                                       }
-                               } catch ( Exception $e2 ) {
-                                       // Already in panic mode -> ignoring $e2 as $e has
-                                       // higher priority
-                               }
-
-                               // Inform caller about problem
-                               throw $e;
-                       }
-               }
-       }
-
-       /**
-        * Runs through a query result set dumping page and revision records.
-        * The result set should be sorted/grouped by page to avoid duplicate
-        * page records in the output.
-        *
-        * Should be safe for
-        * streaming (non-buffered) queries, as long as it was made on a
-        * separate database connection not managed by LoadBalancer; some
-        * blob storage types will make queries to pull source data.
-        *
-        * @param ResultWrapper $resultset
-        */
-       protected function outputPageStream( $resultset ) {
-               $last = null;
-               foreach ( $resultset as $row ) {
-                       if ( $last === null ||
-                               $last->page_namespace != $row->page_namespace ||
-                               $last->page_title != $row->page_title ) {
-                               if ( $last !== null ) {
-                                       $output = '';
-                                       if ( $this->dumpUploads ) {
-                                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
-                                       }
-                                       $output .= $this->writer->closePage();
-                                       $this->sink->writeClosePage( $output );
-                               }
-                               $output = $this->writer->openPage( $row );
-                               $this->sink->writeOpenPage( $row, $output );
-                               $last = $row;
-                       }
-                       $output = $this->writer->writeRevision( $row );
-                       $this->sink->writeRevision( $row, $output );
-               }
-               if ( $last !== null ) {
-                       $output = '';
-                       if ( $this->dumpUploads ) {
-                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
-                       }
-                       $output .= $this->author_list;
-                       $output .= $this->writer->closePage();
-                       $this->sink->writeClosePage( $output );
-               }
-       }
-
-       /**
-        * @param ResultWrapper $resultset
-        */
-       protected function outputLogStream( $resultset ) {
-               foreach ( $resultset as $row ) {
-                       $output = $this->writer->writeLogItem( $row );
-                       $this->sink->writeLogItem( $row, $output );
-               }
-       }
-}
-
-/**
- * @ingroup Dump
- */
-class XmlDumpWriter {
-       /**
-        * Opens the XML output stream's root "<mediawiki>" element.
-        * This does not include an xml directive, so is safe to include
-        * as a subelement in a larger XML stream. Namespace and XML Schema
-        * references are included.
-        *
-        * Output will be encoded in UTF-8.
-        *
-        * @return string
-        */
-       function openStream() {
-               global $wgLanguageCode;
-               $ver = WikiExporter::schemaVersion();
-               return Xml::element( 'mediawiki', array(
-                       'xmlns'              => "http://www.mediawiki.org/xml/export-$ver/",
-                       'xmlns:xsi'          => "http://www.w3.org/2001/XMLSchema-instance",
-                       /*
-                        * When a new version of the schema is created, it needs staging on mediawiki.org.
-                        * This requires a change in the operations/mediawiki-config git repo.
-                        *
-                        * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
-                        * you copy in the new xsd file.
-                        *
-                        * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
-                        * echo "http://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
-                        */
-                       'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
-                               "http://www.mediawiki.org/xml/export-$ver.xsd",
-                       'version'            => $ver,
-                       'xml:lang'           => $wgLanguageCode ),
-                       null ) .
-                       "\n" .
-                       $this->siteInfo();
-       }
-
-       /**
-        * @return string
-        */
-       function siteInfo() {
-               $info = array(
-                       $this->sitename(),
-                       $this->dbname(),
-                       $this->homelink(),
-                       $this->generator(),
-                       $this->caseSetting(),
-                       $this->namespaces() );
-               return "  <siteinfo>\n    " .
-                       implode( "\n    ", $info ) .
-                       "\n  </siteinfo>\n";
-       }
-
-       /**
-        * @return string
-        */
-       function sitename() {
-               global $wgSitename;
-               return Xml::element( 'sitename', array(), $wgSitename );
-       }
-
-       /**
-        * @return string
-        */
-       function dbname() {
-               global $wgDBname;
-               return Xml::element( 'dbname', array(), $wgDBname );
-       }
-
-       /**
-        * @return string
-        */
-       function generator() {
-               global $wgVersion;
-               return Xml::element( 'generator', array(), "MediaWiki $wgVersion" );
-       }
-
-       /**
-        * @return string
-        */
-       function homelink() {
-               return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalURL() );
-       }
-
-       /**
-        * @return string
-        */
-       function caseSetting() {
-               global $wgCapitalLinks;
-               // "case-insensitive" option is reserved for future
-               $sensitivity = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
-               return Xml::element( 'case', array(), $sensitivity );
-       }
-
-       /**
-        * @return string
-        */
-       function namespaces() {
-               global $wgContLang;
-               $spaces = "<namespaces>\n";
-               foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
-                       $spaces .= '      ' .
-                               Xml::element( 'namespace',
-                                       array(
-                                               'key' => $ns,
-                                               'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
-                                       ), $title ) . "\n";
-               }
-               $spaces .= "    </namespaces>";
-               return $spaces;
-       }
-
-       /**
-        * Closes the output stream with the closing root element.
-        * Call when finished dumping things.
-        *
-        * @return string
-        */
-       function closeStream() {
-               return "</mediawiki>\n";
-       }
-
-       /**
-        * Opens a "<page>" section on the output stream, with data
-        * from the given database row.
-        *
-        * @param object $row
-        * @return string
-        */
-       public function openPage( $row ) {
-               $out = "  <page>\n";
-               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-               $out .= '    ' . Xml::elementClean( 'title', array(), self::canonicalTitle( $title ) ) . "\n";
-               $out .= '    ' . Xml::element( 'ns', array(), strval( $row->page_namespace ) ) . "\n";
-               $out .= '    ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
-               if ( $row->page_is_redirect ) {
-                       $page = WikiPage::factory( $title );
-                       $redirect = $page->getRedirectTarget();
-                       if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
-                               $out .= '    ';
-                               $out .= Xml::element( 'redirect', array( 'title' => self::canonicalTitle( $redirect ) ) );
-                               $out .= "\n";
-                       }
-               }
-
-               if ( $row->page_restrictions != '' ) {
-                       $out .= '    ' . Xml::element( 'restrictions', array(),
-                               strval( $row->page_restrictions ) ) . "\n";
-               }
-
-               Hooks::run( 'XmlDumpWriterOpenPage', array( $this, &$out, $row, $title ) );
-
-               return $out;
-       }
-
-       /**
-        * Closes a "<page>" section on the output stream.
-        *
-        * @access private
-        * @return string
-        */
-       function closePage() {
-               return "  </page>\n";
-       }
-
-       /**
-        * Dumps a "<revision>" section on the output stream, with
-        * data filled in from the given database row.
-        *
-        * @param object $row
-        * @return string
-        * @access private
-        */
-       function writeRevision( $row ) {
-
-               $out = "    <revision>\n";
-               $out .= "      " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
-               if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
-                       $out .= "      " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n";
-               }
-
-               $out .= $this->writeTimestamp( $row->rev_timestamp );
-
-               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_USER ) ) {
-                       $out .= "      " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
-               } else {
-                       $out .= $this->writeContributor( $row->rev_user, $row->rev_user_text );
-               }
-
-               if ( isset( $row->rev_minor_edit ) && $row->rev_minor_edit ) {
-                       $out .= "      <minor/>\n";
-               }
-               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_COMMENT ) ) {
-                       $out .= "      " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
-               } elseif ( $row->rev_comment != '' ) {
-                       $out .= "      " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n";
-               }
-
-               if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) {
-                       $content_model = strval( $row->rev_content_model );
-               } else {
-                       // probably using $wgContentHandlerUseDB = false;
-                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
-                       $content_model = ContentHandler::getDefaultModelFor( $title );
-               }
-
-               $content_handler = ContentHandler::getForModelID( $content_model );
-
-               if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) {
-                       $content_format = strval( $row->rev_content_format );
-               } else {
-                       // probably using $wgContentHandlerUseDB = false;
-                       $content_format = $content_handler->getDefaultFormat();
-               }
-
-               $out .= "      " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
-               $out .= "      " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
-
-               $text = '';
-               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
-                       $out .= "      " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
-               } elseif ( isset( $row->old_text ) ) {
-                       // Raw text from the database may have invalid chars
-                       $text = strval( Revision::getRevisionText( $row ) );
-                       $text = $content_handler->exportTransform( $text, $content_format );
-                       $out .= "      " . Xml::elementClean( 'text',
-                               array( 'xml:space' => 'preserve', 'bytes' => intval( $row->rev_len ) ),
-                               strval( $text ) ) . "\n";
-               } else {
-                       // Stub output
-                       $out .= "      " . Xml::element( 'text',
-                               array( 'id' => $row->rev_text_id, 'bytes' => intval( $row->rev_len ) ),
-                               "" ) . "\n";
-               }
-
-               if ( isset( $row->rev_sha1 )
-                       && $row->rev_sha1
-                       && !( $row->rev_deleted & Revision::DELETED_TEXT )
-               ) {
-                       $out .= "      " . Xml::element( 'sha1', null, strval( $row->rev_sha1 ) ) . "\n";
-               } else {
-                       $out .= "      <sha1/>\n";
-               }
-
-               Hooks::run( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
-
-               $out .= "    </revision>\n";
-
-               return $out;
-       }
-
-       /**
-        * Dumps a "<logitem>" section on the output stream, with
-        * data filled in from the given database row.
-        *
-        * @param object $row
-        * @return string
-        * @access private
-        */
-       function writeLogItem( $row ) {
-
-               $out = "  <logitem>\n";
-               $out .= "    " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
-
-               $out .= $this->writeTimestamp( $row->log_timestamp, "    " );
-
-               if ( $row->log_deleted & LogPage::DELETED_USER ) {
-                       $out .= "    " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
-               } else {
-                       $out .= $this->writeContributor( $row->log_user, $row->user_name, "    " );
-               }
-
-               if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
-                       $out .= "    " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
-               } elseif ( $row->log_comment != '' ) {
-                       $out .= "    " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
-               }
-
-               $out .= "    " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
-               $out .= "    " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
-
-               if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
-                       $out .= "    " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
-               } else {
-                       $title = Title::makeTitle( $row->log_namespace, $row->log_title );
-                       $out .= "    " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
-                       $out .= "    " . Xml::elementClean( 'params',
-                               array( 'xml:space' => 'preserve' ),
-                               strval( $row->log_params ) ) . "\n";
-               }
-
-               $out .= "  </logitem>\n";
-
-               return $out;
-       }
-
-       /**
-        * @param string $timestamp
-        * @param string $indent Default to six spaces
-        * @return string
-        */
-       function writeTimestamp( $timestamp, $indent = "      " ) {
-               $ts = wfTimestamp( TS_ISO_8601, $timestamp );
-               return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
-       }
-
-       /**
-        * @param int $id
-        * @param string $text
-        * @param string $indent Default to six spaces
-        * @return string
-        */
-       function writeContributor( $id, $text, $indent = "      " ) {
-               $out = $indent . "<contributor>\n";
-               if ( $id || !IP::isValid( $text ) ) {
-                       $out .= $indent . "  " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
-                       $out .= $indent . "  " . Xml::element( 'id', null, strval( $id ) ) . "\n";
-               } else {
-                       $out .= $indent . "  " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
-               }
-               $out .= $indent . "</contributor>\n";
-               return $out;
-       }
-
-       /**
-        * Warning! This data is potentially inconsistent. :(
-        * @param object $row
-        * @param bool $dumpContents
-        * @return string
-        */
-       function writeUploads( $row, $dumpContents = false ) {
-               if ( $row->page_namespace == NS_FILE ) {
-                       $img = wfLocalFile( $row->page_title );
-                       if ( $img && $img->exists() ) {
-                               $out = '';
-                               foreach ( array_reverse( $img->getHistory() ) as $ver ) {
-                                       $out .= $this->writeUpload( $ver, $dumpContents );
-                               }
-                               $out .= $this->writeUpload( $img, $dumpContents );
-                               return $out;
-                       }
-               }
-               return '';
-       }
-
-       /**
-        * @param File $file
-        * @param bool $dumpContents
-        * @return string
-        */
-       function writeUpload( $file, $dumpContents = false ) {
-               if ( $file->isOld() ) {
-                       $archiveName = "      " .
-                               Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
-               } else {
-                       $archiveName = '';
-               }
-               if ( $dumpContents ) {
-                       $be = $file->getRepo()->getBackend();
-                       # Dump file as base64
-                       # Uses only XML-safe characters, so does not need escaping
-                       # @todo Too bad this loads the contents into memory (script might swap)
-                       $contents = '      <contents encoding="base64">' .
-                               chunk_split( base64_encode(
-                                       $be->getFileContents( array( 'src' => $file->getPath() ) ) ) ) .
-                               "      </contents>\n";
-               } else {
-                       $contents = '';
-               }
-               if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
-                       $comment = Xml::element( 'comment', array( 'deleted' => 'deleted' ) );
-               } else {
-                       $comment = Xml::elementClean( 'comment', null, $file->getDescription() );
-               }
-               return "    <upload>\n" .
-                       $this->writeTimestamp( $file->getTimestamp() ) .
-                       $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
-                       "      " . $comment . "\n" .
-                       "      " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
-                       $archiveName .
-                       "      " . Xml::element( 'src', null, $file->getCanonicalURL() ) . "\n" .
-                       "      " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
-                       "      " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
-                       "      " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
-                       $contents .
-                       "    </upload>\n";
-       }
-
-       /**
-        * Return prefixed text form of title, but using the content language's
-        * canonical namespace. This skips any special-casing such as gendered
-        * user namespaces -- which while useful, are not yet listed in the
-        * XML "<siteinfo>" data so are unsafe in export.
-        *
-        * @param Title $title
-        * @return string
-        * @since 1.18
-        */
-       public static function canonicalTitle( Title $title ) {
-               if ( $title->isExternal() ) {
-                       return $title->getPrefixedText();
-               }
-
-               global $wgContLang;
-               $prefix = $wgContLang->getFormattedNsText( $title->getNamespace() );
-
-               if ( $prefix !== '' ) {
-                       $prefix .= ':';
-               }
-
-               return $prefix . $title->getText();
-       }
-}
-
-/**
- * Base class for output stream; prints to stdout or buffer or wherever.
- * @ingroup Dump
- */
-class DumpOutput {
-
-       /**
-        * @param string $string
-        */
-       function writeOpenStream( $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeLogItem( $rev, $string ) {
-               $this->write( $string );
-       }
-
-       /**
-        * Override to write to a different stream type.
-        * @param string $string
-        * @return bool
-        */
-       function write( $string ) {
-               print $string;
-       }
-
-       /**
-        * Close the old file, move it to a specified name,
-        * and reopen new file with the old name. Use this
-        * for writing out a file in multiple pieces
-        * at specified checkpoints (e.g. every n hours).
-        * @param string|array $newname File name. May be a string or an array with one element
-        */
-       function closeRenameAndReopen( $newname ) {
-       }
-
-       /**
-        * Close the old file, and move it to a specified name.
-        * Use this for the last piece of a file written out
-        * at specified checkpoints (e.g. every n hours).
-        * @param string|array $newname File name. May be a string or an array with one element
-        * @param bool $open If true, a new file with the old filename will be opened
-        *   again for writing (default: false)
-        */
-       function closeAndRename( $newname, $open = false ) {
-       }
-
-       /**
-        * Returns the name of the file or files which are
-        * being written to, if there are any.
-        * @return null
-        */
-       function getFilenames() {
-               return null;
-       }
-}
-
-/**
- * Stream outputter to send data to a file.
- * @ingroup Dump
- */
-class DumpFileOutput extends DumpOutput {
-       protected $handle = false, $filename;
-
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               $this->handle = fopen( $file, "wt" );
-               $this->filename = $file;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               parent::writeCloseStream( $string );
-               if ( $this->handle ) {
-                       fclose( $this->handle );
-                       $this->handle = false;
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function write( $string ) {
-               fputs( $this->handle, $string );
-       }
-
-       /**
-        * @param string $newname
-        */
-       function closeRenameAndReopen( $newname ) {
-               $this->closeAndRename( $newname, true );
-       }
-
-       /**
-        * @param string $newname
-        * @throws MWException
-        */
-       function renameOrException( $newname ) {
-                       if ( !rename( $this->filename, $newname ) ) {
-                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
-                       }
-       }
-
-       /**
-        * @param array $newname
-        * @return string
-        * @throws MWException
-        */
-       function checkRenameArgCount( $newname ) {
-               if ( is_array( $newname ) ) {
-                       if ( count( $newname ) > 1 ) {
-                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
-                       } else {
-                               $newname = $newname[0];
-                       }
-               }
-               return $newname;
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $newname = $this->checkRenameArgCount( $newname );
-               if ( $newname ) {
-                       if ( $this->handle ) {
-                               fclose( $this->handle );
-                               $this->handle = false;
-                       }
-                       $this->renameOrException( $newname );
-                       if ( $open ) {
-                               $this->handle = fopen( $this->filename, "wt" );
-                       }
-               }
-       }
-
-       /**
-        * @return string|null
-        */
-       function getFilenames() {
-               return $this->filename;
-       }
-}
-
-/**
- * Stream outputter to send data to a file via some filter program.
- * Even if compression is available in a library, using a separate
- * program can allow us to make use of a multi-processor system.
- * @ingroup Dump
- */
-class DumpPipeOutput extends DumpFileOutput {
-       protected $command, $filename;
-       protected $procOpenResource = false;
-
-       /**
-        * @param string $command
-        * @param string $file
-        */
-       function __construct( $command, $file = null ) {
-               if ( !is_null( $file ) ) {
-                       $command .= " > " . wfEscapeShellArg( $file );
-               }
-
-               $this->startCommand( $command );
-               $this->command = $command;
-               $this->filename = $file;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               parent::writeCloseStream( $string );
-               if ( $this->procOpenResource ) {
-                       proc_close( $this->procOpenResource );
-                       $this->procOpenResource = false;
-               }
-       }
-
-       /**
-        * @param string $command
-        */
-       function startCommand( $command ) {
-               $spec = array(
-                       0 => array( "pipe", "r" ),
-               );
-               $pipes = array();
-               $this->procOpenResource = proc_open( $command, $spec, $pipes );
-               $this->handle = $pipes[0];
-       }
-
-       /**
-        * @param string $newname
-        */
-       function closeRenameAndReopen( $newname ) {
-               $this->closeAndRename( $newname, true );
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $newname = $this->checkRenameArgCount( $newname );
-               if ( $newname ) {
-                       if ( $this->handle ) {
-                               fclose( $this->handle );
-                               $this->handle = false;
-                       }
-                       if ( $this->procOpenResource ) {
-                               proc_close( $this->procOpenResource );
-                               $this->procOpenResource = false;
-                       }
-                       $this->renameOrException( $newname );
-                       if ( $open ) {
-                               $command = $this->command;
-                               $command .= " > " . wfEscapeShellArg( $this->filename );
-                               $this->startCommand( $command );
-                       }
-               }
-       }
-}
-
-/**
- * Sends dump output via the gzip compressor.
- * @ingroup Dump
- */
-class DumpGZipOutput extends DumpPipeOutput {
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               parent::__construct( "gzip", $file );
-       }
-}
-
-/**
- * Sends dump output via the bgzip2 compressor.
- * @ingroup Dump
- */
-class DumpBZip2Output extends DumpPipeOutput {
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               parent::__construct( "bzip2", $file );
-       }
-}
-
-/**
- * Sends dump output via the p7zip compressor.
- * @ingroup Dump
- */
-class Dump7ZipOutput extends DumpPipeOutput {
-       /**
-        * @param string $file
-        */
-       function __construct( $file ) {
-               $command = $this->setup7zCommand( $file );
-               parent::__construct( $command );
-               $this->filename = $file;
-       }
-
-       /**
-        * @param string $file
-        * @return string
-        */
-       function setup7zCommand( $file ) {
-               $command = "7za a -bd -si -mx=4 " . wfEscapeShellArg( $file );
-               // Suppress annoying useless crap from p7zip
-               // Unfortunately this could suppress real error messages too
-               $command .= ' >' . wfGetNull() . ' 2>&1';
-               return $command;
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $newname = $this->checkRenameArgCount( $newname );
-               if ( $newname ) {
-                       fclose( $this->handle );
-                       proc_close( $this->procOpenResource );
-                       $this->renameOrException( $newname );
-                       if ( $open ) {
-                               $command = $this->setup7zCommand( $this->filename );
-                               $this->startCommand( $command );
-                       }
-               }
-       }
-}
-
-/**
- * Dump output filter class.
- * This just does output filtering and streaming; XML formatting is done
- * higher up, so be careful in what you do.
- * @ingroup Dump
- */
-class DumpFilter {
-       /**
-        * @var DumpOutput
-        * FIXME will need to be made protected whenever legacy code
-        * is updated.
-        */
-       public $sink;
-
-       /**
-        * @var bool
-        */
-       protected $sendingThisPage;
-
-       /**
-        * @param DumpOutput $sink
-        */
-       function __construct( &$sink ) {
-               $this->sink =& $sink;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeOpenStream( $string ) {
-               $this->sink->writeOpenStream( $string );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               $this->sink->writeCloseStream( $string );
-       }
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               $this->sendingThisPage = $this->pass( $page, $string );
-               if ( $this->sendingThisPage ) {
-                       $this->sink->writeOpenPage( $page, $string );
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               if ( $this->sendingThisPage ) {
-                       $this->sink->writeClosePage( $string );
-                       $this->sendingThisPage = false;
-               }
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               if ( $this->sendingThisPage ) {
-                       $this->sink->writeRevision( $rev, $string );
-               }
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeLogItem( $rev, $string ) {
-               $this->sink->writeRevision( $rev, $string );
-       }
-
-       /**
-        * @param string $newname
-        */
-       function closeRenameAndReopen( $newname ) {
-               $this->sink->closeRenameAndReopen( $newname );
-       }
-
-       /**
-        * @param string $newname
-        * @param bool $open
-        */
-       function closeAndRename( $newname, $open = false ) {
-               $this->sink->closeAndRename( $newname, $open );
-       }
-
-       /**
-        * @return array
-        */
-       function getFilenames() {
-               return $this->sink->getFilenames();
-       }
-
-       /**
-        * Override for page-based filter types.
-        * @param object $page
-        * @return bool
-        */
-       function pass( $page ) {
-               return true;
-       }
-}
-
-/**
- * Simple dump output filter to exclude all talk pages.
- * @ingroup Dump
- */
-class DumpNotalkFilter extends DumpFilter {
-       /**
-        * @param object $page
-        * @return bool
-        */
-       function pass( $page ) {
-               return !MWNamespace::isTalk( $page->page_namespace );
-       }
-}
-
-/**
- * Dump output filter to include or exclude pages in a given set of namespaces.
- * @ingroup Dump
- */
-class DumpNamespaceFilter extends DumpFilter {
-       /** @var bool */
-       public $invert = false;
-
-       /** @var array */
-       public $namespaces = array();
-
-       /**
-        * @param DumpOutput $sink
-        * @param array $param
-        * @throws MWException
-        */
-       function __construct( &$sink, $param ) {
-               parent::__construct( $sink );
-
-               $constants = array(
-                       "NS_MAIN"           => NS_MAIN,
-                       "NS_TALK"           => NS_TALK,
-                       "NS_USER"           => NS_USER,
-                       "NS_USER_TALK"      => NS_USER_TALK,
-                       "NS_PROJECT"        => NS_PROJECT,
-                       "NS_PROJECT_TALK"   => NS_PROJECT_TALK,
-                       "NS_FILE"           => NS_FILE,
-                       "NS_FILE_TALK"      => NS_FILE_TALK,
-                       "NS_IMAGE"          => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE
-                       "NS_IMAGE_TALK"     => NS_IMAGE_TALK,
-                       "NS_MEDIAWIKI"      => NS_MEDIAWIKI,
-                       "NS_MEDIAWIKI_TALK" => NS_MEDIAWIKI_TALK,
-                       "NS_TEMPLATE"       => NS_TEMPLATE,
-                       "NS_TEMPLATE_TALK"  => NS_TEMPLATE_TALK,
-                       "NS_HELP"           => NS_HELP,
-                       "NS_HELP_TALK"      => NS_HELP_TALK,
-                       "NS_CATEGORY"       => NS_CATEGORY,
-                       "NS_CATEGORY_TALK"  => NS_CATEGORY_TALK );
-
-               if ( $param { 0 } == '!' ) {
-                       $this->invert = true;
-                       $param = substr( $param, 1 );
-               }
-
-               foreach ( explode( ',', $param ) as $key ) {
-                       $key = trim( $key );
-                       if ( isset( $constants[$key] ) ) {
-                               $ns = $constants[$key];
-                               $this->namespaces[$ns] = true;
-                       } elseif ( is_numeric( $key ) ) {
-                               $ns = intval( $key );
-                               $this->namespaces[$ns] = true;
-                       } else {
-                               throw new MWException( "Unrecognized namespace key '$key'\n" );
-                       }
-               }
-       }
-
-       /**
-        * @param object $page
-        * @return bool
-        */
-       function pass( $page ) {
-               $match = isset( $this->namespaces[$page->page_namespace] );
-               return $this->invert xor $match;
-       }
-}
-
-/**
- * Dump output filter to include only the last revision in each page sequence.
- * @ingroup Dump
- */
-class DumpLatestFilter extends DumpFilter {
-       public $page;
-
-       public $pageString;
-
-       public $rev;
-
-       public $revString;
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               $this->page = $page;
-               $this->pageString = $string;
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               if ( $this->rev ) {
-                       $this->sink->writeOpenPage( $this->page, $this->pageString );
-                       $this->sink->writeRevision( $this->rev, $this->revString );
-                       $this->sink->writeClosePage( $string );
-               }
-               $this->rev = null;
-               $this->revString = null;
-               $this->page = null;
-               $this->pageString = null;
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               if ( $rev->rev_id == $this->page->page_latest ) {
-                       $this->rev = $rev;
-                       $this->revString = $string;
-               }
-       }
-}
-
-/**
- * Base class for output stream; prints to stdout or buffer or wherever.
- * @ingroup Dump
- */
-class DumpMultiWriter {
-
-       /**
-        * @param array $sinks
-        */
-       function __construct( $sinks ) {
-               $this->sinks = $sinks;
-               $this->count = count( $sinks );
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeOpenStream( $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeOpenStream( $string );
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeCloseStream( $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeCloseStream( $string );
-               }
-       }
-
-       /**
-        * @param object $page
-        * @param string $string
-        */
-       function writeOpenPage( $page, $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeOpenPage( $page, $string );
-               }
-       }
-
-       /**
-        * @param string $string
-        */
-       function writeClosePage( $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeClosePage( $string );
-               }
-       }
-
-       /**
-        * @param object $rev
-        * @param string $string
-        */
-       function writeRevision( $rev, $string ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->writeRevision( $rev, $string );
-               }
-       }
-
-       /**
-        * @param array $newnames
-        */
-       function closeRenameAndReopen( $newnames ) {
-               $this->closeAndRename( $newnames, true );
-       }
-
-       /**
-        * @param array $newnames
-        * @param bool $open
-        */
-       function closeAndRename( $newnames, $open = false ) {
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $this->sinks[$i]->closeAndRename( $newnames[$i], $open );
-               }
-       }
-
-       /**
-        * @return array
-        */
-       function getFilenames() {
-               $filenames = array();
-               for ( $i = 0; $i < $this->count; $i++ ) {
-                       $filenames[] = $this->sinks[$i]->getFilenames();
-               }
-               return $filenames;
-       }
-}
diff --git a/includes/FauxRequest.php b/includes/FauxRequest.php
new file mode 100644 (file)
index 0000000..f049d2e
--- /dev/null
@@ -0,0 +1,242 @@
+<?php
+/**
+ * Deal with importing all those nasty globals and things
+ *
+ * Copyright © 2003 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
+ */
+
+use MediaWiki\Session\SessionManager;
+
+/**
+ * WebRequest clone which takes values from a provided array.
+ *
+ * @ingroup HTTP
+ */
+class FauxRequest extends WebRequest {
+       private $wasPosted = false;
+       private $requestUrl;
+       protected $cookies = array();
+
+       /**
+        * @param array $data Array of *non*-urlencoded key => value pairs, the
+        *   fake GET/POST values
+        * @param bool $wasPosted Whether to treat the data as POST
+        * @param MediaWiki\\Session\\Session|array|null $session Session, session
+        *  data array, or null
+        * @param string $protocol 'http' or 'https'
+        * @throws MWException
+        */
+       public function __construct( $data = array(), $wasPosted = false,
+               $session = null, $protocol = 'http'
+       ) {
+               $this->requestTime = microtime( true );
+
+               if ( is_array( $data ) ) {
+                       $this->data = $data;
+               } else {
+                       throw new MWException( "FauxRequest() got bogus data" );
+               }
+               $this->wasPosted = $wasPosted;
+               if ( $session instanceof MediaWiki\Session\Session ) {
+                       $this->sessionId = $session->getSessionId();
+               } elseif ( is_array( $session ) ) {
+                       $mwsession = SessionManager::singleton()->getEmptySession( $this );
+                       $this->sessionId = $mwsession->getSessionId();
+                       foreach ( $session as $key => $value ) {
+                               $mwsession->set( $key, $value );
+                       }
+               } elseif ( $session !== null ) {
+                       throw new MWException( "FauxRequest() got bogus session" );
+               }
+               $this->protocol = $protocol;
+       }
+
+       /**
+        * Initialise the header list
+        */
+       protected function initHeaders() {
+               // Nothing to init
+       }
+
+       /**
+        * @param string $name
+        * @param string $default
+        * @return string
+        */
+       public function getText( $name, $default = '' ) {
+               # Override; don't recode since we're using internal data
+               return (string)$this->getVal( $name, $default );
+       }
+
+       /**
+        * @return array
+        */
+       public function getValues() {
+               return $this->data;
+       }
+
+       /**
+        * @return array
+        */
+       public function getQueryValues() {
+               if ( $this->wasPosted ) {
+                       return array();
+               } else {
+                       return $this->data;
+               }
+       }
+
+       public function getMethod() {
+               return $this->wasPosted ? 'POST' : 'GET';
+       }
+
+       /**
+        * @return bool
+        */
+       public function wasPosted() {
+               return $this->wasPosted;
+       }
+
+       public function getCookie( $key, $prefix = null, $default = null ) {
+               if ( $prefix === null ) {
+                       global $wgCookiePrefix;
+                       $prefix = $wgCookiePrefix;
+               }
+               $name = $prefix . $key;
+               return isset( $this->cookies[$name] ) ? $this->cookies[$name] : $default;
+       }
+
+       /**
+        * @since 1.26
+        * @param string $name Unprefixed name of the cookie to set
+        * @param string|null $value Value of the cookie to set
+        * @param string|null $prefix Cookie prefix. Defaults to $wgCookiePrefix
+        */
+       public function setCookie( $key, $value, $prefix = null ) {
+               $this->setCookies( array( $key => $value ), $prefix );
+       }
+
+       /**
+        * @since 1.26
+        * @param array $cookies
+        * @param string|null $prefix Cookie prefix. Defaults to $wgCookiePrefix
+        */
+       public function setCookies( $cookies, $prefix = null ) {
+               if ( $prefix === null ) {
+                       global $wgCookiePrefix;
+                       $prefix = $wgCookiePrefix;
+               }
+               foreach ( $cookies as $key => $value ) {
+                       $name = $prefix . $key;
+                       $this->cookies[$name] = $value;
+               }
+       }
+
+       /**
+        * @since 1.25
+        */
+       public function setRequestURL( $url ) {
+               $this->requestUrl = $url;
+       }
+
+       /**
+        * @since 1.25 MWException( "getRequestURL not implemented" )
+        * no longer thrown.
+        */
+       public function getRequestURL() {
+               if ( $this->requestUrl === null ) {
+                       throw new MWException( 'Request URL not set' );
+               }
+               return $this->requestUrl;
+       }
+
+       public function getProtocol() {
+               return $this->protocol;
+       }
+
+       /**
+        * @param string $name
+        * @param string $val
+        */
+       public function setHeader( $name, $val ) {
+               $this->setHeaders( array( $name => $val ) );
+       }
+
+       /**
+        * @since 1.26
+        * @param array $headers
+        */
+       public function setHeaders( $headers ) {
+               foreach ( $headers as $name => $val ) {
+                       $name = strtoupper( $name );
+                       $this->headers[$name] = $val;
+               }
+       }
+
+       /**
+        * @return array|null
+        */
+       public function getSessionArray() {
+               if ( $this->sessionId !== null ) {
+                       return iterator_to_array( $this->getSession() );
+               }
+               return null;
+       }
+
+       /**
+        * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
+        * @return string
+        */
+       public function getRawQueryString() {
+               return '';
+       }
+
+       /**
+        * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
+        * @return string
+        */
+       public function getRawPostString() {
+               return '';
+       }
+
+       /**
+        * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
+        * @return string
+        */
+       public function getRawInput() {
+               return '';
+       }
+
+       /**
+        * @param array $extWhitelist
+        * @return bool
+        */
+       public function checkUrlExtension( $extWhitelist = array() ) {
+               return true;
+       }
+
+       /**
+        * @return string
+        */
+       protected function getRawIP() {
+               return '127.0.0.1';
+       }
+}
index 7f05bb0..14f3cc1 100644 (file)
@@ -96,7 +96,7 @@ class GitInfo {
         *
         * @param string $repoDir The root directory of the repo where .git can be found
         * @return string Path to GitInfo cache file in $wgGitInfoCacheDirectory or
-        * null if $wgGitInfoCacheDirectory is false (cache disabled).
+        * fallback in the extension directory itself
         * @since 1.24
         */
        protected static function getCacheFilePath( $repoDir ) {
@@ -119,9 +119,13 @@ class GitInfo {
                        // a filename
                        $repoName = strtr( $repoName, DIRECTORY_SEPARATOR, '-' );
                        $fileName = 'info' . $repoName . '.json';
-                       return "{$wgGitInfoCacheDirectory}/{$fileName}";
+                       $cachePath = "{$wgGitInfoCacheDirectory}/{$fileName}";
+                       if ( is_readable( $cachePath ) ) {
+                               return $cachePath;
+                       }
                }
-               return null;
+
+               return "$repoDir/gitinfo.json";
        }
 
        /**
index e30b371..928066b 100644 (file)
@@ -26,6 +26,7 @@ if ( !defined( 'MEDIAWIKI' ) ) {
 
 use Liuggio\StatsdClient\Sender\SocketSender;
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Session\SessionManager;
 
 // Hide compatibility functions from Doxygen
 /// @cond
@@ -1038,7 +1039,12 @@ function wfMatchesDomainList( $url, $domains ) {
  * @since 1.25 support for additional context data
  *
  * @param string $text
- * @param string|bool $dest Unused
+ * @param string|bool $dest Destination of the message:
+ *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
+ *     - 'private': excluded from HTML output
+ *   For backward compatibility, it can also take a boolean:
+ *     - true: same as 'all'
+ *     - false: same as 'private'
  * @param array $context Additional logging context data
  */
 function wfDebug( $text, $dest = 'all', array $context = array() ) {
@@ -1065,6 +1071,7 @@ function wfDebug( $text, $dest = 'all', array $context = array() ) {
        if ( $wgDebugLogPrefix !== '' ) {
                $context['prefix'] = $wgDebugLogPrefix;
        }
+       $context['private'] = ( $dest === false || $dest === 'private' );
 
        $logger = LoggerFactory::getInstance( 'wfDebug' );
        $logger->debug( $text, $context );
@@ -1126,7 +1133,6 @@ function wfDebugMem( $exact = false ) {
  * @param string $text
  * @param string|bool $dest Destination of the message:
  *     - 'all': both to the log and HTML (debug toolbar or HTML comments)
- *     - 'log': only to the log and not in HTML
  *     - 'private': only to the specific log if set in $wgDebugLogGroups and
  *       discarded otherwise
  *   For backward compatibility, it can also take a boolean:
@@ -1137,17 +1143,10 @@ function wfDebugMem( $exact = false ) {
 function wfDebugLog(
        $logGroup, $text, $dest = 'all', array $context = array()
 ) {
-       // Turn $dest into a string if it's a boolean (for b/c)
-       if ( $dest === true ) {
-               $dest = 'all';
-       } elseif ( $dest === false ) {
-               $dest = 'private';
-       }
-
        $text = trim( $text );
 
        $logger = LoggerFactory::getInstance( $logGroup );
-       $context['private'] = ( $dest === 'private' );
+       $context['private'] = ( $dest === false || $dest === 'private' );
        $logger->info( $text, $context );
 }
 
@@ -1462,159 +1461,6 @@ function wfMessageFallback( /*...*/ ) {
        return call_user_func_array( 'Message::newFallbackSequence', $args );
 }
 
-/**
- * Get a message from anywhere, for the current user language.
- *
- * Use wfMsgForContent() instead if the message should NOT
- * change depending on the user preferences.
- *
- * @deprecated since 1.18
- *
- * @param string $key Lookup key for the message, usually
- *    defined in languages/Language.php
- *
- * Parameters to the message, which can be used to insert variable text into
- * it, can be passed to this function in the following formats:
- * - One per argument, starting at the second parameter
- * - As an array in the second parameter
- * These are not shown in the function definition.
- *
- * @return string
- */
-function wfMsg( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       $args = func_get_args();
-       array_shift( $args );
-       return wfMsgReal( $key, $args );
-}
-
-/**
- * Same as above except doesn't transform the message
- *
- * @deprecated since 1.18
- *
- * @param string $key
- * @return string
- */
-function wfMsgNoTrans( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       $args = func_get_args();
-       array_shift( $args );
-       return wfMsgReal( $key, $args, true, false, false );
-}
-
-/**
- * Get a message from anywhere, for the current global language
- * set with $wgLanguageCode.
- *
- * Use this if the message should NOT change dependent on the
- * language set in the user's preferences. This is the case for
- * most text written into logs, as well as link targets (such as
- * the name of the copyright policy page). Link titles, on the
- * other hand, should be shown in the UI language.
- *
- * Note that MediaWiki allows users to change the user interface
- * language in their preferences, but a single installation
- * typically only contains content in one language.
- *
- * Be wary of this distinction: If you use wfMsg() where you should
- * use wfMsgForContent(), a user of the software may have to
- * customize potentially hundreds of messages in
- * order to, e.g., fix a link in every possible language.
- *
- * @deprecated since 1.18
- *
- * @param string $key Lookup key for the message, usually
- *     defined in languages/Language.php
- * @return string
- */
-function wfMsgForContent( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       global $wgForceUIMsgAsContentMsg;
-       $args = func_get_args();
-       array_shift( $args );
-       $forcontent = true;
-       if ( is_array( $wgForceUIMsgAsContentMsg )
-               && in_array( $key, $wgForceUIMsgAsContentMsg )
-       ) {
-               $forcontent = false;
-       }
-       return wfMsgReal( $key, $args, true, $forcontent );
-}
-
-/**
- * Same as above except doesn't transform the message
- *
- * @deprecated since 1.18
- *
- * @param string $key
- * @return string
- */
-function wfMsgForContentNoTrans( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       global $wgForceUIMsgAsContentMsg;
-       $args = func_get_args();
-       array_shift( $args );
-       $forcontent = true;
-       if ( is_array( $wgForceUIMsgAsContentMsg )
-               && in_array( $key, $wgForceUIMsgAsContentMsg )
-       ) {
-               $forcontent = false;
-       }
-       return wfMsgReal( $key, $args, true, $forcontent, false );
-}
-
-/**
- * Really get a message
- *
- * @deprecated since 1.18
- *
- * @param string $key Key to get.
- * @param array $args
- * @param bool $useDB
- * @param string|bool $forContent Language code, or false for user lang, true for content lang.
- * @param bool $transform Whether or not to transform the message.
- * @return string The requested message.
- */
-function wfMsgReal( $key, $args, $useDB = true, $forContent = false, $transform = true ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       $message = wfMsgGetKey( $key, $useDB, $forContent, $transform );
-       $message = wfMsgReplaceArgs( $message, $args );
-       return $message;
-}
-
-/**
- * Fetch a message string value, but don't replace any keys yet.
- *
- * @deprecated since 1.18
- *
- * @param string $key
- * @param bool $useDB
- * @param string|bool $langCode Code of the language to get the message for, or
- *   behaves as a content language switch if it is a boolean.
- * @param bool $transform Whether to parse magic words, etc.
- * @return string
- */
-function wfMsgGetKey( $key, $useDB = true, $langCode = false, $transform = true ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       Hooks::run( 'NormalizeMessageKey', array( &$key, &$useDB, &$langCode, &$transform ) );
-
-       $cache = MessageCache::singleton();
-       $message = $cache->get( $key, $useDB, $langCode );
-       if ( $message === false ) {
-               $message = '&lt;' . htmlspecialchars( $key ) . '&gt;';
-       } elseif ( $transform ) {
-               $message = $cache->transform( $message );
-       }
-       return $message;
-}
-
 /**
  * Replace message parameter keys on the given formatted output.
  *
@@ -1643,159 +1489,6 @@ function wfMsgReplaceArgs( $message, $args ) {
        return $message;
 }
 
-/**
- * Return an HTML-escaped version of a message.
- * Parameter replacements, if any, are done *after* the HTML-escaping,
- * so parameters may contain HTML (eg links or form controls). Be sure
- * to pre-escape them if you really do want plaintext, or just wrap
- * the whole thing in htmlspecialchars().
- *
- * @deprecated since 1.18
- *
- * @param string $key
- * @param string $args,... Parameters
- * @return string
- */
-function wfMsgHtml( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       $args = func_get_args();
-       array_shift( $args );
-       return wfMsgReplaceArgs( htmlspecialchars( wfMsgGetKey( $key ) ), $args );
-}
-
-/**
- * Return an HTML version of message
- * Parameter replacements, if any, are done *after* parsing the wiki-text message,
- * so parameters may contain HTML (eg links or form controls). Be sure
- * to pre-escape them if you really do want plaintext, or just wrap
- * the whole thing in htmlspecialchars().
- *
- * @deprecated since 1.18
- *
- * @param string $key
- * @param string $args,... Parameters
- * @return string
- */
-function wfMsgWikiHtml( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       $args = func_get_args();
-       array_shift( $args );
-       return wfMsgReplaceArgs(
-               MessageCache::singleton()->parse( wfMsgGetKey( $key ), null,
-               /* can't be set to false */ true, /* interface */ true )->getText(),
-               $args );
-}
-
-/**
- * Returns message in the requested format
- *
- * @deprecated since 1.18
- *
- * @param string $key Key of the message
- * @param array $options Processing rules.
- *   Can take the following options:
- *     parse: parses wikitext to HTML
- *     parseinline: parses wikitext to HTML and removes the surrounding
- *       p's added by parser or tidy
- *     escape: filters message through htmlspecialchars
- *     escapenoentities: same, but allows entity references like &#160; through
- *     replaceafter: parameters are substituted after parsing or escaping
- *     parsemag: transform the message using magic phrases
- *     content: fetch message for content language instead of interface
- *   Also can accept a single associative argument, of the form 'language' => 'xx':
- *     language: Language object or language code to fetch message for
- *       (overridden by content).
- * Behavior for conflicting options (e.g., parse+parseinline) is undefined.
- *
- * @return string
- */
-function wfMsgExt( $key, $options ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       $args = func_get_args();
-       array_shift( $args );
-       array_shift( $args );
-       $options = (array)$options;
-       $validOptions = array( 'parse', 'parseinline', 'escape', 'escapenoentities', 'replaceafter',
-               'parsemag', 'content' );
-
-       foreach ( $options as $arrayKey => $option ) {
-               if ( !preg_match( '/^[0-9]+|language$/', $arrayKey ) ) {
-                       // An unknown index, neither numeric nor "language"
-                       wfWarn( "wfMsgExt called with incorrect parameter key $arrayKey", 1, E_USER_WARNING );
-               } elseif ( preg_match( '/^[0-9]+$/', $arrayKey ) && !in_array( $option, $validOptions ) ) {
-                       // A numeric index with unknown value
-                       wfWarn( "wfMsgExt called with incorrect parameter $option", 1, E_USER_WARNING );
-               }
-       }
-
-       if ( in_array( 'content', $options, true ) ) {
-               $forContent = true;
-               $langCode = true;
-               $langCodeObj = null;
-       } elseif ( array_key_exists( 'language', $options ) ) {
-               $forContent = false;
-               $langCode = wfGetLangObj( $options['language'] );
-               $langCodeObj = $langCode;
-       } else {
-               $forContent = false;
-               $langCode = false;
-               $langCodeObj = null;
-       }
-
-       $string = wfMsgGetKey( $key, /*DB*/true, $langCode, /*Transform*/false );
-
-       if ( !in_array( 'replaceafter', $options, true ) ) {
-               $string = wfMsgReplaceArgs( $string, $args );
-       }
-
-       $messageCache = MessageCache::singleton();
-       $parseInline = in_array( 'parseinline', $options, true );
-       if ( in_array( 'parse', $options, true ) || $parseInline ) {
-               $string = $messageCache->parse( $string, null, true, !$forContent, $langCodeObj );
-               if ( $string instanceof ParserOutput ) {
-                       $string = $string->getText();
-               }
-
-               if ( $parseInline ) {
-                       $string = Parser::stripOuterParagraph( $string );
-               }
-       } elseif ( in_array( 'parsemag', $options, true ) ) {
-               $string = $messageCache->transform( $string,
-                               !$forContent, $langCodeObj );
-       }
-
-       if ( in_array( 'escape', $options, true ) ) {
-               $string = htmlspecialchars( $string );
-       } elseif ( in_array( 'escapenoentities', $options, true ) ) {
-               $string = Sanitizer::escapeHtmlAllowEntities( $string );
-       }
-
-       if ( in_array( 'replaceafter', $options, true ) ) {
-               $string = wfMsgReplaceArgs( $string, $args );
-       }
-
-       return $string;
-}
-
-/**
- * Since wfMsg() and co suck, they don't return false if the message key they
- * looked up didn't exist but instead the key wrapped in <>'s, this function checks for the
- * nonexistence of messages by checking the MessageCache::get() result directly.
- *
- * @deprecated since 1.18. Use Message::isDisabled().
- *
- * @param string $key The message key looked up
- * @return bool True if the message *doesn't* exist.
- */
-function wfEmptyMsg( $key ) {
-       wfDeprecated( __METHOD__, '1.21' );
-
-       return MessageCache::singleton()->get( $key, /*useDB*/true, /*content*/false ) === false;
-}
-
 /**
  * Fetch server name for use in error reporting etc.
  * Use real server name if available, so we know which machine
@@ -2014,21 +1707,6 @@ function wfClientAcceptsGzip( $force = false ) {
        return $result;
 }
 
-/**
- * Obtain the offset and limit values from the request string;
- * used in special pages
- *
- * @param int $deflimit Default limit if none supplied
- * @param string $optionname Name of a user preference to check against
- * @return array
- * @deprecated since 1.24, just call WebRequest::getLimitOffset() directly
- */
-function wfCheckLimits( $deflimit = 50, $optionname = 'rclimit' ) {
-       global $wgRequest;
-       wfDeprecated( __METHOD__, '1.24' );
-       return $wgRequest->getLimitOffset( $deflimit, $optionname );
-}
-
 /**
  * Escapes the given text so that it may be output using addWikiText()
  * without any linking, formatting, etc. making its way through. This
@@ -2484,8 +2162,8 @@ function wfIsHHVM() {
 /**
  * Tries to get the system directory for temporary files. First
  * $wgTmpDirectory is checked, and then the TMPDIR, TMP, and TEMP
- * environment variables are then checked in sequence, and if none are
- * set try sys_get_temp_dir().
+ * environment variables are then checked in sequence, then
+ * sys_get_temp_dir(), then upload_tmp_dir from php.ini.
  *
  * NOTE: When possible, use instead the tmpfile() function to create
  * temporary files to avoid race conditions on file creation, etc.
@@ -2500,13 +2178,16 @@ function wfTempDir() {
        }
 
        $tmpDir = array_map( "getenv", array( 'TMPDIR', 'TMP', 'TEMP' ) );
+       $tmpDir[] = sys_get_temp_dir();
+       $tmpDir[] = ini_get( 'upload_tmp_dir' );
 
        foreach ( $tmpDir as $tmp ) {
                if ( $tmp && file_exists( $tmp ) && is_dir( $tmp ) && is_writable( $tmp ) ) {
                        return $tmp;
                }
        }
-       return sys_get_temp_dir();
+       throw new MWException( 'No writable temporary directory could be found. ' .
+               'Please set $wgTmpDirectory to a writable directory.' );
 }
 
 /**
@@ -3330,9 +3011,12 @@ function wfBaseConvert( $input, $sourceBase, $destBase, $pad = 1,
 /**
  * Check if there is sufficient entropy in php's built-in session generation
  *
+ * @deprecated since 1.27, PHP's session generation isn't used with
+ *  MediaWiki\\Session\\SessionManager
  * @return bool True = there is sufficient entropy
  */
 function wfCheckEntropy() {
+       wfDeprecated( __FUNCTION__, '1.27' );
        return (
                        ( wfIsWindows() && version_compare( PHP_VERSION, '5.3.3', '>=' ) )
                        || ini_get( 'session.entropy_file' )
@@ -3341,83 +3025,65 @@ function wfCheckEntropy() {
 }
 
 /**
- * Override session_id before session startup if php's built-in
- * session generation code is not secure.
+ * @deprecated since 1.27, PHP's session generation isn't used with
+ *  MediaWiki\\Session\\SessionManager
  */
 function wfFixSessionID() {
-       // If the cookie or session id is already set we already have a session and should abort
-       if ( isset( $_COOKIE[session_name()] ) || session_id() ) {
-               return;
-       }
-
-       // PHP's built-in session entropy is enabled if:
-       // - entropy_file is set or you're on Windows with php 5.3.3+
-       // - AND entropy_length is > 0
-       // We treat it as disabled if it doesn't have an entropy length of at least 32
-       $entropyEnabled = wfCheckEntropy();
-
-       // If built-in entropy is not enabled or not sufficient override PHP's
-       // built in session id generation code
-       if ( !$entropyEnabled ) {
-               wfDebug( __METHOD__ . ": PHP's built in entropy is disabled or not sufficient, " .
-                       "overriding session id generation using our cryptrand source.\n" );
-               session_id( MWCryptRand::generateHex( 32 ) );
-       }
+       wfDeprecated( __FUNCTION__, '1.27' );
 }
 
 /**
- * Reset the session_id
+ * Reset the session id
  *
+ * @deprecated since 1.27, use MediaWiki\\Session\\SessionManager instead
  * @since 1.22
  */
 function wfResetSessionID() {
-       global $wgCookieSecure;
-       $oldSessionId = session_id();
-       $cookieParams = session_get_cookie_params();
-       if ( wfCheckEntropy() && $wgCookieSecure == $cookieParams['secure'] ) {
-               session_regenerate_id( false );
-       } else {
-               $tmp = $_SESSION;
-               session_destroy();
-               wfSetupSession( MWCryptRand::generateHex( 32 ) );
-               $_SESSION = $tmp;
+       wfDeprecated( __FUNCTION__, '1.27' );
+       $session = SessionManager::getGlobalSession();
+       $delay = $session->delaySave();
+
+       $session->resetId();
+
+       // Make sure a session is started, since that's what the old
+       // wfResetSessionID() did.
+       if ( session_id() !== $session->getId() ) {
+               wfSetupSession( $session->getId() );
        }
-       $newSessionId = session_id();
+
+       ScopedCallback::consume( $delay );
 }
 
 /**
  * Initialise php session
  *
- * @param bool $sessionId
+ * @deprecated since 1.27, use MediaWiki\\Session\\SessionManager instead.
+ *  Generally, "using" SessionManager will be calling ->getSessionById() or
+ *  ::getGlobalSession() (depending on whether you were passing $sessionId
+ *  here), then calling $session->persist().
+ * @param bool|string $sessionId
  */
 function wfSetupSession( $sessionId = false ) {
-       global $wgSessionsInObjectCache, $wgSessionHandler;
-       global $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly;
+       wfDeprecated( __FUNCTION__, '1.27' );
 
-       if ( $wgSessionsInObjectCache ) {
-               ObjectCacheSessionHandler::install();
-       } elseif ( $wgSessionHandler && $wgSessionHandler != ini_get( 'session.save_handler' ) ) {
-               # Only set this if $wgSessionHandler isn't null and session.save_handler
-               # hasn't already been set to the desired value (that causes errors)
-               ini_set( 'session.save_handler', $wgSessionHandler );
+       // If they're calling this, they probably want our session management even
+       // if NO_SESSION was set for Setup.php.
+       if ( !MediaWiki\Session\PHPSessionHandler::isInstalled() ) {
+               MediaWiki\Session\PHPSessionHandler::install( SessionManager::singleton() );
        }
 
-       session_set_cookie_params(
-               0, $wgCookiePath, $wgCookieDomain, $wgCookieSecure, $wgCookieHttpOnly );
-       session_cache_limiter( 'private, must-revalidate' );
        if ( $sessionId ) {
                session_id( $sessionId );
-       } else {
-               wfFixSessionID();
        }
 
-       MediaWiki\suppressWarnings();
-       session_start();
-       MediaWiki\restoreWarnings();
+       $session = SessionManager::getGlobalSession();
+       $session->persist();
 
-       if ( $wgSessionsInObjectCache ) {
-               ObjectCacheSessionHandler::renewCurrentSession();
+       if ( session_id() !== $session->getId() ) {
+               session_id( $session->getId() );
        }
+
+       MediaWiki\quietCall( 'session_start' );
 }
 
 /**
@@ -3683,55 +3349,35 @@ function wfGetNull() {
  * @param string|bool $cluster Cluster name accepted by LBFactory. Default: false.
  * @param int|null $timeout Max wait time. Default: 1 day (cli), ~10 seconds (web)
  * @return bool Success (able to connect and no timeouts reached)
+ * @deprecated since 1.27 Use LBFactory::waitForReplication
  */
 function wfWaitForSlaves(
        $ifWritesSince = null, $wiki = false, $cluster = false, $timeout = null
 ) {
-       // B/C: first argument used to be "max seconds of lag"; ignore such values
-       $ifWritesSince = ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null;
-
        if ( $timeout === null ) {
                $timeout = ( PHP_SAPI === 'cli' ) ? 86400 : 10;
        }
 
-       // Figure out which clusters need to be checked
-       /** @var LoadBalancer[] $lbs */
-       $lbs = array();
        if ( $cluster === '*' ) {
-               wfGetLBFactory()->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
-                       $lbs[] = $lb;
-               } );
-       } elseif ( $cluster !== false ) {
-               $lbs[] = wfGetLBFactory()->getExternalLB( $cluster );
-       } else {
-               $lbs[] = wfGetLB( $wiki );
+               $cluster = false;
+               $wiki = false;
+       } elseif ( $wiki === false ) {
+               $wiki = wfWikiID();
        }
 
-       // Get all the master positions of applicable DBs right now.
-       // This can be faster since waiting on one cluster reduces the
-       // time needed to wait on the next clusters.
-       $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
-                       // Prevents permission error when getting master position
-                       continue;
-               } elseif ( $ifWritesSince && $lb->lastMasterChangeTimestamp() < $ifWritesSince ) {
-                       continue; // no writes since the last wait
-               }
-               $masterPositions[$i] = $lb->getMasterPos();
-       }
-
-       $ok = true;
-       foreach ( $lbs as $i => $lb ) {
-               if ( $masterPositions[$i] ) {
-                       // The DBMS may not support getMasterPos() or the whole
-                       // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
-                       $ok = $lb->waitForAll( $masterPositions[$i], $timeout ) && $ok;
-               }
+       try {
+               wfGetLBFactory()->waitForReplication( array(
+                       'wiki' => $wiki,
+                       'cluster' => $cluster,
+                       'timeout' => $timeout,
+                       // B/C: first argument used to be "max seconds of lag"; ignore such values
+                       'ifWritesSince' => ( $ifWritesSince > 1e9 ) ? $ifWritesSince : null
+               ) );
+       } catch ( DBReplicationWaitError $e ) {
+               return false;
        }
 
-       return $ok;
+       return true;
 }
 
 /**
diff --git a/includes/Import.php b/includes/Import.php
deleted file mode 100644 (file)
index 0999931..0000000
+++ /dev/null
@@ -1,2030 +0,0 @@
-<?php
-/**
- * MediaWiki page data importer.
- *
- * Copyright © 2003,2005 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 SpecialPage
- */
-
-/**
- * XML file reader for the page data importer
- *
- * implements Special:Import
- * @ingroup SpecialPage
- */
-class WikiImporter {
-       private $reader = null;
-       private $foreignNamespaces = null;
-       private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback;
-       private $mSiteInfoCallback, $mPageOutCallback;
-       private $mNoticeCallback, $mDebug;
-       private $mImportUploads, $mImageBasePath;
-       private $mNoUpdates = false;
-       /** @var Config */
-       private $config;
-       /** @var ImportTitleFactory */
-       private $importTitleFactory;
-       /** @var array */
-       private $countableCache = array();
-
-       /**
-        * Creates an ImportXMLReader drawing from the source provided
-        * @param ImportSource $source
-        * @param Config $config
-        * @throws Exception
-        */
-       function __construct( ImportSource $source, Config $config = null ) {
-               if ( !class_exists( 'XMLReader' ) ) {
-                       throw new Exception( 'Import requires PHP to have been compiled with libxml support' );
-               }
-
-               $this->reader = new XMLReader();
-               if ( !$config ) {
-                       wfDeprecated( __METHOD__ . ' without a Config instance', '1.25' );
-                       $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
-               }
-               $this->config = $config;
-
-               if ( !in_array( 'uploadsource', stream_get_wrappers() ) ) {
-                       stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
-               }
-               $id = UploadSourceAdapter::registerSource( $source );
-
-               // Enable the entity loader, as it is needed for loading external URLs via
-               // XMLReader::open (T86036)
-               $oldDisable = libxml_disable_entity_loader( false );
-               if ( defined( 'LIBXML_PARSEHUGE' ) ) {
-                       $status = $this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
-               } else {
-                       $status = $this->reader->open( "uploadsource://$id" );
-               }
-               if ( !$status ) {
-                       $error = libxml_get_last_error();
-                       libxml_disable_entity_loader( $oldDisable );
-                       throw new MWException( 'Encountered an internal error while initializing WikiImporter object: ' .
-                               $error->message );
-               }
-               libxml_disable_entity_loader( $oldDisable );
-
-               // Default callbacks
-               $this->setPageCallback( array( $this, 'beforeImportPage' ) );
-               $this->setRevisionCallback( array( $this, "importRevision" ) );
-               $this->setUploadCallback( array( $this, 'importUpload' ) );
-               $this->setLogItemCallback( array( $this, 'importLogItem' ) );
-               $this->setPageOutCallback( array( $this, 'finishImportPage' ) );
-
-               $this->importTitleFactory = new NaiveImportTitleFactory();
-       }
-
-       /**
-        * @return null|XMLReader
-        */
-       public function getReader() {
-               return $this->reader;
-       }
-
-       public function throwXmlError( $err ) {
-               $this->debug( "FAILURE: $err" );
-               wfDebug( "WikiImporter XML error: $err\n" );
-       }
-
-       public function debug( $data ) {
-               if ( $this->mDebug ) {
-                       wfDebug( "IMPORT: $data\n" );
-               }
-       }
-
-       public function warn( $data ) {
-               wfDebug( "IMPORT: $data\n" );
-       }
-
-       public function notice( $msg /*, $param, ...*/ ) {
-               $params = func_get_args();
-               array_shift( $params );
-
-               if ( is_callable( $this->mNoticeCallback ) ) {
-                       call_user_func( $this->mNoticeCallback, $msg, $params );
-               } else { # No ImportReporter -> CLI
-                       echo wfMessage( $msg, $params )->text() . "\n";
-               }
-       }
-
-       /**
-        * Set debug mode...
-        * @param bool $debug
-        */
-       function setDebug( $debug ) {
-               $this->mDebug = $debug;
-       }
-
-       /**
-        * Set 'no updates' mode. In this mode, the link tables will not be updated by the importer
-        * @param bool $noupdates
-        */
-       function setNoUpdates( $noupdates ) {
-               $this->mNoUpdates = $noupdates;
-       }
-
-       /**
-        * Set a callback that displays notice messages
-        *
-        * @param callable $callback
-        * @return callable
-        */
-       public function setNoticeCallback( $callback ) {
-               return wfSetVar( $this->mNoticeCallback, $callback );
-       }
-
-       /**
-        * Sets the action to perform as each new page in the stream is reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setPageCallback( $callback ) {
-               $previous = $this->mPageCallback;
-               $this->mPageCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each page in the stream is completed.
-        * Callback accepts the page title (as a Title object), a second object
-        * with the original title form (in case it's been overridden into a
-        * local namespace), and a count of revisions.
-        *
-        * @param callable $callback
-        * @return callable
-        */
-       public function setPageOutCallback( $callback ) {
-               $previous = $this->mPageOutCallback;
-               $this->mPageOutCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each page revision is reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setRevisionCallback( $callback ) {
-               $previous = $this->mRevisionCallback;
-               $this->mRevisionCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each file upload version is reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setUploadCallback( $callback ) {
-               $previous = $this->mUploadCallback;
-               $this->mUploadCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform as each log item reached.
-        * @param callable $callback
-        * @return callable
-        */
-       public function setLogItemCallback( $callback ) {
-               $previous = $this->mLogItemCallback;
-               $this->mLogItemCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the action to perform when site info is encountered
-        * @param callable $callback
-        * @return callable
-        */
-       public function setSiteInfoCallback( $callback ) {
-               $previous = $this->mSiteInfoCallback;
-               $this->mSiteInfoCallback = $callback;
-               return $previous;
-       }
-
-       /**
-        * Sets the factory object to use to convert ForeignTitle objects into local
-        * Title objects
-        * @param ImportTitleFactory $factory
-        */
-       public function setImportTitleFactory( $factory ) {
-               $this->importTitleFactory = $factory;
-       }
-
-       /**
-        * Set a target namespace to override the defaults
-        * @param null|int $namespace
-        * @return bool
-        */
-       public function setTargetNamespace( $namespace ) {
-               if ( is_null( $namespace ) ) {
-                       // Don't override namespaces
-                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
-                       return true;
-               } elseif (
-                       $namespace >= 0 &&
-                       MWNamespace::exists( intval( $namespace ) )
-               ) {
-                       $namespace = intval( $namespace );
-                       $this->setImportTitleFactory( new NamespaceImportTitleFactory( $namespace ) );
-                       return true;
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Set a target root page under which all pages are imported
-        * @param null|string $rootpage
-        * @return Status
-        */
-       public function setTargetRootPage( $rootpage ) {
-               $status = Status::newGood();
-               if ( is_null( $rootpage ) ) {
-                       // No rootpage
-                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
-               } elseif ( $rootpage !== '' ) {
-                       $rootpage = rtrim( $rootpage, '/' ); // avoid double slashes
-                       $title = Title::newFromText( $rootpage );
-
-                       if ( !$title || $title->isExternal() ) {
-                               $status->fatal( 'import-rootpage-invalid' );
-                       } else {
-                               if ( !MWNamespace::hasSubpages( $title->getNamespace() ) ) {
-                                       global $wgContLang;
-
-                                       $displayNSText = $title->getNamespace() == NS_MAIN
-                                               ? wfMessage( 'blanknamespace' )->text()
-                                               : $wgContLang->getNsText( $title->getNamespace() );
-                                       $status->fatal( 'import-rootpage-nosubpage', $displayNSText );
-                               } else {
-                                       // set namespace to 'all', so the namespace check in processTitle() can pass
-                                       $this->setTargetNamespace( null );
-                                       $this->setImportTitleFactory( new SubpageImportTitleFactory( $title ) );
-                               }
-                       }
-               }
-               return $status;
-       }
-
-       /**
-        * @param string $dir
-        */
-       public function setImageBasePath( $dir ) {
-               $this->mImageBasePath = $dir;
-       }
-
-       /**
-        * @param bool $import
-        */
-       public function setImportUploads( $import ) {
-               $this->mImportUploads = $import;
-       }
-
-       /**
-        * Default per-page callback. Sets up some things related to site statistics
-        * @param array $titleAndForeignTitle Two-element array, with Title object at
-        * index 0 and ForeignTitle object at index 1
-        * @return bool
-        */
-       public function beforeImportPage( $titleAndForeignTitle ) {
-               $title = $titleAndForeignTitle[0];
-               $page = WikiPage::factory( $title );
-               $this->countableCache['title_' . $title->getPrefixedText()] = $page->isCountable();
-               return true;
-       }
-
-       /**
-        * Default per-revision callback, performs the import.
-        * @param WikiRevision $revision
-        * @return bool
-        */
-       public function importRevision( $revision ) {
-               if ( !$revision->getContentHandler()->canBeUsedOn( $revision->getTitle() ) ) {
-                       $this->notice( 'import-error-bad-location',
-                               $revision->getTitle()->getPrefixedText(),
-                               $revision->getID(),
-                               $revision->getModel(),
-                               $revision->getFormat() );
-
-                       return false;
-               }
-
-               try {
-                       $dbw = wfGetDB( DB_MASTER );
-                       return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
-               } catch ( MWContentSerializationException $ex ) {
-                       $this->notice( 'import-error-unserialize',
-                               $revision->getTitle()->getPrefixedText(),
-                               $revision->getID(),
-                               $revision->getModel(),
-                               $revision->getFormat() );
-               }
-
-               return false;
-       }
-
-       /**
-        * Default per-revision callback, performs the import.
-        * @param WikiRevision $revision
-        * @return bool
-        */
-       public function importLogItem( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( array( $revision, 'importLogItem' ) );
-       }
-
-       /**
-        * Dummy for now...
-        * @param WikiRevision $revision
-        * @return bool
-        */
-       public function importUpload( $revision ) {
-               $dbw = wfGetDB( DB_MASTER );
-               return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
-       }
-
-       /**
-        * Mostly for hook use
-        * @param Title $title
-        * @param ForeignTitle $foreignTitle
-        * @param int $revCount
-        * @param int $sRevCount
-        * @param array $pageInfo
-        * @return bool
-        */
-       public function finishImportPage( $title, $foreignTitle, $revCount,
-                       $sRevCount, $pageInfo ) {
-
-               // 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
-               // and revision count, and we implement our own custom logic for the
-               // article (content page) count.
-               $page = WikiPage::factory( $title );
-               $page->loadPageData( 'fromdbmaster' );
-               $content = $page->getContent();
-               if ( $content === null ) {
-                       wfDebug( __METHOD__ . ': Skipping article count adjustment for ' . $title .
-                               ' because WikiPage::getContent() returned null' );
-               } else {
-                       $editInfo = $page->prepareContentForEdit( $content );
-                       $countKey = 'title_' . $title->getPrefixedText();
-                       $countable = $page->isCountable( $editInfo );
-                       if ( array_key_exists( $countKey, $this->countableCache ) &&
-                               $countable != $this->countableCache[$countKey] ) {
-                               DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array(
-                                       'articles' => ( (int)$countable - (int)$this->countableCache[$countKey] )
-                               ) ) );
-                       }
-               }
-
-               $args = func_get_args();
-               return Hooks::run( 'AfterImportPage', $args );
-       }
-
-       /**
-        * Alternate per-revision callback, for debugging.
-        * @param WikiRevision $revision
-        */
-       public function debugRevisionHandler( &$revision ) {
-               $this->debug( "Got revision:" );
-               if ( is_object( $revision->title ) ) {
-                       $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
-               } else {
-                       $this->debug( "-- Title: <invalid>" );
-               }
-               $this->debug( "-- User: " . $revision->user_text );
-               $this->debug( "-- Timestamp: " . $revision->timestamp );
-               $this->debug( "-- Comment: " . $revision->comment );
-               $this->debug( "-- Text: " . $revision->text );
-       }
-
-       /**
-        * Notify the callback function of site info
-        * @param array $siteInfo
-        * @return bool|mixed
-        */
-       private function siteInfoCallback( $siteInfo ) {
-               if ( isset( $this->mSiteInfoCallback ) ) {
-                       return call_user_func_array( $this->mSiteInfoCallback,
-                                       array( $siteInfo, $this ) );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Notify the callback function when a new "<page>" is reached.
-        * @param Title $title
-        */
-       function pageCallback( $title ) {
-               if ( isset( $this->mPageCallback ) ) {
-                       call_user_func( $this->mPageCallback, $title );
-               }
-       }
-
-       /**
-        * Notify the callback function when a "</page>" is closed.
-        * @param Title $title
-        * @param ForeignTitle $foreignTitle
-        * @param int $revCount
-        * @param int $sucCount Number of revisions for which callback returned true
-        * @param array $pageInfo Associative array of page information
-        */
-       private function pageOutCallback( $title, $foreignTitle, $revCount,
-                       $sucCount, $pageInfo ) {
-               if ( isset( $this->mPageOutCallback ) ) {
-                       $args = func_get_args();
-                       call_user_func_array( $this->mPageOutCallback, $args );
-               }
-       }
-
-       /**
-        * Notify the callback function of a revision
-        * @param WikiRevision $revision
-        * @return bool|mixed
-        */
-       private function revisionCallback( $revision ) {
-               if ( isset( $this->mRevisionCallback ) ) {
-                       return call_user_func_array( $this->mRevisionCallback,
-                                       array( $revision, $this ) );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Notify the callback function of a new log item
-        * @param WikiRevision $revision
-        * @return bool|mixed
-        */
-       private function logItemCallback( $revision ) {
-               if ( isset( $this->mLogItemCallback ) ) {
-                       return call_user_func_array( $this->mLogItemCallback,
-                                       array( $revision, $this ) );
-               } else {
-                       return false;
-               }
-       }
-
-       /**
-        * Retrieves the contents of the named attribute of the current element.
-        * @param string $attr The name of the attribute
-        * @return string The value of the attribute or an empty string if it is not set in the current
-        * element.
-        */
-       public function nodeAttribute( $attr ) {
-               return $this->reader->getAttribute( $attr );
-       }
-
-       /**
-        * Shouldn't something like this be built-in to XMLReader?
-        * Fetches text contents of the current element, assuming
-        * no sub-elements or such scary things.
-        * @return string
-        * @access private
-        */
-       public function nodeContents() {
-               if ( $this->reader->isEmptyElement ) {
-                       return "";
-               }
-               $buffer = "";
-               while ( $this->reader->read() ) {
-                       switch ( $this->reader->nodeType ) {
-                       case XMLReader::TEXT:
-                       case XMLReader::CDATA:
-                       case XMLReader::SIGNIFICANT_WHITESPACE:
-                               $buffer .= $this->reader->value;
-                               break;
-                       case XMLReader::END_ELEMENT:
-                               return $buffer;
-                       }
-               }
-
-               $this->reader->close();
-               return '';
-       }
-
-       /**
-        * Primary entry point
-        * @throws MWException
-        * @return bool
-        */
-       public function doImport() {
-               // Calls to reader->read need to be wrapped in calls to
-               // libxml_disable_entity_loader() to avoid local file
-               // inclusion attacks (bug 46932).
-               $oldDisable = libxml_disable_entity_loader( true );
-               $this->reader->read();
-
-               if ( $this->reader->localName != 'mediawiki' ) {
-                       libxml_disable_entity_loader( $oldDisable );
-                       throw new MWException( "Expected <mediawiki> tag, got " .
-                               $this->reader->localName );
-               }
-               $this->debug( "<mediawiki> tag is correct." );
-
-               $this->debug( "Starting primary dump processing loop." );
-
-               $keepReading = $this->reader->read();
-               $skip = false;
-               $rethrow = null;
-               try {
-                       while ( $keepReading ) {
-                               $tag = $this->reader->localName;
-                               $type = $this->reader->nodeType;
-
-                               if ( !Hooks::run( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
-                                       // Do nothing
-                               } elseif ( $tag == 'mediawiki' && $type == XMLReader::END_ELEMENT ) {
-                                       break;
-                               } elseif ( $tag == 'siteinfo' ) {
-                                       $this->handleSiteInfo();
-                               } elseif ( $tag == 'page' ) {
-                                       $this->handlePage();
-                               } elseif ( $tag == 'logitem' ) {
-                                       $this->handleLogItem();
-                               } elseif ( $tag != '#text' ) {
-                                       $this->warn( "Unhandled top-level XML tag $tag" );
-
-                                       $skip = true;
-                               }
-
-                               if ( $skip ) {
-                                       $keepReading = $this->reader->next();
-                                       $skip = false;
-                                       $this->debug( "Skip" );
-                               } else {
-                                       $keepReading = $this->reader->read();
-                               }
-                       }
-               } catch ( Exception $ex ) {
-                       $rethrow = $ex;
-               }
-
-               // finally
-               libxml_disable_entity_loader( $oldDisable );
-               $this->reader->close();
-
-               if ( $rethrow ) {
-                       throw $rethrow;
-               }
-
-               return true;
-       }
-
-       private function handleSiteInfo() {
-               $this->debug( "Enter site info handler." );
-               $siteInfo = array();
-
-               // Fields that can just be stuffed in the siteInfo object
-               $normalFields = array( 'sitename', 'base', 'generator', 'case' );
-
-               while ( $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
-                                       $this->reader->localName == 'siteinfo' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( $tag == 'namespace' ) {
-                               $this->foreignNamespaces[$this->nodeAttribute( 'key' )] =
-                                       $this->nodeContents();
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $siteInfo[$tag] = $this->nodeContents();
-                       }
-               }
-
-               $siteInfo['_namespaces'] = $this->foreignNamespaces;
-               $this->siteInfoCallback( $siteInfo );
-       }
-
-       private function handleLogItem() {
-               $this->debug( "Enter log item handler." );
-               $logInfo = array();
-
-               // Fields that can just be stuffed in the pageInfo object
-               $normalFields = array( 'id', 'comment', 'type', 'action', 'timestamp',
-                                       'logtitle', 'params' );
-
-               while ( $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'logitem' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( !Hooks::run( 'ImportHandleLogItemXMLTag', array(
-                               $this, $logInfo
-                       ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $logInfo[$tag] = $this->nodeContents();
-                       } elseif ( $tag == 'contributor' ) {
-                               $logInfo['contributor'] = $this->handleContributor();
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled log-item XML tag $tag" );
-                       }
-               }
-
-               $this->processLogItem( $logInfo );
-       }
-
-       /**
-        * @param array $logInfo
-        * @return bool|mixed
-        */
-       private function processLogItem( $logInfo ) {
-
-               $revision = new WikiRevision( $this->config );
-
-               if ( isset( $logInfo['id'] ) ) {
-                       $revision->setID( $logInfo['id'] );
-               }
-               $revision->setType( $logInfo['type'] );
-               $revision->setAction( $logInfo['action'] );
-               if ( isset( $logInfo['timestamp'] ) ) {
-                       $revision->setTimestamp( $logInfo['timestamp'] );
-               }
-               if ( isset( $logInfo['params'] ) ) {
-                       $revision->setParams( $logInfo['params'] );
-               }
-               if ( isset( $logInfo['logtitle'] ) ) {
-                       // @todo Using Title for non-local titles is a recipe for disaster.
-                       // We should use ForeignTitle here instead.
-                       $revision->setTitle( Title::newFromText( $logInfo['logtitle'] ) );
-               }
-
-               $revision->setNoUpdates( $this->mNoUpdates );
-
-               if ( isset( $logInfo['comment'] ) ) {
-                       $revision->setComment( $logInfo['comment'] );
-               }
-
-               if ( isset( $logInfo['contributor']['ip'] ) ) {
-                       $revision->setUserIP( $logInfo['contributor']['ip'] );
-               }
-
-               if ( !isset( $logInfo['contributor']['username'] ) ) {
-                       $revision->setUsername( 'Unknown user' );
-               } else {
-                       $revision->setUserName( $logInfo['contributor']['username'] );
-               }
-
-               return $this->logItemCallback( $revision );
-       }
-
-       private function handlePage() {
-               // Handle page data.
-               $this->debug( "Enter page handler." );
-               $pageInfo = array( 'revisionCount' => 0, 'successfulRevisionCount' => 0 );
-
-               // Fields that can just be stuffed in the pageInfo object
-               $normalFields = array( 'title', 'ns', 'id', 'redirect', 'restrictions' );
-
-               $skip = false;
-               $badTitle = false;
-
-               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'page' ) {
-                               break;
-                       }
-
-                       $skip = false;
-
-                       $tag = $this->reader->localName;
-
-                       if ( $badTitle ) {
-                               // The title is invalid, bail out of this page
-                               $skip = true;
-                       } elseif ( !Hooks::run( 'ImportHandlePageXMLTag', array( $this,
-                                               &$pageInfo ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               // An XML snippet:
-                               // <page>
-                               //     <id>123</id>
-                               //     <title>Page</title>
-                               //     <redirect title="NewTitle"/>
-                               //     ...
-                               // Because the redirect tag is built differently, we need special handling for that case.
-                               if ( $tag == 'redirect' ) {
-                                       $pageInfo[$tag] = $this->nodeAttribute( 'title' );
-                               } else {
-                                       $pageInfo[$tag] = $this->nodeContents();
-                               }
-                       } elseif ( $tag == 'revision' || $tag == 'upload' ) {
-                               if ( !isset( $title ) ) {
-                                       $title = $this->processTitle( $pageInfo['title'],
-                                               isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
-
-                                       // $title is either an array of two titles or false.
-                                       if ( is_array( $title ) ) {
-                                               $this->pageCallback( $title );
-                                               list( $pageInfo['_title'], $foreignTitle ) = $title;
-                                       } else {
-                                               $badTitle = true;
-                                               $skip = true;
-                                       }
-                               }
-
-                               if ( $title ) {
-                                       if ( $tag == 'revision' ) {
-                                               $this->handleRevision( $pageInfo );
-                                       } else {
-                                               $this->handleUpload( $pageInfo );
-                                       }
-                               }
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled page XML tag $tag" );
-                               $skip = true;
-                       }
-               }
-
-               // @note $pageInfo is only set if a valid $title is processed above with
-               //       no error. If we have a valid $title, then pageCallback is called
-               //       above, $pageInfo['title'] is set and we do pageOutCallback here.
-               //       If $pageInfo['_title'] is not set, then $foreignTitle is also not
-               //       set since they both come from $title above.
-               if ( array_key_exists( '_title', $pageInfo ) ) {
-                       $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
-                                       $pageInfo['revisionCount'],
-                                       $pageInfo['successfulRevisionCount'],
-                                       $pageInfo );
-               }
-       }
-
-       /**
-        * @param array $pageInfo
-        */
-       private function handleRevision( &$pageInfo ) {
-               $this->debug( "Enter revision handler" );
-               $revisionInfo = array();
-
-               $normalFields = array( 'id', 'timestamp', 'comment', 'minor', 'model', 'format', 'text' );
-
-               $skip = false;
-
-               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'revision' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( !Hooks::run( 'ImportHandleRevisionXMLTag', array(
-                               $this, $pageInfo, $revisionInfo
-                       ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $revisionInfo[$tag] = $this->nodeContents();
-                       } elseif ( $tag == 'contributor' ) {
-                               $revisionInfo['contributor'] = $this->handleContributor();
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled revision XML tag $tag" );
-                               $skip = true;
-                       }
-               }
-
-               $pageInfo['revisionCount']++;
-               if ( $this->processRevision( $pageInfo, $revisionInfo ) ) {
-                       $pageInfo['successfulRevisionCount']++;
-               }
-       }
-
-       /**
-        * @param array $pageInfo
-        * @param array $revisionInfo
-        * @return bool|mixed
-        */
-       private function processRevision( $pageInfo, $revisionInfo ) {
-               $revision = new WikiRevision( $this->config );
-
-               if ( isset( $revisionInfo['id'] ) ) {
-                       $revision->setID( $revisionInfo['id'] );
-               }
-               if ( isset( $revisionInfo['model'] ) ) {
-                       $revision->setModel( $revisionInfo['model'] );
-               }
-               if ( isset( $revisionInfo['format'] ) ) {
-                       $revision->setFormat( $revisionInfo['format'] );
-               }
-               $revision->setTitle( $pageInfo['_title'] );
-
-               if ( isset( $revisionInfo['text'] ) ) {
-                       $handler = $revision->getContentHandler();
-                       $text = $handler->importTransform(
-                               $revisionInfo['text'],
-                               $revision->getFormat() );
-
-                       $revision->setText( $text );
-               }
-               if ( isset( $revisionInfo['timestamp'] ) ) {
-                       $revision->setTimestamp( $revisionInfo['timestamp'] );
-               } else {
-                       $revision->setTimestamp( wfTimestampNow() );
-               }
-
-               if ( isset( $revisionInfo['comment'] ) ) {
-                       $revision->setComment( $revisionInfo['comment'] );
-               }
-
-               if ( isset( $revisionInfo['minor'] ) ) {
-                       $revision->setMinor( true );
-               }
-               if ( isset( $revisionInfo['contributor']['ip'] ) ) {
-                       $revision->setUserIP( $revisionInfo['contributor']['ip'] );
-               } elseif ( isset( $revisionInfo['contributor']['username'] ) ) {
-                       $revision->setUserName( $revisionInfo['contributor']['username'] );
-               } else {
-                       $revision->setUserName( 'Unknown user' );
-               }
-               $revision->setNoUpdates( $this->mNoUpdates );
-
-               return $this->revisionCallback( $revision );
-       }
-
-       /**
-        * @param array $pageInfo
-        * @return mixed
-        */
-       private function handleUpload( &$pageInfo ) {
-               $this->debug( "Enter upload handler" );
-               $uploadInfo = array();
-
-               $normalFields = array( 'timestamp', 'comment', 'filename', 'text',
-                                       'src', 'size', 'sha1base36', 'archivename', 'rel' );
-
-               $skip = false;
-
-               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'upload' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( !Hooks::run( 'ImportHandleUploadXMLTag', array(
-                               $this, $pageInfo
-                       ) ) ) {
-                               // Do nothing
-                       } elseif ( in_array( $tag, $normalFields ) ) {
-                               $uploadInfo[$tag] = $this->nodeContents();
-                       } elseif ( $tag == 'contributor' ) {
-                               $uploadInfo['contributor'] = $this->handleContributor();
-                       } elseif ( $tag == 'contents' ) {
-                               $contents = $this->nodeContents();
-                               $encoding = $this->reader->getAttribute( 'encoding' );
-                               if ( $encoding === 'base64' ) {
-                                       $uploadInfo['fileSrc'] = $this->dumpTemp( base64_decode( $contents ) );
-                                       $uploadInfo['isTempSrc'] = true;
-                               }
-                       } elseif ( $tag != '#text' ) {
-                               $this->warn( "Unhandled upload XML tag $tag" );
-                               $skip = true;
-                       }
-               }
-
-               if ( $this->mImageBasePath && isset( $uploadInfo['rel'] ) ) {
-                       $path = "{$this->mImageBasePath}/{$uploadInfo['rel']}";
-                       if ( file_exists( $path ) ) {
-                               $uploadInfo['fileSrc'] = $path;
-                               $uploadInfo['isTempSrc'] = false;
-                       }
-               }
-
-               if ( $this->mImportUploads ) {
-                       return $this->processUpload( $pageInfo, $uploadInfo );
-               }
-       }
-
-       /**
-        * @param string $contents
-        * @return string
-        */
-       private function dumpTemp( $contents ) {
-               $filename = tempnam( wfTempDir(), 'importupload' );
-               file_put_contents( $filename, $contents );
-               return $filename;
-       }
-
-       /**
-        * @param array $pageInfo
-        * @param array $uploadInfo
-        * @return mixed
-        */
-       private function processUpload( $pageInfo, $uploadInfo ) {
-               $revision = new WikiRevision( $this->config );
-               $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : '';
-
-               $revision->setTitle( $pageInfo['_title'] );
-               $revision->setID( $pageInfo['id'] );
-               $revision->setTimestamp( $uploadInfo['timestamp'] );
-               $revision->setText( $text );
-               $revision->setFilename( $uploadInfo['filename'] );
-               if ( isset( $uploadInfo['archivename'] ) ) {
-                       $revision->setArchiveName( $uploadInfo['archivename'] );
-               }
-               $revision->setSrc( $uploadInfo['src'] );
-               if ( isset( $uploadInfo['fileSrc'] ) ) {
-                       $revision->setFileSrc( $uploadInfo['fileSrc'],
-                               !empty( $uploadInfo['isTempSrc'] ) );
-               }
-               if ( isset( $uploadInfo['sha1base36'] ) ) {
-                       $revision->setSha1Base36( $uploadInfo['sha1base36'] );
-               }
-               $revision->setSize( intval( $uploadInfo['size'] ) );
-               $revision->setComment( $uploadInfo['comment'] );
-
-               if ( isset( $uploadInfo['contributor']['ip'] ) ) {
-                       $revision->setUserIP( $uploadInfo['contributor']['ip'] );
-               }
-               if ( isset( $uploadInfo['contributor']['username'] ) ) {
-                       $revision->setUserName( $uploadInfo['contributor']['username'] );
-               }
-               $revision->setNoUpdates( $this->mNoUpdates );
-
-               return call_user_func( $this->mUploadCallback, $revision );
-       }
-
-       /**
-        * @return array
-        */
-       private function handleContributor() {
-               $fields = array( 'id', 'ip', 'username' );
-               $info = array();
-
-               if ( $this->reader->isEmptyElement ) {
-                       return $info;
-               }
-               while ( $this->reader->read() ) {
-                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
-                                       $this->reader->localName == 'contributor' ) {
-                               break;
-                       }
-
-                       $tag = $this->reader->localName;
-
-                       if ( in_array( $tag, $fields ) ) {
-                               $info[$tag] = $this->nodeContents();
-                       }
-               }
-
-               return $info;
-       }
-
-       /**
-        * @param string $text
-        * @param string|null $ns
-        * @return array|bool
-        */
-       private function processTitle( $text, $ns = null ) {
-               if ( is_null( $this->foreignNamespaces ) ) {
-                       $foreignTitleFactory = new NaiveForeignTitleFactory();
-               } else {
-                       $foreignTitleFactory = new NamespaceAwareForeignTitleFactory(
-                               $this->foreignNamespaces );
-               }
-
-               $foreignTitle = $foreignTitleFactory->createForeignTitle( $text,
-                       intval( $ns ) );
-
-               $title = $this->importTitleFactory->createTitleFromForeignTitle(
-                       $foreignTitle );
-
-               $commandLineMode = $this->config->get( 'CommandLineMode' );
-               if ( is_null( $title ) ) {
-                       # Invalid page title? Ignore the page
-                       $this->notice( 'import-error-invalid', $foreignTitle->getFullText() );
-                       return false;
-               } elseif ( $title->isExternal() ) {
-                       $this->notice( 'import-error-interwiki', $title->getPrefixedText() );
-                       return false;
-               } elseif ( !$title->canExist() ) {
-                       $this->notice( 'import-error-special', $title->getPrefixedText() );
-                       return false;
-               } elseif ( !$title->userCan( 'edit' ) && !$commandLineMode ) {
-                       # Do not import if the importing wiki user cannot edit this page
-                       $this->notice( 'import-error-edit', $title->getPrefixedText() );
-                       return false;
-               } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$commandLineMode ) {
-                       # Do not import if the importing wiki user cannot create this page
-                       $this->notice( 'import-error-create', $title->getPrefixedText() );
-                       return false;
-               }
-
-               return array( $title, $foreignTitle );
-       }
-}
-
-/** This is a horrible hack used to keep source compatibility */
-class UploadSourceAdapter {
-       /** @var array */
-       public static $sourceRegistrations = array();
-
-       /** @var string */
-       private $mSource;
-
-       /** @var string */
-       private $mBuffer;
-
-       /** @var int */
-       private $mPosition;
-
-       /**
-        * @param ImportSource $source
-        * @return string
-        */
-       static function registerSource( ImportSource $source ) {
-               $id = wfRandomString();
-
-               self::$sourceRegistrations[$id] = $source;
-
-               return $id;
-       }
-
-       /**
-        * @param string $path
-        * @param string $mode
-        * @param array $options
-        * @param string $opened_path
-        * @return bool
-        */
-       function stream_open( $path, $mode, $options, &$opened_path ) {
-               $url = parse_url( $path );
-               $id = $url['host'];
-
-               if ( !isset( self::$sourceRegistrations[$id] ) ) {
-                       return false;
-               }
-
-               $this->mSource = self::$sourceRegistrations[$id];
-
-               return true;
-       }
-
-       /**
-        * @param int $count
-        * @return string
-        */
-       function stream_read( $count ) {
-               $return = '';
-               $leave = false;
-
-               while ( !$leave && !$this->mSource->atEnd() &&
-                               strlen( $this->mBuffer ) < $count ) {
-                       $read = $this->mSource->readChunk();
-
-                       if ( !strlen( $read ) ) {
-                               $leave = true;
-                       }
-
-                       $this->mBuffer .= $read;
-               }
-
-               if ( strlen( $this->mBuffer ) ) {
-                       $return = substr( $this->mBuffer, 0, $count );
-                       $this->mBuffer = substr( $this->mBuffer, $count );
-               }
-
-               $this->mPosition += strlen( $return );
-
-               return $return;
-       }
-
-       /**
-        * @param string $data
-        * @return bool
-        */
-       function stream_write( $data ) {
-               return false;
-       }
-
-       /**
-        * @return mixed
-        */
-       function stream_tell() {
-               return $this->mPosition;
-       }
-
-       /**
-        * @return bool
-        */
-       function stream_eof() {
-               return $this->mSource->atEnd();
-       }
-
-       /**
-        * @return array
-        */
-       function url_stat() {
-               $result = array();
-
-               $result['dev'] = $result[0] = 0;
-               $result['ino'] = $result[1] = 0;
-               $result['mode'] = $result[2] = 0;
-               $result['nlink'] = $result[3] = 0;
-               $result['uid'] = $result[4] = 0;
-               $result['gid'] = $result[5] = 0;
-               $result['rdev'] = $result[6] = 0;
-               $result['size'] = $result[7] = 0;
-               $result['atime'] = $result[8] = 0;
-               $result['mtime'] = $result[9] = 0;
-               $result['ctime'] = $result[10] = 0;
-               $result['blksize'] = $result[11] = 0;
-               $result['blocks'] = $result[12] = 0;
-
-               return $result;
-       }
-}
-
-/**
- * @todo document (e.g. one-sentence class description).
- * @ingroup SpecialPage
- */
-class WikiRevision {
-       /** @todo Unused? */
-       public $importer = null;
-
-       /** @var Title */
-       public $title = null;
-
-       /** @var int */
-       public $id = 0;
-
-       /** @var string */
-       public $timestamp = "20010115000000";
-
-       /**
-        * @var int
-        * @todo Can't find any uses. Public, because that's suspicious. Get clarity. */
-       public $user = 0;
-
-       /** @var string */
-       public $user_text = "";
-
-       /** @var string */
-       public $model = null;
-
-       /** @var string */
-       public $format = null;
-
-       /** @var string */
-       public $text = "";
-
-       /** @var int */
-       protected $size;
-
-       /** @var Content */
-       public $content = null;
-
-       /** @var ContentHandler */
-       protected $contentHandler = null;
-
-       /** @var string */
-       public $comment = "";
-
-       /** @var bool */
-       public $minor = false;
-
-       /** @var string */
-       public $type = "";
-
-       /** @var string */
-       public $action = "";
-
-       /** @var string */
-       public $params = "";
-
-       /** @var string */
-       public $fileSrc = '';
-
-       /** @var bool|string */
-       public $sha1base36 = false;
-
-       /**
-        * @var bool
-        * @todo Unused?
-        */
-       public $isTemp = false;
-
-       /** @var string */
-       public $archiveName = '';
-
-       protected $filename;
-
-       /** @var mixed */
-       protected $src;
-
-       /** @todo Unused? */
-       public $fileIsTemp;
-
-       /** @var bool */
-       private $mNoUpdates = false;
-
-       /** @var Config $config */
-       private $config;
-
-       public function __construct( Config $config ) {
-               $this->config = $config;
-       }
-
-       /**
-        * @param Title $title
-        * @throws MWException
-        */
-       function setTitle( $title ) {
-               if ( is_object( $title ) ) {
-                       $this->title = $title;
-               } elseif ( is_null( $title ) ) {
-                       throw new MWException( "WikiRevision given a null title in import. "
-                               . "You may need to adjust \$wgLegalTitleChars." );
-               } else {
-                       throw new MWException( "WikiRevision given non-object title in import." );
-               }
-       }
-
-       /**
-        * @param int $id
-        */
-       function setID( $id ) {
-               $this->id = $id;
-       }
-
-       /**
-        * @param string $ts
-        */
-       function setTimestamp( $ts ) {
-               # 2003-08-05T18:30:02Z
-               $this->timestamp = wfTimestamp( TS_MW, $ts );
-       }
-
-       /**
-        * @param string $user
-        */
-       function setUsername( $user ) {
-               $this->user_text = $user;
-       }
-
-       /**
-        * @param string $ip
-        */
-       function setUserIP( $ip ) {
-               $this->user_text = $ip;
-       }
-
-       /**
-        * @param string $model
-        */
-       function setModel( $model ) {
-               $this->model = $model;
-       }
-
-       /**
-        * @param string $format
-        */
-       function setFormat( $format ) {
-               $this->format = $format;
-       }
-
-       /**
-        * @param string $text
-        */
-       function setText( $text ) {
-               $this->text = $text;
-       }
-
-       /**
-        * @param string $text
-        */
-       function setComment( $text ) {
-               $this->comment = $text;
-       }
-
-       /**
-        * @param bool $minor
-        */
-       function setMinor( $minor ) {
-               $this->minor = (bool)$minor;
-       }
-
-       /**
-        * @param mixed $src
-        */
-       function setSrc( $src ) {
-               $this->src = $src;
-       }
-
-       /**
-        * @param string $src
-        * @param bool $isTemp
-        */
-       function setFileSrc( $src, $isTemp ) {
-               $this->fileSrc = $src;
-               $this->fileIsTemp = $isTemp;
-       }
-
-       /**
-        * @param string $sha1base36
-        */
-       function setSha1Base36( $sha1base36 ) {
-               $this->sha1base36 = $sha1base36;
-       }
-
-       /**
-        * @param string $filename
-        */
-       function setFilename( $filename ) {
-               $this->filename = $filename;
-       }
-
-       /**
-        * @param string $archiveName
-        */
-       function setArchiveName( $archiveName ) {
-               $this->archiveName = $archiveName;
-       }
-
-       /**
-        * @param int $size
-        */
-       function setSize( $size ) {
-               $this->size = intval( $size );
-       }
-
-       /**
-        * @param string $type
-        */
-       function setType( $type ) {
-               $this->type = $type;
-       }
-
-       /**
-        * @param string $action
-        */
-       function setAction( $action ) {
-               $this->action = $action;
-       }
-
-       /**
-        * @param array $params
-        */
-       function setParams( $params ) {
-               $this->params = $params;
-       }
-
-       /**
-        * @param bool $noupdates
-        */
-       public function setNoUpdates( $noupdates ) {
-               $this->mNoUpdates = $noupdates;
-       }
-
-       /**
-        * @return Title
-        */
-       function getTitle() {
-               return $this->title;
-       }
-
-       /**
-        * @return int
-        */
-       function getID() {
-               return $this->id;
-       }
-
-       /**
-        * @return string
-        */
-       function getTimestamp() {
-               return $this->timestamp;
-       }
-
-       /**
-        * @return string
-        */
-       function getUser() {
-               return $this->user_text;
-       }
-
-       /**
-        * @return string
-        *
-        * @deprecated Since 1.21, use getContent() instead.
-        */
-       function getText() {
-               ContentHandler::deprecated( __METHOD__, '1.21' );
-
-               return $this->text;
-       }
-
-       /**
-        * @return ContentHandler
-        */
-       function getContentHandler() {
-               if ( is_null( $this->contentHandler ) ) {
-                       $this->contentHandler = ContentHandler::getForModelID( $this->getModel() );
-               }
-
-               return $this->contentHandler;
-       }
-
-       /**
-        * @return Content
-        */
-       function getContent() {
-               if ( is_null( $this->content ) ) {
-                       $handler = $this->getContentHandler();
-                       $this->content = $handler->unserializeContent( $this->text, $this->getFormat() );
-               }
-
-               return $this->content;
-       }
-
-       /**
-        * @return string
-        */
-       function getModel() {
-               if ( is_null( $this->model ) ) {
-                       $this->model = $this->getTitle()->getContentModel();
-               }
-
-               return $this->model;
-       }
-
-       /**
-        * @return string
-        */
-       function getFormat() {
-               if ( is_null( $this->format ) ) {
-                       $this->format = $this->getContentHandler()->getDefaultFormat();
-               }
-
-               return $this->format;
-       }
-
-       /**
-        * @return string
-        */
-       function getComment() {
-               return $this->comment;
-       }
-
-       /**
-        * @return bool
-        */
-       function getMinor() {
-               return $this->minor;
-       }
-
-       /**
-        * @return mixed
-        */
-       function getSrc() {
-               return $this->src;
-       }
-
-       /**
-        * @return bool|string
-        */
-       function getSha1() {
-               if ( $this->sha1base36 ) {
-                       return Wikimedia\base_convert( $this->sha1base36, 36, 16 );
-               }
-               return false;
-       }
-
-       /**
-        * @return string
-        */
-       function getFileSrc() {
-               return $this->fileSrc;
-       }
-
-       /**
-        * @return bool
-        */
-       function isTempSrc() {
-               return $this->isTemp;
-       }
-
-       /**
-        * @return mixed
-        */
-       function getFilename() {
-               return $this->filename;
-       }
-
-       /**
-        * @return string
-        */
-       function getArchiveName() {
-               return $this->archiveName;
-       }
-
-       /**
-        * @return mixed
-        */
-       function getSize() {
-               return $this->size;
-       }
-
-       /**
-        * @return string
-        */
-       function getType() {
-               return $this->type;
-       }
-
-       /**
-        * @return string
-        */
-       function getAction() {
-               return $this->action;
-       }
-
-       /**
-        * @return string
-        */
-       function getParams() {
-               return $this->params;
-       }
-
-       /**
-        * @return bool
-        */
-       function importOldRevision() {
-               $dbw = wfGetDB( DB_MASTER );
-
-               # Sneak a single revision into place
-               $user = User::newFromName( $this->getUser() );
-               if ( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-                       $userObj = $user;
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-                       $userObj = new User;
-               }
-
-               // avoid memory leak...?
-               Title::clearCaches();
-
-               $page = WikiPage::factory( $this->title );
-               $page->loadPageData( 'fromdbmaster' );
-               if ( !$page->exists() ) {
-                       # must create the page...
-                       $pageId = $page->insertOn( $dbw );
-                       $created = true;
-                       $oldcountable = null;
-               } else {
-                       $pageId = $page->getId();
-                       $created = false;
-
-                       $prior = $dbw->selectField( 'revision', '1',
-                               array( 'rev_page' => $pageId,
-                                       'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
-                                       'rev_user_text' => $userText,
-                                       'rev_comment' => $this->getComment() ),
-                               __METHOD__
-                       );
-                       if ( $prior ) {
-                               // @todo FIXME: This could fail slightly for multiple matches :P
-                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
-                                       $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
-                               return false;
-                       }
-               }
-
-               // Select previous version to make size diffs correct
-               $prevId = $dbw->selectField( 'revision', 'rev_id',
-                       array(
-                               'rev_page' => $pageId,
-                               'rev_timestamp <= ' . $dbw->timestamp( $this->timestamp ),
-                       ),
-                       __METHOD__,
-                       array( 'ORDER BY' => array(
-                                       'rev_timestamp DESC',
-                                       'rev_id DESC', // timestamp is not unique per page
-                               )
-                       )
-               );
-
-               # @todo FIXME: Use original rev_id optionally (better for backups)
-               # Insert the row
-               $revision = new Revision( array(
-                       'title' => $this->title,
-                       'page' => $pageId,
-                       'content_model' => $this->getModel(),
-                       'content_format' => $this->getFormat(),
-                       // XXX: just set 'content' => $this->getContent()?
-                       'text' => $this->getContent()->serialize( $this->getFormat() ),
-                       'comment' => $this->getComment(),
-                       'user' => $userId,
-                       'user_text' => $userText,
-                       'timestamp' => $this->timestamp,
-                       'minor_edit' => $this->minor,
-                       'parent_id' => $prevId,
-                       ) );
-               $revision->insertOn( $dbw );
-               $changed = $page->updateIfNewerOn( $dbw, $revision );
-
-               if ( $changed !== false && !$this->mNoUpdates ) {
-                       wfDebug( __METHOD__ . ": running updates\n" );
-                       // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
-                       $page->doEditUpdates(
-                               $revision,
-                               $userObj,
-                               array( 'created' => $created, 'oldcountable' => 'no-change' )
-                       );
-               }
-
-               return true;
-       }
-
-       function importLogItem() {
-               $dbw = wfGetDB( DB_MASTER );
-
-               $user = User::newFromName( $this->getUser() );
-               if ( $user ) {
-                       $userId = intval( $user->getId() );
-                       $userText = $user->getName();
-               } else {
-                       $userId = 0;
-                       $userText = $this->getUser();
-               }
-
-               # @todo FIXME: This will not record autoblocks
-               if ( !$this->getTitle() ) {
-                       wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
-                               $this->timestamp . "\n" );
-                       return;
-               }
-               # Check if it exists already
-               // @todo FIXME: Use original log ID (better for backups)
-               $prior = $dbw->selectField( 'logging', '1',
-                       array( 'log_type' => $this->getType(),
-                               'log_action' => $this->getAction(),
-                               'log_timestamp' => $dbw->timestamp( $this->timestamp ),
-                               'log_namespace' => $this->getTitle()->getNamespace(),
-                               'log_title' => $this->getTitle()->getDBkey(),
-                               'log_comment' => $this->getComment(),
-                               # 'log_user_text' => $this->user_text,
-                               'log_params' => $this->params ),
-                       __METHOD__
-               );
-               // @todo FIXME: This could fail slightly for multiple matches :P
-               if ( $prior ) {
-                       wfDebug( __METHOD__
-                               . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp "
-                               . $this->timestamp . "\n" );
-                       return;
-               }
-               $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
-               $data = array(
-                       'log_id' => $log_id,
-                       'log_type' => $this->type,
-                       'log_action' => $this->action,
-                       'log_timestamp' => $dbw->timestamp( $this->timestamp ),
-                       'log_user' =>  $userId,
-                       'log_user_text' => $userText,
-                       'log_namespace' => $this->getTitle()->getNamespace(),
-                       'log_title' => $this->getTitle()->getDBkey(),
-                       'log_comment' => $this->getComment(),
-                       'log_params' => $this->params
-               );
-               $dbw->insert( 'logging', $data, __METHOD__ );
-       }
-
-       /**
-        * @return bool
-        */
-       function importUpload() {
-               # Construct a file
-               $archiveName = $this->getArchiveName();
-               if ( $archiveName ) {
-                       wfDebug( __METHOD__ . "Importing archived file as $archiveName\n" );
-                       $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
-                               RepoGroup::singleton()->getLocalRepo(), $archiveName );
-               } else {
-                       $file = wfLocalFile( $this->getTitle() );
-                       $file->load( File::READ_LATEST );
-                       wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
-                       if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) {
-                               $archiveName = $file->getTimestamp() . '!' . $file->getName();
-                               $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
-                                       RepoGroup::singleton()->getLocalRepo(), $archiveName );
-                               wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" );
-                       }
-               }
-               if ( !$file ) {
-                       wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" );
-                       return false;
-               }
-
-               # Get the file source or download if necessary
-               $source = $this->getFileSrc();
-               $flags = $this->isTempSrc() ? File::DELETE_SOURCE : 0;
-               if ( !$source ) {
-                       $source = $this->downloadSource();
-                       $flags |= File::DELETE_SOURCE;
-               }
-               if ( !$source ) {
-                       wfDebug( __METHOD__ . ": Could not fetch remote file.\n" );
-                       return false;
-               }
-               $sha1 = $this->getSha1();
-               if ( $sha1 && ( $sha1 !== sha1_file( $source ) ) ) {
-                       if ( $flags & File::DELETE_SOURCE ) {
-                               # Broken file; delete it if it is a temporary file
-                               unlink( $source );
-                       }
-                       wfDebug( __METHOD__ . ": Corrupt file $source.\n" );
-                       return false;
-               }
-
-               $user = User::newFromName( $this->user_text );
-
-               # Do the actual upload
-               if ( $archiveName ) {
-                       $status = $file->uploadOld( $source, $archiveName,
-                               $this->getTimestamp(), $this->getComment(), $user, $flags );
-               } else {
-                       $status = $file->upload( $source, $this->getComment(), $this->getComment(),
-                               $flags, false, $this->getTimestamp(), $user );
-               }
-
-               if ( $status->isGood() ) {
-                       wfDebug( __METHOD__ . ": Successful\n" );
-                       return true;
-               } else {
-                       wfDebug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
-                       return false;
-               }
-       }
-
-       /**
-        * @return bool|string
-        */
-       function downloadSource() {
-               if ( !$this->config->get( 'EnableUploads' ) ) {
-                       return false;
-               }
-
-               $tempo = tempnam( wfTempDir(), 'download' );
-               $f = fopen( $tempo, 'wb' );
-               if ( !$f ) {
-                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
-                       return false;
-               }
-
-               // @todo FIXME!
-               $src = $this->getSrc();
-               $data = Http::get( $src, array(), __METHOD__ );
-               if ( !$data ) {
-                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
-                       fclose( $f );
-                       unlink( $tempo );
-                       return false;
-               }
-
-               fwrite( $f, $data );
-               fclose( $f );
-
-               return $tempo;
-       }
-
-}
-
-/**
- * Source interface for XML import.
- */
-interface ImportSource {
-
-       /**
-        * Indicates whether the end of the input has been reached.
-        * Will return true after a finite number of calls to readChunk.
-        *
-        * @return bool true if there is no more input, false otherwise.
-        */
-       function atEnd();
-
-       /**
-        * Return a chunk of the input, as a (possibly empty) string.
-        * When the end of input is reached, readChunk() returns false.
-        * If atEnd() returns false, readChunk() will return a string.
-        * If atEnd() returns true, readChunk() will return false.
-        *
-        * @return bool|string
-        */
-       function readChunk();
-}
-
-/**
- * Used for importing XML dumps where the content of the dump is in a string.
- * This class is ineffecient, and should only be used for small dumps.
- * For larger dumps, ImportStreamSource should be used instead.
- *
- * @ingroup SpecialPage
- */
-class ImportStringSource implements ImportSource {
-       function __construct( $string ) {
-               $this->mString = $string;
-               $this->mRead = false;
-       }
-
-       /**
-        * @return bool
-        */
-       function atEnd() {
-               return $this->mRead;
-       }
-
-       /**
-        * @return bool|string
-        */
-       function readChunk() {
-               if ( $this->atEnd() ) {
-                       return false;
-               }
-               $this->mRead = true;
-               return $this->mString;
-       }
-}
-
-/**
- * Imports a XML dump from a file (either from file upload, files on disk, or HTTP)
- * @ingroup SpecialPage
- */
-class ImportStreamSource implements ImportSource {
-       function __construct( $handle ) {
-               $this->mHandle = $handle;
-       }
-
-       /**
-        * @return bool
-        */
-       function atEnd() {
-               return feof( $this->mHandle );
-       }
-
-       /**
-        * @return string
-        */
-       function readChunk() {
-               return fread( $this->mHandle, 32768 );
-       }
-
-       /**
-        * @param string $filename
-        * @return Status
-        */
-       static function newFromFile( $filename ) {
-               MediaWiki\suppressWarnings();
-               $file = fopen( $filename, 'rt' );
-               MediaWiki\restoreWarnings();
-               if ( !$file ) {
-                       return Status::newFatal( "importcantopen" );
-               }
-               return Status::newGood( new ImportStreamSource( $file ) );
-       }
-
-       /**
-        * @param string $fieldname
-        * @return Status
-        */
-       static function newFromUpload( $fieldname = "xmlimport" ) {
-               $upload =& $_FILES[$fieldname];
-
-               if ( $upload === null || !$upload['name'] ) {
-                       return Status::newFatal( 'importnofile' );
-               }
-               if ( !empty( $upload['error'] ) ) {
-                       switch ( $upload['error'] ) {
-                               case 1:
-                                       # The uploaded file exceeds the upload_max_filesize directive in php.ini.
-                                       return Status::newFatal( 'importuploaderrorsize' );
-                               case 2:
-                                       # The uploaded file exceeds the MAX_FILE_SIZE directive that
-                                       # was specified in the HTML form.
-                                       return Status::newFatal( 'importuploaderrorsize' );
-                               case 3:
-                                       # The uploaded file was only partially uploaded
-                                       return Status::newFatal( 'importuploaderrorpartial' );
-                               case 6:
-                                       # Missing a temporary folder.
-                                       return Status::newFatal( 'importuploaderrortemp' );
-                               # case else: # Currently impossible
-                       }
-
-               }
-               $fname = $upload['tmp_name'];
-               if ( is_uploaded_file( $fname ) ) {
-                       return ImportStreamSource::newFromFile( $fname );
-               } else {
-                       return Status::newFatal( 'importnofile' );
-               }
-       }
-
-       /**
-        * @param string $url
-        * @param string $method
-        * @return Status
-        */
-       static function newFromURL( $url, $method = 'GET' ) {
-               wfDebug( __METHOD__ . ": opening $url\n" );
-               # Use the standard HTTP fetch function; it times out
-               # quicker and sorts out user-agent problems which might
-               # otherwise prevent importing from large sites, such
-               # as the Wikimedia cluster, etc.
-               $data = Http::request( $method, $url, array( 'followRedirects' => true ), __METHOD__ );
-               if ( $data !== false ) {
-                       $file = tmpfile();
-                       fwrite( $file, $data );
-                       fflush( $file );
-                       fseek( $file, 0 );
-                       return Status::newGood( new ImportStreamSource( $file ) );
-               } else {
-                       return Status::newFatal( 'importcantopen' );
-               }
-       }
-
-       /**
-        * @param string $interwiki
-        * @param string $page
-        * @param bool $history
-        * @param bool $templates
-        * @param int $pageLinkDepth
-        * @return Status
-        */
-       public static function newFromInterwiki( $interwiki, $page, $history = false,
-               $templates = false, $pageLinkDepth = 0
-       ) {
-               if ( $page == '' ) {
-                       return Status::newFatal( 'import-noarticle' );
-               }
-
-               # Look up the first interwiki prefix, and let the foreign site handle
-               # subsequent interwiki prefixes
-               $firstIwPrefix = strtok( $interwiki, ':' );
-               $firstIw = Interwiki::fetch( $firstIwPrefix );
-               if ( !$firstIw ) {
-                       return Status::newFatal( 'importbadinterwiki' );
-               }
-
-               $additionalIwPrefixes = strtok( '' );
-               if ( $additionalIwPrefixes ) {
-                       $additionalIwPrefixes .= ':';
-               }
-               # Have to do a DB-key replacement ourselves; otherwise spaces get
-               # URL-encoded to +, which is wrong in this case. Similar to logic in
-               # Title::getLocalURL
-               $link = $firstIw->getURL( strtr( "${additionalIwPrefixes}Special:Export/$page",
-                       ' ', '_' ) );
-
-               $params = array();
-               if ( $history ) {
-                       $params['history'] = 1;
-               }
-               if ( $templates ) {
-                       $params['templates'] = 1;
-               }
-               if ( $pageLinkDepth ) {
-                       $params['pagelink-depth'] = $pageLinkDepth;
-               }
-
-               $url = wfAppendQuery( $link, $params );
-               # For interwikis, use POST to avoid redirects.
-               return ImportStreamSource::newFromURL( $url, "POST" );
-       }
-}
index 5255b9a..3ba472b 100644 (file)
@@ -346,7 +346,10 @@ class Linker {
                } elseif ( in_array( 'known', $options ) ) {
                        $defaults['title'] = $target->getPrefixedText();
                } else {
-                       $defaults['title'] = wfMessage( 'red-link-title', $target->getPrefixedText() )->text();
+                       // This ends up in parser cache!
+                       $defaults['title'] = wfMessage( 'red-link-title', $target->getPrefixedText() )
+                               ->inContentLanguage()
+                               ->text();
                }
 
                # Finally, merge the custom attribs with the default ones, and iterate
@@ -670,17 +673,6 @@ class Linker {
                return str_replace( "\n", ' ', $prefix . $s . $postfix );
        }
 
-       /**
-        * See makeImageLink()
-        * When this function is removed, remove if( $parser instanceof Parser ) check there too
-        * @deprecated since 1.20
-        */
-       public static function makeImageLink2( Title $title, $file, $frameParams = array(),
-               $handlerParams = array(), $time = false, $query = "", $widthOption = null ) {
-               return self::makeImageLink( null, $title, $file, $frameParams,
-                       $handlerParams, $time, $query, $widthOption );
-       }
-
        /**
         * Get the link parameters for MediaTransformOutput::toHtml() from given
         * frame parameters supplied by the Parser.
@@ -2161,13 +2153,13 @@ class Linker {
         * @param string $name Id of the element, minus prefixes.
         * @param string|null $options Null or the string 'withaccess' to add an access-
         *   key hint
+        * @param array $msgParams Parameters to pass to the message
+        *
         * @return string Contents of the title attribute (which you must HTML-
         *   escape), or false for no title attribute
         */
-       public static function titleAttrib( $name, $options = null ) {
-
-               $message = wfMessage( "tooltip-$name" );
-
+       public static function titleAttrib( $name, $options = null, array $msgParams = array() ) {
+               $message = wfMessage( "tooltip-$name", $msgParams );
                if ( !$message->exists() ) {
                        $tooltip = false;
                } else {
@@ -2315,84 +2307,20 @@ class Linker {
 
        /* Deprecated methods */
 
-       /**
-        * @deprecated since 1.16 Use link(); warnings since 1.21
-        *
-        * Make a link for a title which may or may not be in the database. If you need to
-        * call this lots of times, pre-fill the link cache with a LinkBatch, otherwise each
-        * call to this will result in a DB query.
-        *
-        * @param Title $nt The title object to make the link from, e.g. from Title::newFromText.
-        * @param string $text Link text
-        * @param string $query Optional query part
-        * @param string $trail Optional trail. Alphabetic characters at the start of this string will
-        *   be included in the link text. Other characters will be appended after
-        *   the end of the link.
-        * @param string $prefix Optional prefix. As trail, only before instead of after.
-        * @return string
-        */
-       static function makeLinkObj( $nt, $text = '', $query = '', $trail = '', $prefix = '' ) {
-               wfDeprecated( __METHOD__, '1.21' );
-
-               $query = wfCgiToArray( $query );
-               list( $inside, $trail ) = self::splitTrail( $trail );
-               if ( $text === '' ) {
-                       $text = self::linkText( $nt );
-               }
-
-               $ret = self::link( $nt, "$prefix$text$inside", array(), $query ) . $trail;
-
-               return $ret;
-       }
-
-       /**
-        * @deprecated since 1.16 Use link(); warnings since 1.21
-        *
-        * Make a link for a title which definitely exists. This is faster than makeLinkObj because
-        * it doesn't have to do a database query. It's also valid for interwiki titles and special
-        * pages.
-        *
-        * @param Title $title Title object of target page
-        * @param string $text Text to replace the title
-        * @param string $query Link target
-        * @param string $trail Text after link
-        * @param string $prefix Text before link text
-        * @param string $aprops Extra attributes to the a-element
-        * @param string $style Style to apply - if empty, use getInternalLinkAttributesObj instead
-        * @return string The a-element
-        */
-       static function makeKnownLinkObj(
-               $title, $text = '', $query = '', $trail = '', $prefix = '', $aprops = '', $style = ''
-       ) {
-               wfDeprecated( __METHOD__, '1.21' );
-
-               if ( $text == '' ) {
-                       $text = self::linkText( $title );
-               }
-               $attribs = Sanitizer::mergeAttributes(
-                       Sanitizer::decodeTagAttributes( $aprops ),
-                       Sanitizer::decodeTagAttributes( $style )
-               );
-               $query = wfCgiToArray( $query );
-               list( $inside, $trail ) = self::splitTrail( $trail );
-
-               $ret = self::link( $title, "$prefix$text$inside", $attribs, $query,
-                       array( 'known', 'noclasses' ) ) . $trail;
-
-               return $ret;
-       }
-
        /**
         * Returns the attributes for the tooltip and access key.
+        *
         * @param string $name
+        * @param array $msgParams Params for constructing the message
+        *
         * @return array
         */
-       public static function tooltipAndAccesskeyAttribs( $name ) {
+       public static function tooltipAndAccesskeyAttribs( $name, array $msgParams = array() ) {
                # @todo FIXME: If Sanitizer::expandAttributes() treated "false" as "output
                # no attribute" instead of "output '' as value for attribute", this
                # would be three lines.
                $attribs = array(
-                       'title' => self::titleAttrib( $name, 'withaccess' ),
+                       'title' => self::titleAttrib( $name, 'withaccess', $msgParams ),
                        'accesskey' => self::accesskey( $name )
                );
                if ( $attribs['title'] === false ) {
index be0073d..6342d71 100644 (file)
@@ -379,23 +379,26 @@ class MediaWiki {
         * Initialize the main Article object for "standard" actions (view, etc)
         * Create an Article object for the page, following redirects if needed.
         *
-        * @return mixed An Article, or a string to redirect to another URL
+        * @return Article|string An Article, or a string to redirect to another URL
         */
        private function initializeArticle() {
-
                $title = $this->context->getTitle();
                if ( $this->context->canUseWikiPage() ) {
                        // Try to use request context wiki page, as there
                        // is already data from db saved in per process
                        // cache there from this->getAction() call.
                        $page = $this->context->getWikiPage();
-                       $article = Article::newFromWikiPage( $page, $this->context );
                } else {
                        // This case should not happen, but just in case.
-                       $article = Article::newFromTitle( $title, $this->context );
-                       $this->context->setWikiPage( $article->getPage() );
+                       // @TODO: remove this or use an exception
+                       $page = WikiPage::factory( $title );
+                       $this->context->setWikiPage( $page );
+                       wfWarn( "RequestContext::canUseWikiPage() returned false" );
                }
 
+               // Make GUI wrapper for the WikiPage
+               $article = Article::newFromWikiPage( $page, $this->context );
+
                // Skip some unnecessary code if the content model doesn't support redirects
                if ( !ContentHandler::getForTitle( $title )->supportsRedirects() ) {
                        return $article;
@@ -406,7 +409,7 @@ class MediaWiki {
                // Namespace might change when using redirects
                // Check for redirects ...
                $action = $request->getVal( 'action', 'view' );
-               $file = ( $title->getNamespace() == NS_FILE ) ? $article->getFile() : null;
+               $file = ( $page instanceof WikiFilePage ) ? $page->getFile() : null;
                if ( ( $action == 'view' || $action == 'render' ) // ... for actions that show content
                        && !$request->getVal( 'oldid' ) // ... and are not old revisions
                        && !$request->getVal( 'diff' ) // ... and not when showing diff
@@ -419,12 +422,13 @@ class MediaWiki {
 
                        Hooks::run( 'InitializeArticleMaybeRedirect',
                                array( &$title, &$request, &$ignoreRedirect, &$target, &$article ) );
+                       $page = $article->getPage(); // reflect any hook changes
 
                        // Follow redirects only for... redirects.
                        // If $target is set, then a hook wanted to redirect.
-                       if ( !$ignoreRedirect && ( $target || $article->isRedirect() ) ) {
+                       if ( !$ignoreRedirect && ( $target || $page->isRedirect() ) ) {
                                // Is the target already set by an extension?
-                               $target = $target ? $target : $article->followRedirect();
+                               $target = $target ? $target : $page->followRedirect();
                                if ( is_string( $target ) ) {
                                        if ( !$this->config->get( 'DisableHardRedirects' ) ) {
                                                // we'll need to redirect
@@ -433,16 +437,19 @@ class MediaWiki {
                                }
                                if ( is_object( $target ) ) {
                                        // Rewrite environment to redirected article
-                                       $rarticle = Article::newFromTitle( $target, $this->context );
-                                       $rarticle->loadPageData();
-                                       if ( $rarticle->exists() || ( is_object( $file ) && !$file->isLocal() ) ) {
+                                       $rpage = WikiPage::factory( $target );
+                                       $rpage->loadPageData();
+                                       if ( $rpage->exists() || ( is_object( $file ) && !$file->isLocal() ) ) {
+                                               $rarticle = Article::newFromWikiPage( $rpage, $this->context );
                                                $rarticle->setRedirectedFrom( $title );
+
                                                $article = $rarticle;
                                                $this->context->setTitle( $target );
                                                $this->context->setWikiPage( $article->getPage() );
                                        }
                                }
                        } else {
+                               // Article may have been changed by hook
                                $this->context->setTitle( $article->getTitle() );
                                $this->context->setWikiPage( $article->getPage() );
                        }
@@ -458,7 +465,6 @@ class MediaWiki {
         * @param Title $requestTitle The original title, before any redirects were applied
         */
        private function performAction( Page $page, Title $requestTitle ) {
-
                $request = $this->context->getRequest();
                $output = $this->context->getOutput();
                $title = $this->context->getTitle();
@@ -471,10 +477,16 @@ class MediaWiki {
                }
 
                $act = $this->getAction();
-
                $action = Action::factory( $act, $page, $this->context );
 
                if ( $action instanceof Action ) {
+                       // Narrow DB query expectations for this HTTP request
+                       $trxLimits = $this->config->get( 'TrxProfilerLimits' );
+                       $trxProfiler = Profiler::instance()->getTransactionProfiler();
+                       if ( $request->wasPosted() && !$action->doesWrites() ) {
+                               $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
+                       }
+
                        # Let CDN cache things if we can purge them.
                        if ( $this->config->get( 'UseSquid' ) &&
                                in_array(
@@ -494,7 +506,6 @@ class MediaWiki {
                        $output->setStatusCode( 404 );
                        $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
                }
-
        }
 
        /**
@@ -554,7 +565,7 @@ class MediaWiki {
                        } );
                } );
                // Commit all changes
-               $factory->commitMasterChanges();
+               $factory->commitMasterChanges( __METHOD__ );
                // Record ChronologyProtector positions
                $factory->shutdown();
                wfDebug( __METHOD__ . ': all transactions committed' );
@@ -562,15 +573,19 @@ class MediaWiki {
                DeferredUpdates::doUpdates( 'enqueue', DeferredUpdates::PRESEND );
                wfDebug( __METHOD__ . ': pre-send deferred updates completed' );
 
-               // 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)
+               // 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() ) {
                        $expires = time() + $config->get( 'DataCenterUpdateStickTTL' );
-                       $request->response()->setCookie( 'UseDC', 'master', $expires, array( 'prefix' => '' ) );
+                       $options = array( '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
+               // Avoid letting a few seconds of slave lag cause a month of stale data. This logic is
+               // also intimately related to the value of $wgCdnReboundPurgeDelay.
                if ( $factory->laggedSlaveUsed() ) {
                        $maxAge = $config->get( 'CdnMaxageLagged' );
                        $context->getOutput()->lowerCdnMaxage( $maxAge );
@@ -623,7 +638,7 @@ class MediaWiki {
        }
 
        private function main() {
-               global $wgTitle, $wgTrxProfilerLimits;
+               global $wgTitle;
 
                $request = $this->context->getRequest();
 
@@ -647,13 +662,14 @@ class MediaWiki {
                $action = $this->getAction();
                $wgTitle = $title;
 
+               // Set DB query expectations for this HTTP request
+               $trxLimits = $this->config->get( 'TrxProfilerLimits' );
                $trxProfiler = Profiler::instance()->getTransactionProfiler();
                $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
-
                if ( $request->wasPosted() ) {
-                       $trxProfiler->setExpectations( $wgTrxProfilerLimits['POST'], __METHOD__ );
+                       $trxProfiler->setExpectations( $trxLimits['POST'], __METHOD__ );
                } else {
-                       $trxProfiler->setExpectations( $wgTrxProfilerLimits['GET'], __METHOD__ );
+                       $trxProfiler->setExpectations( $trxLimits['GET'], __METHOD__ );
                }
 
                // If the user has forceHTTPS set to true, or if the user
@@ -664,8 +680,10 @@ class MediaWiki {
                if (
                        $request->getProtocol() == 'http' &&
                        (
+                               $request->getSession()->shouldForceHTTPS() ||
+                               // Check the cookie manually, for paranoia
                                $request->getCookie( 'forceHTTPS', '' ) ||
-                               // check for prefixed version for currently logged in users
+                               // check for prefixed version that was used for a time in older MW versions
                                $request->getCookie( 'forceHTTPS' ) ||
                                // Avoid checking the user and groups unless it's enabled.
                                (
@@ -741,7 +759,7 @@ class MediaWiki {
         */
        public function restInPeace( $mode = 'fast' ) {
                // Assure deferred updates are not in the main transaction
-               wfGetLBFactory()->commitMasterChanges();
+               wfGetLBFactory()->commitMasterChanges( __METHOD__ );
 
                // Ignore things like master queries/connections on GET requests
                // as long as they are in deferred updates (which catch errors).
@@ -764,7 +782,7 @@ class MediaWiki {
 
                // Commit and close up!
                $factory = wfGetLBFactory();
-               $factory->commitMasterChanges();
+               $factory->commitMasterChanges( __METHOD__ );
                $factory->shutdown( LBFactory::SHUTDOWN_NO_CHRONPROT );
 
                wfDebug( "Request ended normally\n" );
index 78eb458..93ba702 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Session\SessionManager;
 use WrappedString\WrappedString;
 
 /**
@@ -229,9 +230,6 @@ class OutputPage extends ContextSource {
        /** @var string */
        private $mPageTitleActionText = '';
 
-       /** @var array */
-       private $mParseWarnings = array();
-
        /** @var int Cache stuff. Looks like mEnableClientCache */
        protected $mCdnMaxage = 0;
        /** @var int Upper limit on mCdnMaxage */
@@ -816,7 +814,7 @@ class OutputPage extends ContextSource {
 
                $clientHeader = $this->getRequest()->getHeader( 'If-Modified-Since' );
                if ( $clientHeader === false ) {
-                       wfDebug( __METHOD__ . ": client did not send If-Modified-Since header\n", 'log' );
+                       wfDebug( __METHOD__ . ": client did not send If-Modified-Since header", 'private' );
                        return false;
                }
 
@@ -845,17 +843,17 @@ class OutputPage extends ContextSource {
                }
 
                wfDebug( __METHOD__ . ": client sent If-Modified-Since: " .
-                       wfTimestamp( TS_ISO_8601, $clientHeaderTime ) . "\n", 'log' );
+                       wfTimestamp( TS_ISO_8601, $clientHeaderTime ), 'private' );
                wfDebug( __METHOD__ . ": effective Last-Modified: " .
-                       wfTimestamp( TS_ISO_8601, $maxModified ) . "\n", 'log' );
+                       wfTimestamp( TS_ISO_8601, $maxModified ), 'private' );
                if ( $clientHeaderTime < $maxModified ) {
-                       wfDebug( __METHOD__ . ": STALE, $info\n", 'log' );
+                       wfDebug( __METHOD__ . ": STALE, $info", 'private' );
                        return false;
                }
 
                # Not modified
                # Give a 304 Not Modified response code and disable body output
-               wfDebug( __METHOD__ . ": NOT MODIFIED, $info\n", 'log' );
+               wfDebug( __METHOD__ . ": NOT MODIFIED, $info", 'private' );
                ini_set( 'zlib.output_compression', 0 );
                $this->getRequest()->response()->statusHeader( 304 );
                $this->sendCacheControl();
@@ -1773,7 +1771,6 @@ class OutputPage extends ContextSource {
                $this->mNewSectionLink = $parserOutput->getNewSection();
                $this->mHideNewSectionLink = $parserOutput->getHideNewSection();
 
-               $this->mParseWarnings = $parserOutput->getWarnings();
                if ( !$parserOutput->isCacheable() ) {
                        $this->enableClientCache( false );
                }
@@ -1981,11 +1978,9 @@ class OutputPage extends ContextSource {
                if ( $cookies === null ) {
                        $config = $this->getConfig();
                        $cookies = array_merge(
+                               SessionManager::singleton()->getVaryCookies(),
                                array(
-                                       $config->get( 'CookiePrefix' ) . 'Token',
-                                       $config->get( 'CookiePrefix' ) . 'LoggedOut',
-                                       "forceHTTPS",
-                                       session_name()
+                                       'forceHTTPS',
                                ),
                                $config->get( 'CacheVaryCookies' )
                        );
@@ -2037,6 +2032,9 @@ class OutputPage extends ContextSource {
         * @return string
         */
        public function getVaryHeader() {
+               foreach ( SessionManager::singleton()->getVaryHeaders() as $header => $options ) {
+                       $this->addVaryHeader( $header, $options );
+               }
                return 'Vary: ' . join( ', ', array_keys( $this->mVaryHeader ) );
        }
 
@@ -2054,6 +2052,10 @@ class OutputPage extends ContextSource {
                }
                $this->addVaryHeader( 'Cookie', $cookiesOption );
 
+               foreach ( SessionManager::singleton()->getVaryHeaders() as $header => $options ) {
+                       $this->addVaryHeader( $header, $options );
+               }
+
                $headers = array();
                foreach ( $this->mVaryHeader as $header => $option ) {
                        $newheader = $header;
@@ -2177,14 +2179,14 @@ class OutputPage extends ContextSource {
 
                if ( $this->mEnableClientCache ) {
                        if (
-                               $config->get( 'UseSquid' ) && session_id() == '' && !$this->isPrintable() &&
-                               $this->mCdnMaxage != 0 && !$this->haveCacheVaryCookies()
+                               $config->get( 'UseSquid' ) && !SessionManager::getGlobalSession()->isPersistent() &&
+                               !$this->isPrintable() && $this->mCdnMaxage != 0 && !$this->haveCacheVaryCookies()
                        ) {
                                if ( $config->get( 'UseESI' ) ) {
                                        # We'll purge the proxy cache explicitly, but require end user agents
                                        # to revalidate against the proxy on each visit.
                                        # Surrogate-Control controls our CDN, Cache-Control downstream caches
-                                       wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **\n", 'log' );
+                                       wfDebug( __METHOD__ . ": proxy caching with ESI; {$this->mLastModified} **", 'private' );
                                        # start with a shorter timeout for initial testing
                                        # header( 'Surrogate-Control: max-age=2678400+2678400, content="ESI/1.0"');
                                        $response->header( 'Surrogate-Control: max-age=' . $config->get( 'SquidMaxage' )
@@ -2195,7 +2197,7 @@ class OutputPage extends ContextSource {
                                        # to revalidate against the proxy on each visit.
                                        # IMPORTANT! The CDN needs to replace the Cache-Control header with
                                        # Cache-Control: s-maxage=0, must-revalidate, max-age=0
-                                       wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **\n", 'log' );
+                                       wfDebug( __METHOD__ . ": local proxy caching; {$this->mLastModified} **", 'private' );
                                        # start with a shorter timeout for initial testing
                                        # header( "Cache-Control: s-maxage=2678400, must-revalidate, max-age=0" );
                                        $response->header( 'Cache-Control: s-maxage=' . $this->mCdnMaxage
@@ -2204,7 +2206,7 @@ class OutputPage extends ContextSource {
                        } else {
                                # We do want clients to cache if they can, but they *must* check for updates
                                # on revisiting the page.
-                               wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **\n", 'log' );
+                               wfDebug( __METHOD__ . ": private caching; {$this->mLastModified} **", 'private' );
                                $response->header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', 0 ) . ' GMT' );
                                $response->header( "Cache-Control: private, must-revalidate, max-age=0" );
                        }
@@ -2212,7 +2214,7 @@ class OutputPage extends ContextSource {
                                $response->header( "Last-Modified: {$this->mLastModified}" );
                        }
                } else {
-                       wfDebug( __METHOD__ . ": no caching **\n", 'log' );
+                       wfDebug( __METHOD__ . ": no caching **", 'private' );
 
                        # In general, the absence of a last modified header should be enough to prevent
                        # the client from using its cache. We send a few other things just to make sure.
@@ -2338,15 +2340,6 @@ class OutputPage extends ContextSource {
                print $ins;
        }
 
-       /**
-        * Produce a "user is blocked" page.
-        * @deprecated since 1.18
-        */
-       function blockedPage() {
-               wfDeprecated( __METHOD__, '1.18' );
-               throw new UserBlockedError( $this->getUser()->mBlock );
-       }
-
        /**
         * Prepare this object to display an error page; disable caching and
         * indexing, clear the current text and redirect, set the page's title
@@ -2488,17 +2481,6 @@ class OutputPage extends ContextSource {
                $this->returnToMain();
        }
 
-       /**
-        * Display an error page noting that a given permission bit is required.
-        * @deprecated since 1.18, just throw the exception directly
-        * @param string $permission Key required
-        * @throws PermissionsError
-        */
-       public function permissionRequired( $permission ) {
-               wfDeprecated( __METHOD__, '1.18' );
-               throw new PermissionsError( $permission );
-       }
-
        /**
         * Format a list of error messages
         *
@@ -3953,20 +3935,6 @@ class OutputPage extends ContextSource {
                $this->addWikiText( $s );
        }
 
-       /**
-        * Include jQuery core. Use this to avoid loading it multiple times
-        * before we get a usable script loader.
-        *
-        * @param array $modules List of jQuery modules which should be loaded
-        * @return array The list of modules which were not loaded.
-        * @since 1.16
-        * @deprecated since 1.17
-        */
-       public function includeJQuery( array $modules = array() ) {
-               wfDeprecated( __METHOD__, '1.17' );
-               return array();
-       }
-
        /**
         * Enables/disables TOC, doesn't override __NOTOC__
         * @param bool $flag
index 41e88c2..eaab9c8 100644 (file)
@@ -162,7 +162,7 @@ function wfPHPVersionError( $type, $mwVersion, $minimumVersionPHP, $phpVersion )
 
        $longHtml = <<<HTML
                        Please consider <a href="http://www.php.net/downloads.php">upgrading your copy of PHP</a>.
-                       PHP versions less than 5.3.0 are no longer supported by the PHP Group and will not receive
+                       PHP versions less than 5.5.0 are no longer supported by the PHP Group and will not receive
                        security or bugfix updates.
                </p>
                <p>
diff --git a/includes/PageProps.php b/includes/PageProps.php
new file mode 100644 (file)
index 0000000..0a3a324
--- /dev/null
@@ -0,0 +1,264 @@
+<?php
+/**
+ * Access to properties of a page.
+ *
+ * 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
+ */
+
+/**
+ * Gives access to properties of a page.
+ *
+ * @since 1.27
+ *
+ */
+class PageProps {
+
+       /**
+        * @var PageProps
+        */
+       private static $instance;
+
+       /**
+        * @return PageProps
+        */
+       public static function getInstance() {
+               if ( self::$instance === null ) {
+                       self::$instance = new self();
+               }
+               return self::$instance;
+       }
+
+       /** Cache parameters */
+       const CACHE_TTL = 10; // integer; TTL in seconds
+       const CACHE_SIZE = 100; // integer; max cached pages
+
+       /** Property cache */
+       private $cache = null;
+
+       /**
+        * Create a PageProps object
+        */
+       private function __construct() {
+               $this->cache = new ProcessCacheLRU( self::CACHE_SIZE );
+       }
+
+       /**
+        * Given one or more Titles and the name of a property, returns an
+        * associative array mapping page ID to property value. Pages in the
+        * provided set of Titles that do not have a value for the given
+        * property will not appear in the returned array. If a single Title
+        * is provided, it does not need to be passed in an array, but an array
+        * will always be returned. An empty array will be returned if no
+        * matching properties were found.
+        *
+        * @param array|Title $titles
+        * @param string $propertyName
+        *
+        * @return array associative array mapping page ID to property value
+        *
+        */
+       public function getProperty( $titles, $propertyName ) {
+               $values = array();
+               $goodIDs = $this->getGoodIDs( $titles );
+               $queryIDs = array();
+               foreach ( $goodIDs as $pageID ) {
+                       $propertyValue = $this->getCachedProperty( $pageID, $propertyName );
+                       if ( $propertyValue === false ) {
+                               $queryIDs[] = $pageID;
+                       } else {
+                               $values[$pageID] = $propertyValue;
+                       }
+               }
+
+               if ( $queryIDs != array() ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $result = $dbr->select(
+                               'page_props',
+                               array(
+                                       'pp_page',
+                                       'pp_value'
+                               ),
+                               array(
+                                       'pp_page' => $queryIDs,
+                                       'pp_propname' => $propertyName
+                               ),
+                               __METHOD__
+                       );
+
+                       foreach ( $result as $row ) {
+                               $pageID = $row->pp_page;
+                               $propertyValue = $row->pp_value;
+                               $this->cacheProperty( $pageID, $propertyName, $propertyValue );
+                               $values[$pageID] = $propertyValue;
+                       }
+               }
+
+               return $values;
+       }
+
+       /**
+        * Get all page property values.
+        * Given one or more Titles, returns an associative array mapping page
+        * ID to an associative array mapping property names to property
+        * values. Pages in the provided set of Titles that do not have any
+        * properties will not appear in the returned array. If a single Title
+        * is provided, it does not need to be passed in an array, but an array
+        * will always be returned. An empty array will be returned if no
+        * matching properties were found.
+        *
+        * @param array|Title $titles
+        *
+        * @return array associative array mapping page ID to property value array
+        *
+        */
+       public function getProperties( $titles ) {
+               $values = array();
+               $goodIDs = $this->getGoodIDs( $titles );
+               $queryIDs = array();
+               foreach ( $goodIDs as $pageID ) {
+                       $pageProperties = $this->getCachedProperties( $pageID );
+                       if ( $pageProperties === false ) {
+                               $queryIDs[] = $pageID;
+                       } else {
+                               $values[$pageID] = $pageProperties;
+                       }
+               }
+
+               if ( $queryIDs != array() ) {
+                       $dbr = wfGetDB( DB_SLAVE );
+                       $result = $dbr->select(
+                               'page_props',
+                               array(
+                                       'pp_page',
+                                       'pp_propname',
+                                       'pp_value'
+                               ),
+                               array(
+                                       'pp_page' => $queryIDs,
+                               ),
+                               __METHOD__
+                       );
+
+                       $currentPageID = 0;
+                       $pageProperties = array();
+                       foreach ( $result as $row ) {
+                               $pageID = $row->pp_page;
+                               if ( $currentPageID != $pageID ) {
+                                       if ( $pageProperties != array() ) {
+                                               $this->cacheProperties( $currentPageID, $pageProperties );
+                                               $values[$currentPageID] = $pageProperties;
+                                       }
+                                       $currentPageID = $pageID;
+                                       $pageProperties = array();
+                               }
+                               $pageProperties[$row->pp_propname] = $row->pp_value;
+                       }
+                       if ( $pageProperties != array() ) {
+                               $this->cacheProperties( $pageID, $pageProperties );
+                               $values[$pageID] = $pageProperties;
+                       }
+               }
+
+               return $values;
+       }
+
+       /**
+        * @param array|Title $titles
+        *
+        * @return array array of good page IDs
+        *
+        */
+       private function getGoodIDs( $titles ) {
+               $result = array();
+               if ( is_array( $titles ) ) {
+                       foreach ( $titles as $title ) {
+                               $pageID = $title->getArticleID();
+                               if ( $pageID > 0 ) {
+                                       $result[] = $pageID;
+                               }
+                       }
+               } else {
+                       $pageID = $titles->getArticleID();
+                       if ( $pageID > 0 ) {
+                               $result[] = $pageID;
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Get a property from the cache.
+        *
+        * @param int $pageID page ID of page being queried
+        * @param string $propertyName name of property being queried
+        *
+        * @return string|bool property value array or false if not found
+        *
+        */
+       private function getCachedProperty( $pageID, $propertyName ) {
+               if ( $this->cache->has( $pageID, $propertyName, self::CACHE_TTL ) ) {
+                       return $this->cache->get( $pageID, $propertyName );
+               }
+               if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
+                       $pageProperties = $this->cache->get( 0, $pageID );
+                       if ( isset( $pageProperties[$propertyName] ) ) {
+                               return $pageProperties[$propertyName];
+                       }
+               }
+               return false;
+       }
+
+       /**
+        * Get properties from the cache.
+        *
+        * @param int $pageID page ID of page being queried
+        *
+        * @return string|bool property value array or false if not found
+        *
+        */
+       private function getCachedProperties( $pageID ) {
+               if ( $this->cache->has( 0, $pageID, self::CACHE_TTL ) ) {
+                       return $this->cache->get( 0, $pageID );
+               }
+               return false;
+       }
+
+       /**
+        * Save a property to the cache.
+        *
+        * @param int $pageID page ID of page being cached
+        * @param string $propertyName name of property being cached
+        * @param mixed $propertyValue value of property
+        *
+        */
+       private function cacheProperty( $pageID, $propertyName, $propertyValue ) {
+               $this->cache->set( $pageID, $propertyName, $propertyValue );
+       }
+
+       /**
+        * Save properties to the cache.
+        *
+        * @param int $pageID page ID of page being cached
+        * @param array $pageProperties associative array of page properties to be cached
+        *
+        */
+       private function cacheProperties( $pageID, $pageProperties ) {
+               $this->cache->clear( $pageID );
+               $this->cache->set( 0, $pageID, $pageProperties );
+       }
+}
index ad25fa8..5f37e3f 100644 (file)
@@ -1470,7 +1470,7 @@ class Preferences {
                $res = self::tryFormSubmit( $formData, $form );
 
                if ( $res ) {
-                       $urlOptions = array( 'success' => 1 );
+                       $urlOptions = array();
 
                        if ( $res === 'eauth' ) {
                                $urlOptions['eauth'] = 1;
@@ -1480,7 +1480,11 @@ class Preferences {
 
                        $url = $form->getTitle()->getFullURL( $urlOptions );
 
-                       $form->getContext()->getOutput()->redirect( $url );
+                       $context = $form->getContext();
+                       // Set session data for the success message
+                       $context->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
+
+                       $context->getOutput()->redirect( $url );
                }
 
                return Status::newGood();
index 1d7ac72..bce9781 100644 (file)
@@ -1366,6 +1366,17 @@ class Revision implements IDBAccessObject {
        public function insertOn( $dbw ) {
                global $wgDefaultExternalStore, $wgContentHandlerUseDB;
 
+               // Not allowed to have rev_page equal to 0, false, etc.
+               if ( !$this->mPage ) {
+                       $title = $this->getTitle();
+                       if ( $title instanceof Title ) {
+                               $titleText = ' for page ' . $title->getPrefixedText();
+                       } else {
+                               $titleText = '';
+                       }
+                       throw new MWException( "Cannot insert revision$titleText: page ID must be nonzero" );
+               }
+
                $this->checkContentModel();
 
                $data = $this->mText;
index 4fc775f..d41e559 100644 (file)
@@ -756,7 +756,9 @@ class Sanitizer {
                        # * data-mw-<name here> is reserved for extensions (or core) if
                        #   they need to communicate some data to the client and want to be
                        #   sure that it isn't coming from an untrusted user.
-                       if ( !preg_match( '/^data-(?!ooui|mw|parsoid)/i', $attribute )
+                       # * Ensure that the attribute is not namespaced by banning
+                       #   colons.
+                       if ( !preg_match( '/^data-(?!ooui|mw|parsoid)[^:]*$/i', $attribute )
                                && !isset( $whitelist[$attribute] )
                        ) {
                                continue;
@@ -948,7 +950,6 @@ class Sanitizer {
                return $value;
        }
 
-
        /**
         * Pick apart some CSS and check it for forbidden or unsafe structures.
         * Returns a sanitized string. This sanitized string will have
index e22184f..e962a49 100644 (file)
@@ -264,6 +264,7 @@ foreach ( $wgForeignFileRepos as &$repo ) {
 }
 unset( $repo ); // no global pollution; destroy reference
 
+$rcMaxAgeDays = $wgRCMaxAge / ( 3600 * 24 );
 if ( $wgRCFilterByAge ) {
        // Trim down $wgRCLinkDays so that it only lists links which are valid
        // as determined by $wgRCMaxAge.
@@ -273,12 +274,22 @@ if ( $wgRCFilterByAge ) {
        // @codingStandardsIgnoreStart Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
        for ( $i = 0; $i < count( $wgRCLinkDays ); $i++ ) {
                // @codingStandardsIgnoreEnd
-               if ( $wgRCLinkDays[$i] >= $wgRCMaxAge / ( 3600 * 24 ) ) {
+               if ( $wgRCLinkDays[$i] >= $rcMaxAgeDays ) {
                        $wgRCLinkDays = array_slice( $wgRCLinkDays, 0, $i + 1, false );
                        break;
                }
        }
 }
+// Ensure that default user options are not invalid, since that breaks Special:Preferences
+$wgDefaultUserOptions['rcdays'] = min(
+       $wgDefaultUserOptions['rcdays'],
+       ceil( $rcMaxAgeDays )
+);
+$wgDefaultUserOptions['watchlistdays'] = min(
+       $wgDefaultUserOptions['watchlistdays'],
+       ceil( $rcMaxAgeDays )
+);
+unset( $rcMaxAgeDays );
 
 if ( $wgSkipSkin ) {
        $wgSkipSkins[] = $wgSkipSkin;
@@ -486,10 +497,25 @@ if ( $wgMaximalPasswordLength !== false ) {
        $wgPasswordPolicy['policies']['default']['MaximalPasswordLength'] = $wgMaximalPasswordLength;
 }
 
-// Backwards compatibility with deprecated alias
-// Must be before call to wfSetupSession()
-if ( $wgSessionsInMemcached ) {
-       $wgSessionsInObjectCache = true;
+// Backwards compatibility warning
+if ( !$wgSessionsInObjectCache && !$wgSessionsInMemcached ) {
+       wfDeprecated( '$wgSessionsInObjectCache = false', '1.27' );
+       if ( $wgSessionHandler ) {
+               wfDeprecated( '$wgSessionsHandler', '1.27' );
+       }
+       $cacheType = get_class( ObjectCache::getInstance( $wgSessionCacheType ) );
+       wfDebugLog(
+               "Session data will be stored in \"$cacheType\" cache with " .
+                       "expiry $wgObjectCacheSessionExpiry seconds"
+       );
+}
+$wgSessionsInObjectCache = true;
+
+if ( $wgPHPSessionHandling !== 'enable' &&
+       $wgPHPSessionHandling !== 'warn' &&
+       $wgPHPSessionHandling !== 'disable'
+) {
+       $wgPHPSessionHandling = 'warn';
 }
 
 Profiler::instance()->scopedProfileOut( $ps_default );
@@ -509,17 +535,18 @@ MWExceptionHandler::installHandler();
 
 require_once "$IP/includes/compat/normal/UtfNormalUtil.php";
 
-
 $ps_validation = Profiler::instance()->scopedProfileIn( $fname . '-validation' );
 
 // T48998: Bail out early if $wgArticlePath is non-absolute
-if ( !preg_match( '/^(https?:\/\/|\/)/', $wgArticlePath ) ) {
-       throw new FatalError(
-               'If you use a relative URL for $wgArticlePath, it must start ' .
-               'with a slash (<code>/</code>).<br><br>See ' .
-               '<a href="https://www.mediawiki.org/wiki/Manual:$wgArticlePath">' .
-               'https://www.mediawiki.org/wiki/Manual:$wgArticlePath</a>.'
-       );
+foreach ( array( 'wgArticlePath', 'wgVariantArticlePath' ) as $varName ) {
+       if ( $$varName && !preg_match( '/^(https?:\/\/|\/)/', $$varName ) ) {
+               throw new FatalError(
+                       "If you use a relative URL for \$$varName, it must start " .
+                       'with a slash (<code>/</code>).<br><br>See ' .
+                       "<a href=\"https://www.mediawiki.org/wiki/Manual:\$$varName\">" .
+                       "https://www.mediawiki.org/wiki/Manual:\$$varName</a>."
+               );
+       }
 }
 
 Profiler::instance()->scopedProfileOut( $ps_validation );
@@ -607,15 +634,13 @@ if ( !$wgDBerrorLogTZ ) {
        $wgDBerrorLogTZ = $wgLocaltimezone;
 }
 
+// initialize the request object in $wgRequest
+$wgRequest = RequestContext::getMain()->getRequest(); // BackCompat
+
 // Useful debug output
 if ( $wgCommandLineMode ) {
-       $wgRequest = new FauxRequest( array() );
-
        wfDebug( "\n\nStart command line script $self\n" );
 } else {
-       // Can't stub this one, it sets up $_GET and $_REQUEST in its constructor
-       $wgRequest = new WebRequest;
-
        $debug = "\n\nStart request {$wgRequest->getMethod()} {$wgRequest->getRequestURL()}\n";
 
        if ( $wgDebugPrintHttpHeaders ) {
@@ -647,20 +672,6 @@ Profiler::instance()->scopedProfileOut( $ps_memcached );
 // Most of the config is out, some might want to run hooks here.
 Hooks::run( 'SetupAfterCache' );
 
-$ps_session = Profiler::instance()->scopedProfileIn( $fname . '-session' );
-
-if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
-       // If session.auto_start is there, we can't touch session name
-       if ( !wfIniGetBool( 'session.auto_start' ) ) {
-               session_name( $wgSessionName ? $wgSessionName : $wgCookiePrefix . '_session' );
-       }
-
-       if ( $wgRequest->checkSessionCookie() || isset( $_COOKIE[$wgCookiePrefix . 'Token'] ) ) {
-               wfSetupSession();
-       }
-}
-
-Profiler::instance()->scopedProfileOut( $ps_session );
 $ps_globals = Profiler::instance()->scopedProfileIn( $fname . '-globals' );
 
 /**
@@ -673,6 +684,56 @@ $wgContLang->initContLang();
 // Now that variant lists may be available...
 $wgRequest->interpolateTitle();
 
+if ( !is_object( $wgAuth ) ) {
+       $wgAuth = new AuthPlugin;
+       Hooks::run( 'AuthPluginSetup', array( &$wgAuth ) );
+}
+
+// Set up the session
+$ps_session = Profiler::instance()->scopedProfileIn( $fname . '-session' );
+if ( !defined( 'MW_NO_SESSION' ) && !$wgCommandLineMode ) {
+       // If session.auto_start is there, we can't touch session name
+       if ( $wgPHPSessionHandling !== 'disable' && !wfIniGetBool( 'session.auto_start' ) ) {
+               session_name( $wgSessionName ? $wgSessionName : $wgCookiePrefix . '_session' );
+       }
+
+       // Create the SessionManager singleton and set up our session handler
+       MediaWiki\Session\PHPSessionHandler::install(
+               MediaWiki\Session\SessionManager::singleton()
+       );
+
+       // Initialize the session
+       try {
+               $session = MediaWiki\Session\SessionManager::getGlobalSession();
+       } catch ( OverflowException $ex ) {
+               if ( isset( $ex->sessionInfos ) && count( $ex->sessionInfos ) >= 2 ) {
+                       // The exception is because the request had multiple possible
+                       // sessions tied for top priority. Report this to the user.
+                       $list = array();
+                       foreach ( $ex->sessionInfos as $info ) {
+                               $list[] = $info->getProvider()->describe( $wgContLang );
+                       }
+                       $list = $wgContLang->listToText( $list );
+                       throw new HttpError( 400,
+                               Message::newFromKey( 'sessionmanager-tie', $list )->inLanguage( $wgContLang )->plain()
+                       );
+               }
+
+               // Not the one we want, rethrow
+               throw $ex;
+       }
+
+       $session->renew();
+       if ( MediaWiki\Session\PHPSessionHandler::isEnabled() &&
+               ( $session->isPersistent() || $session->shouldRememberUser() )
+       ) {
+               // Start the PHP-session for backwards compatibility
+               session_id( $session->getId() );
+               MediaWiki\quietCall( 'session_start' );
+       }
+}
+Profiler::instance()->scopedProfileOut( $ps_session );
+
 /**
  * @var User $wgUser
  */
@@ -693,11 +754,6 @@ $wgOut = RequestContext::getMain()->getOutput(); // BackCompat
  */
 $wgParser = new StubObject( 'wgParser', $wgParserConf['class'], array( $wgParserConf ) );
 
-if ( !is_object( $wgAuth ) ) {
-       $wgAuth = new AuthPlugin;
-       Hooks::run( 'AuthPluginSetup', array( &$wgAuth ) );
-}
-
 /**
  * @var Title $wgTitle
  */
@@ -729,9 +785,18 @@ foreach ( $wgExtensionFunctions as $func ) {
        Profiler::instance()->scopedProfileOut( $ps_ext_func );
 }
 
+// If the session user has a 0 id but a valid name, that means we need to
+// autocreate it.
+$sessionUser = MediaWiki\Session\SessionManager::getGlobalSession()->getUser();
+if ( $sessionUser->getId() === 0 && User::isValidUserName( $sessionUser->getName() ) ) {
+       $ps_autocreate = Profiler::instance()->scopedProfileIn( $fname . '-autocreate' );
+       MediaWiki\Session\SessionManager::autoCreateUser( $sessionUser );
+       Profiler::instance()->scopedProfileOut( $ps_autocreate );
+}
+unset( $sessionUser );
+
 wfDebug( "Fully initialised\n" );
 $wgFullyInitialised = true;
 
 Profiler::instance()->scopedProfileOut( $ps_extensions );
 Profiler::instance()->scopedProfileOut( $ps_setup );
-
index 5d8b072..e549037 100644 (file)
@@ -130,12 +130,6 @@ class Title {
         */
        public $mDefaultNamespace = NS_MAIN;
 
-       /**
-        * @var bool Is $wgUser watching this page? null if unfilled, accessed
-        * through userIsWatching()
-        */
-       protected $mWatched = null;
-
        /** @var int The page length, 0 for special pages */
        protected $mLength = -1;
 
@@ -768,16 +762,16 @@ class Title {
         * @param string $title The DB key form the title
         * @param string $fragment The link fragment (after the "#")
         * @param string $interwiki The interwiki prefix
-        * @param bool $canoncialNamespace If true, use the canonical name for
+        * @param bool $canonicalNamespace If true, use the canonical name for
         *   $ns instead of the localized version.
         * @return string The prefixed form of the title
         */
        public static function makeName( $ns, $title, $fragment = '', $interwiki = '',
-               $canoncialNamespace = false
+               $canonicalNamespace = false
        ) {
                global $wgContLang;
 
-               if ( $canoncialNamespace ) {
+               if ( $canonicalNamespace ) {
                        $namespace = MWNamespace::getCanonicalName( $ns );
                } else {
                        $namespace = $wgContLang->getNsText( $ns );
@@ -1875,25 +1869,6 @@ class Title {
                return $s;
        }
 
-       /**
-        * Is $wgUser watching this page?
-        *
-        * @deprecated since 1.20; use User::isWatched() instead.
-        * @return bool
-        */
-       public function userIsWatching() {
-               global $wgUser;
-
-               if ( is_null( $this->mWatched ) ) {
-                       if ( NS_SPECIAL == $this->mNamespace || !$wgUser->isLoggedIn() ) {
-                               $this->mWatched = false;
-                       } else {
-                               $this->mWatched = $wgUser->isWatched( $this );
-                       }
-               }
-               return $this->mWatched;
-       }
-
        /**
         * Can $user perform $action on this page?
         * This skips potentially expensive cascading permission checks
@@ -3045,20 +3020,22 @@ class Title {
                        return;
                }
 
-               $method = __METHOD__;
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->onTransactionIdle( function () use ( $dbw, $method ) {
-                       $dbw->delete(
-                               'page_restrictions',
-                               array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
-                               $method
-                       );
-                       $dbw->delete(
-                               'protected_titles',
-                               array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
-                               $method
-                       );
-               } );
+               DeferredUpdates::addUpdate( new AtomicSectionUpdate(
+                       wfGetDB( DB_MASTER ),
+                       __METHOD__,
+                       function ( IDatabase $dbw, $fname ) {
+                               $dbw->delete(
+                                       'page_restrictions',
+                                       array( 'pr_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
+                                       $fname
+                               );
+                               $dbw->delete(
+                                       'protected_titles',
+                                       array( 'pt_expiry < ' . $dbw->addQuotes( $dbw->timestamp() ) ),
+                                       $fname
+                               );
+                       }
+               ) );
        }
 
        /**
@@ -3572,7 +3549,7 @@ class Title {
                if ( $pageLang->hasVariants() ) {
                        $variants = $pageLang->getVariants();
                        foreach ( $variants as $vCode ) {
-                               $urls[] = $this->getInternalURL( '', $vCode );
+                               $urls[] = $this->getInternalURL( $vCode );
                        }
                }
 
@@ -4654,7 +4631,7 @@ class Title {
                        // Checking $wgLanguageCode hasn't changed for the benefit of unit
                        // tests.
                        $contentHandler = ContentHandler::getForTitle( $this );
-                       $langObj = wfGetLangObj( $contentHandler->getPageLanguage( $this ) );
+                       $langObj = $contentHandler->getPageLanguage( $this );
                        $this->mPageLanguage = array( $langObj->getCode(), $wgLanguageCode );
                } else {
                        $langObj = wfGetLangObj( $this->mPageLanguage[0] );
index 23eb63b..2c14618 100644 (file)
  * @file
  */
 
+use MediaWiki\Session\SessionManager;
+
 /**
  * The WebRequest class encapsulates getting at data passed in the
  * URL or via a POSTed form stripping illegal input characters and
  * normalizing Unicode sequences.
  *
- * Usually this is used via a global singleton, $wgRequest. You should
- * not create a second WebRequest object; make a FauxRequest object if
- * you want to pass arbitrary data to some function in place of the web
- * input.
- *
  * @ingroup HTTP
  */
 class WebRequest {
@@ -68,6 +65,13 @@ class WebRequest {
         */
        protected $protocol;
 
+       /**
+        * @var \\MediaWiki\\Session\\SessionId|null Session ID to use for this
+        *  request. We can't save the session directly due to reference cycles not
+        *  working too well (slow GC in Zend and never collected in HHVM).
+        */
+       protected $sessionId = null;
+
        public function __construct() {
                $this->requestTime = isset( $_SERVER['REQUEST_TIME_FLOAT'] )
                        ? $_SERVER['REQUEST_TIME_FLOAT'] : microtime( true );
@@ -643,18 +647,47 @@ class WebRequest {
        }
 
        /**
-        * Returns true if there is a session cookie set.
+        * Return the session for this request
+        * @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.
+        * @return MediaWiki\\Session\\Session
+        */
+       public function getSession() {
+               if ( $this->sessionId !== null ) {
+                       $session = SessionManager::singleton()->getSessionById( (string)$this->sessionId, true, $this );
+                       if ( $session ) {
+                               return $session;
+                       }
+               }
+
+               $session = SessionManager::singleton()->getSessionForRequest( $this );
+               $this->sessionId = $session->getSessionId();
+               return $session;
+       }
+
+       /**
+        * Set the session for this request
+        * @since 1.27
+        * @private For use by MediaWiki\\Session classes only
+        * @param MediaWiki\\Session\\SessionId $sessionId
+        */
+       public function setSessionId( MediaWiki\Session\SessionId $sessionId ) {
+               $this->sessionId = $sessionId;
+       }
+
+       /**
+        * Returns true if the request has a persistent session.
         * This does not necessarily mean that the user is logged in!
         *
-        * If you want to check for an open session, use session_id()
-        * instead; that will also tell you if the session was opened
-        * during the current request (in which case the cookie will
-        * be sent back to the client at the end of the script run).
-        *
+        * @deprecated since 1.27, use
+        *  \\MediaWiki\\Session\\SessionManager::singleton()->getPersistedSessionId()
+        *  instead.
         * @return bool
         */
        public function checkSessionCookie() {
-               return isset( $_COOKIE[session_name()] );
+               wfDeprecated( __METHOD__, '1.27' );
+               return SessionManager::singleton()->getPersistedSessionId( $this ) !== null;
        }
 
        /**
@@ -731,47 +764,27 @@ class WebRequest {
                return wfExpandUrl( $this->getRequestURL(), PROTO_CURRENT );
        }
 
-       /**
-        * Take an arbitrary query and rewrite the present URL to include it
-        * @deprecated Use appendQueryValue/appendQueryArray instead
-        * @param string $query Query string fragment; do not include initial '?'
-        * @return string
-        */
-       public function appendQuery( $query ) {
-               wfDeprecated( __METHOD__, '1.25' );
-               return $this->appendQueryArray( wfCgiToArray( $query ) );
-       }
-
        /**
         * @param string $key
         * @param string $value
-        * @param bool $onlyquery [deprecated]
         * @return string
         */
-       public function appendQueryValue( $key, $value, $onlyquery = true ) {
-               return $this->appendQueryArray( array( $key => $value ), $onlyquery );
+       public function appendQueryValue( $key, $value ) {
+               return $this->appendQueryArray( array( $key => $value ) );
        }
 
        /**
         * Appends or replaces value of query variables.
         *
         * @param array $array Array of values to replace/add to query
-        * @param bool $onlyquery Whether to only return the query string
-        *  and not the complete URL [deprecated]
         * @return string
         */
-       public function appendQueryArray( $array, $onlyquery = true ) {
-               global $wgTitle;
+       public function appendQueryArray( $array ) {
                $newquery = $this->getQueryValues();
                unset( $newquery['title'] );
                $newquery = array_merge( $newquery, $array );
-               $query = wfArrayToCgi( $newquery );
-               if ( !$onlyquery ) {
-                       wfDeprecated( __METHOD__, '1.25' );
-                       return $wgTitle->getLocalURL( $query );
-               }
 
-               return $query;
+               return wfArrayToCgi( $newquery );
        }
 
        /**
@@ -932,26 +945,25 @@ class WebRequest {
        }
 
        /**
-        * Get data from $_SESSION
+        * Get data from the session
         *
-        * @param string $key Name of key in $_SESSION
+        * @note Prefer $this->getSession() instead if making multiple calls.
+        * @param string $key Name of key in the session
         * @return mixed
         */
        public function getSessionData( $key ) {
-               if ( !isset( $_SESSION[$key] ) ) {
-                       return null;
-               }
-               return $_SESSION[$key];
+               return $this->getSession()->get( $key );
        }
 
        /**
         * Set session data
         *
-        * @param string $key Name of key in $_SESSION
+        * @note Prefer $this->getSession() instead if making multiple calls.
+        * @param string $key Name of key in the session
         * @param mixed $data
         */
        public function setSessionData( $key, $data ) {
-               $_SESSION[$key] = $data;
+               return $this->getSession()->set( $key, $data );
        }
 
        /**
@@ -1175,294 +1187,3 @@ HTML;
                $this->ip = $ip;
        }
 }
-
-/**
- * WebRequest clone which takes values from a provided array.
- *
- * @ingroup HTTP
- */
-class FauxRequest extends WebRequest {
-       private $wasPosted = false;
-       private $session = array();
-       private $requestUrl;
-       protected $cookies = array();
-
-       /**
-        * @param array $data Array of *non*-urlencoded key => value pairs, the
-        *   fake GET/POST values
-        * @param bool $wasPosted Whether to treat the data as POST
-        * @param array|null $session Session array or null
-        * @param string $protocol 'http' or 'https'
-        * @throws MWException
-        */
-       public function __construct( $data = array(), $wasPosted = false,
-               $session = null, $protocol = 'http'
-       ) {
-               $this->requestTime = microtime( true );
-
-               if ( is_array( $data ) ) {
-                       $this->data = $data;
-               } else {
-                       throw new MWException( "FauxRequest() got bogus data" );
-               }
-               $this->wasPosted = $wasPosted;
-               if ( $session ) {
-                       $this->session = $session;
-               }
-               $this->protocol = $protocol;
-       }
-
-       /**
-        * Initialise the header list
-        */
-       protected function initHeaders() {
-               // Nothing to init
-       }
-
-       /**
-        * @param string $name
-        * @param string $default
-        * @return string
-        */
-       public function getText( $name, $default = '' ) {
-               # Override; don't recode since we're using internal data
-               return (string)$this->getVal( $name, $default );
-       }
-
-       /**
-        * @return array
-        */
-       public function getValues() {
-               return $this->data;
-       }
-
-       /**
-        * @return array
-        */
-       public function getQueryValues() {
-               if ( $this->wasPosted ) {
-                       return array();
-               } else {
-                       return $this->data;
-               }
-       }
-
-       public function getMethod() {
-               return $this->wasPosted ? 'POST' : 'GET';
-       }
-
-       /**
-        * @return bool
-        */
-       public function wasPosted() {
-               return $this->wasPosted;
-       }
-
-       public function getCookie( $key, $prefix = null, $default = null ) {
-               if ( $prefix === null ) {
-                       global $wgCookiePrefix;
-                       $prefix = $wgCookiePrefix;
-               }
-               $name = $prefix . $key;
-               return isset( $this->cookies[$name] ) ? $this->cookies[$name] : $default;
-       }
-
-       /**
-        * @since 1.26
-        * @param string $name Unprefixed name of the cookie to set
-        * @param string|null $value Value of the cookie to set
-        * @param string|null $prefix Cookie prefix. Defaults to $wgCookiePrefix
-        */
-       public function setCookie( $key, $value, $prefix = null ) {
-               $this->setCookies( array( $key => $value ), $prefix );
-       }
-
-       /**
-        * @since 1.26
-        * @param array $cookies
-        * @param string|null $prefix Cookie prefix. Defaults to $wgCookiePrefix
-        */
-       public function setCookies( $cookies, $prefix = null ) {
-               if ( $prefix === null ) {
-                       global $wgCookiePrefix;
-                       $prefix = $wgCookiePrefix;
-               }
-               foreach ( $cookies as $key => $value ) {
-                       $name = $prefix . $key;
-                       $this->cookies[$name] = $value;
-               }
-       }
-
-       public function checkSessionCookie() {
-               return false;
-       }
-
-       /**
-        * @since 1.25
-        */
-       public function setRequestURL( $url ) {
-               $this->requestUrl = $url;
-       }
-
-       /**
-        * @since 1.25 MWException( "getRequestURL not implemented" )
-        * no longer thrown.
-        */
-       public function getRequestURL() {
-               if ( $this->requestUrl === null ) {
-                       throw new MWException( 'Request URL not set' );
-               }
-               return $this->requestUrl;
-       }
-
-       public function getProtocol() {
-               return $this->protocol;
-       }
-
-       /**
-        * @param string $name
-        * @param string $val
-        */
-       public function setHeader( $name, $val ) {
-               $this->setHeaders( array( $name => $val ) );
-       }
-
-       /**
-        * @since 1.26
-        * @param array $headers
-        */
-       public function setHeaders( $headers ) {
-               foreach ( $headers as $name => $val ) {
-                       $name = strtoupper( $name );
-                       $this->headers[$name] = $val;
-               }
-       }
-
-       /**
-        * @param string $key
-        * @return array|null
-        */
-       public function getSessionData( $key ) {
-               if ( isset( $this->session[$key] ) ) {
-                       return $this->session[$key];
-               }
-               return null;
-       }
-
-       /**
-        * @param string $key
-        * @param array $data
-        */
-       public function setSessionData( $key, $data ) {
-               $this->session[$key] = $data;
-       }
-
-       /**
-        * @return array|mixed|null
-        */
-       public function getSessionArray() {
-               return $this->session;
-       }
-
-       /**
-        * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
-        * @return string
-        */
-       public function getRawQueryString() {
-               return '';
-       }
-
-       /**
-        * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
-        * @return string
-        */
-       public function getRawPostString() {
-               return '';
-       }
-
-       /**
-        * FauxRequests shouldn't depend on raw request data (but that could be implemented here)
-        * @return string
-        */
-       public function getRawInput() {
-               return '';
-       }
-
-       /**
-        * @param array $extWhitelist
-        * @return bool
-        */
-       public function checkUrlExtension( $extWhitelist = array() ) {
-               return true;
-       }
-
-       /**
-        * @return string
-        */
-       protected function getRawIP() {
-               return '127.0.0.1';
-       }
-}
-
-/**
- * Similar to FauxRequest, but only fakes URL parameters and method
- * (POST or GET) and use the base request for the remaining stuff
- * (cookies, session and headers).
- *
- * @ingroup HTTP
- * @since 1.19
- */
-class DerivativeRequest extends FauxRequest {
-       private $base;
-
-       /**
-        * @param WebRequest $base
-        * @param array $data Array of *non*-urlencoded key => value pairs, the
-        *   fake GET/POST values
-        * @param bool $wasPosted Whether to treat the data as POST
-        */
-       public function __construct( WebRequest $base, $data, $wasPosted = false ) {
-               $this->base = $base;
-               parent::__construct( $data, $wasPosted );
-       }
-
-       public function getCookie( $key, $prefix = null, $default = null ) {
-               return $this->base->getCookie( $key, $prefix, $default );
-       }
-
-       public function checkSessionCookie() {
-               return $this->base->checkSessionCookie();
-       }
-
-       public function getHeader( $name, $flags = 0 ) {
-               return $this->base->getHeader( $name, $flags );
-       }
-
-       public function getAllHeaders() {
-               return $this->base->getAllHeaders();
-       }
-
-       public function getSessionData( $key ) {
-               return $this->base->getSessionData( $key );
-       }
-
-       public function setSessionData( $key, $data ) {
-               $this->base->setSessionData( $key, $data );
-       }
-
-       public function getAcceptLang() {
-               return $this->base->getAcceptLang();
-       }
-
-       public function getIP() {
-               return $this->base->getIP();
-       }
-
-       public function getProtocol() {
-               return $this->base->getProtocol();
-       }
-
-       public function getElapsedTime() {
-               return $this->base->getElapsedTime();
-       }
-}
index c99b0e3..0881a16 100644 (file)
@@ -126,7 +126,7 @@ class WebRequestUpload {
                        return true;
                }
 
-               $contentLength = $this->request->getHeader( 'CONTENT_LENGTH' );
+               $contentLength = $this->request->getHeader( 'CONTENT-LENGTH' );
                $maxPostSize = wfShorthandToInteger(
                        ini_get( 'post_max_size' ) ?: ini_get( 'hhvm.server.max_post_size' ),
                        0
index 26fb20f..f14cf22 100644 (file)
  */
 class WebResponse {
 
+       /** @var array Used to record set cookies, because PHP's setcookie() will
+        * happily send an identical Set-Cookie to the client.
+        */
+       protected static $setCookies = array();
+
        /**
         * Output an HTTP header, wrapper for PHP's header()
         * @param string $string Header to output
@@ -62,6 +67,15 @@ class WebResponse {
                HttpStatus::header( $code );
        }
 
+       /**
+        * Test if headers have been sent
+        * @since 1.27
+        * @return bool
+        */
+       public function headersSent() {
+               return headers_sent();
+       }
+
        /**
         * Set the browser cookie
         * @param string $name The name of the cookie.
@@ -115,25 +129,26 @@ class WebResponse {
                $func = $options['raw'] ? 'setrawcookie' : 'setcookie';
 
                if ( Hooks::run( 'WebResponseSetCookie', array( &$name, &$value, &$expire, $options ) ) ) {
-                       wfDebugLog( 'cookie',
-                               $func . ': "' . implode( '", "',
-                                       array(
-                                               $options['prefix'] . $name,
-                                               $value,
-                                               $expire,
-                                               $options['path'],
-                                               $options['domain'],
-                                               $options['secure'],
-                                               $options['httpOnly'] ) ) . '"' );
-
-                       call_user_func( $func,
-                               $options['prefix'] . $name,
-                               $value,
-                               $expire,
-                               $options['path'],
-                               $options['domain'],
-                               $options['secure'],
-                               $options['httpOnly'] );
+                       $cookie = $options['prefix'] . $name;
+                       $data = array(
+                               (string)$cookie,
+                               (string)$value,
+                               (int)$expire,
+                               (string)$options['path'],
+                               (string)$options['domain'],
+                               (bool)$options['secure'],
+                               (bool)$options['httpOnly'],
+                       );
+                       if ( !isset( self::$setCookies[$cookie] ) ||
+                               self::$setCookies[$cookie] !== array( $func, $data )
+                       ) {
+                               wfDebugLog( 'cookie', $func . ': "' . implode( '", "', $data ) . '"' );
+                               if ( call_user_func_array( $func, $data ) ) {
+                                       self::$setCookies[$cookie] = array( $func, $data );
+                               }
+                       } else {
+                               wfDebugLog( 'cookie', 'already set ' . $func . ': "' . implode( '", "', $data ) . '"' );
+                       }
                }
        }
 
@@ -156,7 +171,7 @@ class WebResponse {
  */
 class FauxResponse extends WebResponse {
        private $headers;
-       private $cookies;
+       private $cookies = array();
        private $code;
 
        /**
@@ -192,6 +207,10 @@ class FauxResponse extends WebResponse {
                $this->code = intval( $code );
        }
 
+       public function headersSent() {
+               return false;
+       }
+
        /**
         * @param string $key The name of the header to get (case insensitive).
         * @return string|null The header value (if set); null otherwise.
index c5e7457..fb6c3e6 100644 (file)
@@ -40,7 +40,6 @@ if ( function_exists( 'get_magic_quotes_gpc' ) && get_magic_quotes_gpc() ) {
                . 'for help on how to disable magic quotes.' );
 }
 
-
 # bug 15461: Make IE8 turn off content sniffing. Everybody else should ignore this
 # We're adding it here so that it's *always* set, even for alternate entry
 # points and when $wgOut gets disabled or overridden.
@@ -91,7 +90,6 @@ if ( file_exists( "$IP/StartProfiler.php" ) ) {
        require "$IP/StartProfiler.php";
 }
 
-
 # Load default settings
 require_once "$IP/includes/DefaultSettings.php";
 
@@ -141,7 +139,6 @@ if ( defined( 'MW_CONFIG_CALLBACK' ) ) {
        require_once MW_CONFIG_FILE;
 }
 
-
 # Initialise output buffering
 # Check that there is no previous output or previously set up buffers, because
 # that would cause us to potentially mix gzip and non-gzip output, creating a
index 11f14db..63301ac 100644 (file)
@@ -91,7 +91,7 @@ class Xml {
        public static function elementClean( $element, $attribs = array(), $contents = '' ) {
                global $wgContLang;
                if ( $attribs ) {
-                       $attribs = array_map( array( 'UtfNormal', 'cleanUp' ), $attribs );
+                       $attribs = array_map( array( 'UtfNormal\Validator', 'cleanUp' ), $attribs );
                }
                if ( $contents ) {
                        $contents = $wgContLang->normalize( $contents );
index 9e7d12c..6c768ff 100644 (file)
@@ -294,6 +294,7 @@ $zh2Hant = array(
 '卖' => '賣',
 '卢' => '盧',
 '卤' => '鹵',
+'卧' => '臥',
 '卫' => '衛',
 '却' => '卻',
 '厂' => '廠',
@@ -3424,6 +3425,7 @@ $zh2Hant = array(
 '干股' => '乾股',
 '干肥' => '乾肥',
 '干脆' => '乾脆',
+'干脆面' => '乾脆麵',
 '干花' => '乾花',
 '干刍' => '乾芻',
 '干苔' => '乾苔',
@@ -3564,6 +3566,7 @@ $zh2Hant = array(
 '于慧' => '于慧',
 '于成龍' => '于成龍',
 '于成龙' => '于成龍',
+'于承惠' => '于承惠',
 '于振' => '于振',
 '于振武' => '于振武',
 '于敏' => '于敏',
@@ -3644,6 +3647,7 @@ $zh2Hant = array(
 '于赠' => '于贈',
 '于越' => '于越',
 '于軍' => '于軍',
+'于逸堯' => '于逸堯',
 '于道泉' => '于道泉',
 '于远伟' => '于遠偉',
 '于遠偉' => '于遠偉',
@@ -3829,6 +3833,8 @@ $zh2Hant = array(
 '信托' => '信託',
 '修杰楷' => '修杰楷',
 '修杰麟' => '修杰麟',
+'修筑前' => '修築前',
+'修筑后' => '修築後',
 '修胡刀' => '修鬍刀',
 '俯冲' => '俯衝',
 '个月里' => '個月裡',
@@ -4112,7 +4118,6 @@ $zh2Hant = array(
 '削面' => '削麵',
 '克剥' => '剋剝',
 '克扣' => '剋扣',
-'克星' => '剋星',
 '克期' => '剋期',
 '克死' => '剋死',
 '克薄' => '剋薄',
@@ -4235,6 +4240,7 @@ $zh2Hant = array(
 '去山里' => '去山裡',
 '参数只' => '參數只',
 '参数里' => '參數裡',
+'反反复复' => '反反覆覆',
 '反应制得' => '反應製得',
 '反朴' => '反樸',
 '反冲' => '反衝',
@@ -4736,14 +4742,10 @@ $zh2Hant = array(
 '委托书' => '委託書',
 '奸夫' => '姦夫',
 '奸妇' => '姦婦',
-'奸宄' => '姦宄',
 '奸情' => '姦情',
 '奸杀' => '姦殺',
 '奸污' => '姦污',
 '奸淫' => '姦淫',
-'奸猾' => '姦猾',
-'奸细' => '姦細',
-'奸邪' => '姦邪',
 '威棱' => '威稜',
 '婢仆' => '婢僕',
 '嫁祸于' => '嫁禍於',
@@ -4758,6 +4760,7 @@ $zh2Hant = array(
 '子里' => '子裡',
 '子里甲' => '子里甲',
 '字汇' => '字彙',
+'字母后' => '字母後',
 '字码表' => '字碼表',
 '字里行间' => '字裡行間',
 '存折' => '存摺',
@@ -5056,8 +5059,11 @@ $zh2Hant = array(
 '廢后' => '廢后',
 '广征' => '廣徵',
 '广舍' => '廣捨',
+'广播里' => '廣播裡',
 '延历' => '延曆',
 '建于' => '建於',
+'建筑前' => '建築前',
+'建筑后' => '建築後',
 '弄干' => '弄乾',
 '弄丑' => '弄醜',
 '弄脏胸' => '弄髒胸',
@@ -5131,6 +5137,7 @@ $zh2Hant = array(
 '影后' => '影后',
 '影相吊' => '影相弔',
 '役于' => '役於',
+'往复式' => '往復式',
 '往日无仇' => '往日無讎',
 '往里' => '往裡',
 '待复' => '待覆',
@@ -5405,7 +5412,6 @@ $zh2Hant = array(
 '欲令智昏' => '慾令智昏',
 '欲壑难填' => '慾壑難填',
 '欲念' => '慾念',
-'欲望' => '慾望',
 '欲海' => '慾海',
 '欲火' => '慾火',
 '欲障' => '慾障',
@@ -5472,6 +5478,7 @@ $zh2Hant = array(
 '手表面' => '手表面',
 '手里剑' => '手裏劍',
 '手里' => '手裡',
+'手游' => '手遊',
 '手表' => '手錶',
 '手链' => '手鍊',
 '手松' => '手鬆',
@@ -6227,6 +6234,7 @@ $zh2Hant = array(
 '水来汤里去' => '水來湯裡去',
 '水准' => '水準',
 '水无怜奈' => '水無怜奈',
+'水表示' => '水表示',
 '水表面' => '水表面',
 '水里' => '水裡',
 '水里商工' => '水里商工',
@@ -6499,6 +6507,7 @@ $zh2Hant = array(
 '沈丹客运' => '瀋丹客運',
 '沈丹线' => '瀋丹線',
 '沈丹铁路' => '瀋丹鐵路',
+'沈丹高' => '瀋丹高',
 '沈北' => '瀋北',
 '沈吉' => '瀋吉',
 '沈大线' => '瀋大線',
@@ -6714,7 +6723,6 @@ $zh2Hant = array(
 '发松' => '發鬆',
 '发面' => '發麵',
 '白干儿' => '白乾兒',
-'白子里' => '白子里',
 '白术' => '白朮',
 '白朴' => '白樸',
 '白净面皮' => '白淨面皮',
@@ -6735,6 +6743,7 @@ $zh2Hant = array(
 '百只足夠' => '百只足夠',
 '百周后' => '百周後',
 '百天后' => '百天後',
+'百子里' => '百子里',
 '百年' => '百年',
 '百拙千丑' => '百拙千醜',
 '百科里' => '百科裡',
@@ -6970,6 +6979,7 @@ $zh2Hant = array(
 '窗明几净' => '窗明几淨',
 '窗帘' => '窗簾',
 '窝里' => '窩裡',
+'窝里斗' => '窩裡鬥',
 '穷于' => '窮於',
 '穷追不舍' => '窮追不捨',
 '穷发' => '窮髮',
@@ -7280,6 +7290,7 @@ $zh2Hant = array(
 '聚药雄蕊' => '聚葯雄蕊',
 '闻风后' => '聞風後',
 '联系' => '聯繫',
+'声母后' => '聲母後',
 '听于' => '聽於',
 '肉干' => '肉乾',
 '肉欲' => '肉慾',
@@ -7970,11 +7981,13 @@ $zh2Hant = array(
 '警报钟' => '警報鐘',
 '警示钟' => '警示鐘',
 '警钟' => '警鐘',
+'译制' => '譯製',
 '译注' => '譯註',
 '护发' => '護髮',
 '变征' => '變徵',
 '变丑' => '變醜',
 '仇隙' => '讎隙',
+'赞一个' => '讚一個',
 '赞不绝口' => '讚不絕口',
 '赞佩' => '讚佩',
 '赞呗' => '讚唄',
@@ -8593,7 +8606,6 @@ $zh2Hant = array(
 '防御' => '防禦',
 '防范' => '防範',
 '防锈' => '防鏽',
-'防台' => '防颱',
 '阻于' => '阻於',
 '阿里' => '阿里',
 '附于' => '附於',
@@ -8683,6 +8695,7 @@ $zh2Hant = array(
 '电码表' => '電碼表',
 '电冲' => '電衝',
 '电视台风' => '電視台風',
+'电视里' => '電視裡',
 '电表' => '電錶',
 '电钟' => '電鐘',
 '震栗' => '震慄',
@@ -9345,7 +9358,6 @@ $zh2Hant = array(
 '齿危发秀' => '齒危髮秀',
 '齿落发白' => '齒落髮白',
 '齿发' => '齒髮',
-'出儿' => '齣兒',
 '龙岩' => '龍巖',
 '龙卷' => '龍捲',
 '龙眼干' => '龍眼乾',
@@ -9354,6 +9366,10 @@ $zh2Hant = array(
 '龙斗虎伤' => '龍鬥虎傷',
 '龜山庄' => '龜山庄',
 '龟鉴' => '龜鑑',
+',并力' => ',並力',
+',并力攻' => ',并力攻',
+',并力討' => ',并力討',
+',并力讨' => ',并力討',
 ',个中' => ',箇中',
 );
 
@@ -13874,6 +13890,8 @@ $zh2TW = array(
 '傅里叶' => '傅立葉',
 '光盘' => '光碟',
 '光驱' => '光碟機',
+'开普勒' => '克卜勒',
+'開普勒' => '克卜勒',
 '克罗地亚' => '克羅埃西亞',
 '克羅地亞' => '克羅埃西亞',
 '克里斯托弗' => '克里斯多福',
@@ -13896,6 +13914,7 @@ $zh2TW = array(
 '包豪斯' => '包浩斯',
 '北朝鲜' => '北韓',
 '局域网' => '區域網',
+'局域网络' => '區域網路',
 '十杆' => '十桿',
 '特立尼达和托巴哥' => '千里達托貝哥',
 '特立尼達和多巴哥' => '千里達托貝哥',
@@ -14100,6 +14119,8 @@ $zh2TW = array(
 '摩根士丹利' => '摩根史坦利',
 '台球' => '撞球',
 '攻打' => '攻打',
+'数字化' => '數位化',
+'數碼化' => '數位化',
 '数字技术' => '數位技術',
 '數碼技術' => '數位技術',
 '数字照相机' => '數位照相機',
@@ -14150,6 +14171,7 @@ $zh2TW = array(
 '撒切尔' => '柴契爾',
 '格林納達' => '格瑞那達',
 '格林纳达' => '格瑞那達',
+'桃金娘' => '桃金孃',
 '台式电脑' => '桌上型電腦',
 '乒乓' => '桌球',
 '乒乓球' => '桌球',
@@ -14291,6 +14313,7 @@ $zh2TW = array(
 '互联网' => '網際網路',
 '互联网络' => '網際網路',
 '互聯網' => '網際網路',
+'互聯網絡' => '網際網路',
 '因特网' => '網際網路',
 '系着' => '繫著',
 '卢瓦尔' => '羅亞爾',
@@ -14311,7 +14334,6 @@ $zh2TW = array(
 '聖馬力諾' => '聖馬利諾',
 '肯尼亚' => '肯亞',
 '氨基酸' => '胺基酸',
-'卧' => '臥',
 '自由泳' => '自由式',
 '三藩市' => '舊金山',
 '艾森豪威尔' => '艾森豪',
@@ -14403,8 +14425,6 @@ $zh2TW = array(
 '普密蓬' => '蒲美蓬',
 '布隆迪' => '蒲隆地',
 '圭亚那' => '蓋亞那',
-'开曼群岛' => '蓋曼群島',
-'開曼群島' => '蓋曼群島',
 '肖斯塔科维奇' => '蕭士塔高維奇',
 '蕭士達高維契' => '蕭士塔高維奇',
 '肖邦' => '蕭邦',
@@ -14429,7 +14449,6 @@ $zh2TW = array(
 '埃塞俄比亚' => '衣索比亞',
 '埃塞俄比亞' => '衣索比亞',
 '克隆人' => '複製人',
-'囯际象棋' => '西洋棋',
 '国际象棋' => '西洋棋',
 '國際象棋' => '西洋棋',
 '赫梯' => '西臺',
@@ -14454,7 +14473,9 @@ $zh2TW = array(
 '信息时代' => '資訊時代',
 '信息论' => '資訊理論',
 '乔布斯' => '賈伯斯',
+'本·拉登' => '賓·拉登',
 '宾西法尼亚' => '賓夕法尼亞',
+'本拉登' => '賓拉登',
 '利比里亚' => '賴比瑞亞',
 '利比里亞' => '賴比瑞亞',
 '莱索托' => '賴索托',
@@ -15486,6 +15507,7 @@ $zh2HK = array(
 '坎城' => '康城',
 '戛纳' => '康城',
 '庙里' => '廟裏',
+'广播里' => '廣播裏',
 '強占' => '強佔',
 '强占' => '強佔',
 '约翰斯顿岛' => '強斯頓環礁',
@@ -15889,6 +15911,8 @@ $zh2HK = array(
 '数字照相机' => '数碼照相機',
 '數位照相機' => '数碼照相機',
 '數著' => '數着',
+'数字化' => '數碼化',
+'數位化' => '數碼化',
 '数字技术' => '數碼技術',
 '數位技術' => '數碼技術',
 '數位相機' => '數碼相機',
@@ -15994,7 +16018,9 @@ $zh2HK = array(
 '朝著述' => '朝著述',
 '朝著錄' => '朝著錄',
 '板球' => '木球',
+'賓·拉登' => '本·拉登',
 '班傑明' => '本傑明',
+'賓拉登' => '本拉登',
 '本著' => '本着',
 '本著作' => '本著作',
 '本著名' => '本著名',
@@ -16559,6 +16585,7 @@ $zh2HK = array(
 '穿著述' => '穿著述',
 '穿著錄' => '穿著錄',
 '窝里' => '窩裏',
+'窝里斗' => '窩裏鬥',
 '立著' => '立着',
 '立著《' => '立著《',
 '立著作' => '立著作',
@@ -17292,7 +17319,7 @@ $zh2HK = array(
 '閉著者' => '閉著者',
 '閉著述' => '閉著述',
 '閉著錄' => '閉著錄',
-'蓋曼群島' => '開曼群島',
+'克卜勒' => '開普勒',
 '開著' => '開着',
 '開著作' => '開著作',
 '開著名' => '開著名',
@@ -17409,6 +17436,7 @@ $zh2HK = array(
 '电梯里' => '電梯裏',
 '电脑程序' => '電腦程式',
 '计算机程序' => '電腦程式',
+'电视里' => '電視裏',
 '霄裡' => '霄裡',
 '荷姆茲' => '霍爾木茲',
 '雾里' => '霧裏',
@@ -18186,7 +18214,6 @@ $zh2CN = array(
 '因著稱' => '因著称',
 '因著者' => '因著者',
 '因著述' => '因著述',
-'西洋棋' => '囯际象棋',
 '困著' => '困着',
 '困著書' => '困著书',
 '困著作' => '困著作',
@@ -18205,6 +18232,7 @@ $zh2CN = array(
 '圍著述' => '围著述',
 '韌體' => '固件',
 '固著' => '固着',
+'西洋棋' => '国际象棋',
 '土魯斯' => '图卢兹',
 '吐瓦魯' => '图瓦卢',
 '原子筆' => '圆珠笔',
@@ -18340,6 +18368,7 @@ $zh2CN = array(
 '奈及利亞' => '尼日利亚',
 '尼日爾' => '尼日尔',
 '區域網' => '局域网',
+'區域網路' => '局域网络',
 '螢幕' => '屏幕',
 '展著' => '展着',
 '展著書' => '展著书',
@@ -18403,7 +18432,6 @@ $zh2CN = array(
 '應著述' => '应著述',
 '建帳' => '建账',
 '克卜勒' => '开普勒',
-'蓋曼群島' => '开曼群岛',
 '開著' => '开着',
 '開著書' => '开著书',
 '開著作' => '开著作',
@@ -18805,8 +18833,8 @@ $zh2CN = array(
 '散布著' => '散布着',
 '數位訊號' => '数字信号',
 '數碼訊號' => '数字信号',
+'數位化' => '数字化',
 '數位技術' => '数字技术',
-'數碼技術' => '数字技术',
 '數位電視' => '数字电视',
 '數碼電視' => '数字电视',
 '資料庫' => '数据库',
@@ -18913,7 +18941,9 @@ $zh2CN = array(
 '朝著稱' => '朝著称',
 '朝著者' => '朝著者',
 '朝著述' => '朝著述',
+'賓·拉登' => '本·拉登',
 '本份' => '本分',
+'賓拉登' => '本拉登',
 '本本份份' => '本本分分',
 '班傑明' => '本杰明',
 '本著' => '本着',
index 6ddc596..97d7132 100644 (file)
@@ -417,4 +417,13 @@ abstract class Action {
                        wfTransactionalTimeLimit();
                }
        }
+
+       /**
+        * Indicates whether this action may perform database writes
+        * @return bool
+        * @since 1.27
+        */
+       public function doesWrites() {
+               return false;
+       }
 }
index 841a94d..cb57d3e 100644 (file)
@@ -53,4 +53,8 @@ class DeleteAction extends FormlessAction {
                $this->addHelpLink( 'Help:Sysop deleting and undeleting' );
                $this->page->delete();
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index eb53f19..643d1c4 100644 (file)
@@ -58,4 +58,8 @@ class EditAction extends FormlessAction {
                        $editor->edit();
                }
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index aa201d7..55b9b99 100644 (file)
@@ -127,4 +127,8 @@ abstract class FormAction extends Action {
                        $this->onSuccess();
                }
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 7389ae2..6f33db7 100644 (file)
@@ -203,18 +203,10 @@ class InfoAction extends FormlessAction {
 
                $pageCounts = $this->pageCounts( $this->page );
 
-               // Get page properties
-               $dbr = wfGetDB( DB_SLAVE );
-               $result = $dbr->select(
-                       'page_props',
-                       array( 'pp_propname', 'pp_value' ),
-                       array( 'pp_page' => $id ),
-                       __METHOD__
-               );
-
                $pageProperties = array();
-               foreach ( $result as $row ) {
-                       $pageProperties[$row->pp_propname] = $row->pp_value;
+               $props = PageProps::getInstance()->getProperties( $title );
+               if ( isset( $props[$id] ) ) {
+                       $pageProperties = $props[$id];
                }
 
                // Basic information
index 4016f67..b8a7616 100644 (file)
@@ -63,8 +63,14 @@ class MarkpatrolledAction extends FormlessAction {
                }
 
                # It would be nice to see where the user had actually come from, but for now just guess
-               $returnto = $rc->getAttribute( 'rc_type' ) == RC_NEW ? 'Newpages' : 'Recentchanges';
-               $return = SpecialPage::getTitleFor( $returnto );
+               if ( $rc->getAttribute( 'rc_type' ) == RC_NEW ) {
+                       $returnTo = 'Newpages';
+               } elseif ( $rc->getAttribute( 'rc_log_type' ) == 'upload' ) {
+                       $returnTo = 'Newfiles';
+               } else {
+                       $returnTo = 'Recentchanges';
+               }
+               $return = SpecialPage::getTitleFor( $returnTo );
 
                if ( in_array( array( 'markedaspatrollederror-noautopatrol' ), $errors ) ) {
                        $this->getOutput()->setPageTitle( $this->msg( 'markedaspatrollederror' ) );
@@ -83,4 +89,8 @@ class MarkpatrolledAction extends FormlessAction {
                $this->getOutput()->addWikiMsg( 'markedaspatrolledtext', $rc->getTitle()->getPrefixedText() );
                $this->getOutput()->returnToMain( null, $return );
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 48909cf..126daa0 100644 (file)
@@ -51,4 +51,8 @@ class ProtectAction extends FormlessAction {
 
                $this->page->protect();
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 7e77846..183c3b9 100644 (file)
@@ -97,4 +97,8 @@ class PurgeAction extends FormAction {
        public function onSuccess() {
                $this->getOutput()->redirect( $this->getTitle()->getFullURL( $this->redirectParams ) );
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 69cd7aa..b371848 100644 (file)
@@ -83,7 +83,8 @@ class RawAction extends FormlessAction {
                $response->header( 'Content-type: ' . $contentType . '; charset=UTF-8' );
                // Output may contain user-specific data;
                // vary generated content for open sessions on private wikis
-               $privateCache = !User::isEveryoneAllowed( 'read' ) && ( $smaxage == 0 || session_id() != '' );
+               $privateCache = !User::isEveryoneAllowed( 'read' ) &&
+                       ( $smaxage == 0 || MediaWiki\Session\SessionManager::getGlobalSession()->isPersistent() );
                // Don't accidentally cache cookies if user is logged in (T55032)
                $privateCache = $privateCache || $this->getUser()->isLoggedIn();
                $mode = $privateCache ? 'private' : 'public';
index 4885a31..8a54c39 100644 (file)
@@ -151,4 +151,8 @@ class RevertAction extends FormAction {
        protected function getDescription() {
                return OutputPage::buildBacklinkSubtitle( $this->getTitle() );
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index dbcb848..7df42b3 100644 (file)
@@ -58,4 +58,8 @@ class RevisiondeleteAction extends FormlessAction {
                $special->getContext()->setTitle( $special->getPageTitle() );
                $special->run( '' );
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 0404856..cc94fd3 100644 (file)
@@ -126,4 +126,8 @@ class RollbackAction extends FormlessAction {
        protected function getDescription() {
                return '';
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 9b72163..29a494b 100644 (file)
@@ -25,7 +25,6 @@
  * @since 1.25
  */
 class SpecialPageAction extends FormlessAction {
-
        /**
         * @var array A mapping of action names to special page names.
         */
@@ -49,6 +48,7 @@ class SpecialPageAction extends FormlessAction {
                if ( isset( self::$actionToSpecialPageMapping[$actionName] ) ) {
                        return $actionName;
                }
+
                return 'nosuchaction';
        }
 
@@ -65,15 +65,33 @@ class SpecialPageAction extends FormlessAction {
        }
 
        public function show() {
-               $action = self::getName();
-               if ( $action === 'nosuchaction' ) {
-                       throw new ErrorPageError( $this->msg( 'nosuchaction' ), $this->msg( 'nosuchactiontext' ) );
+               $special = $this->getSpecialPage();
+               if ( !$special ) {
+                       throw new ErrorPageError(
+                               $this->msg( 'nosuchaction' ), $this->msg( 'nosuchactiontext' ) );
                }
 
-               // map actions to (whitelisted) special pages
-               $special = SpecialPageFactory::getPage( self::$actionToSpecialPageMapping[$action] );
                $special->setContext( $this->getContext() );
                $special->getContext()->setTitle( $special->getPageTitle() );
                $special->run( '' );
        }
+
+       public function doesWrites() {
+               $special = $this->getSpecialPage();
+
+               return $special ? $special->doesWrites() : false;
+       }
+
+       /**
+        * @return SpecialPage|null
+        */
+       protected function getSpecialPage() {
+               $action = $this->getName();
+               if ( $action === 'nosuchaction' ) {
+                       return null;
+               }
+
+               // map actions to (whitelisted) special pages
+               return SpecialPageFactory::getPage( self::$actionToSpecialPageMapping[$action] );
+       }
 }
index fae49f6..8990b75 100644 (file)
@@ -32,10 +32,8 @@ class SubmitAction extends EditAction {
        }
 
        public function show() {
-               if ( session_id() === '' ) {
-                       // Send a cookie so anons get talk message notifications
-                       wfSetupSession();
-               }
+               // Send a cookie so anons get talk message notifications
+               MediaWiki\Session\SessionManager::getGlobalSession()->persist();
 
                parent::show();
        }
index 559cfaf..0757e88 100644 (file)
@@ -39,4 +39,8 @@ class UnprotectAction extends ProtectAction {
        public function show() {
                $this->page->unprotect();
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 0a8628d..f8f1dc1 100644 (file)
@@ -52,4 +52,8 @@ class UnwatchAction extends WatchAction {
        public function onSuccess() {
                $this->getOutput()->addWikiMsg( 'removedwatchtext', $this->getTitle()->getPrefixedText() );
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 30b83d7..547fd9b 100644 (file)
@@ -178,4 +178,8 @@ class WatchAction extends FormAction {
        public static function getUnwatchToken( Title $title, User $user, $action = 'unwatch' ) {
                return self::getWatchToken( $title, $user, $action );
        }
+
+       public function doesWrites() {
+               return true;
+       }
 }
index 8a98197..13d13a6 100644 (file)
@@ -79,7 +79,7 @@ abstract class ApiBase extends ContextSource {
         * - timestamp: A timestamp in any format recognized by MWTimestamp, or the
         *   string 'now' representing the current timestamp. Will be returned in
         *   TS_MW format.
-        * - user: A MediaWiki username. Will be returned normalized but not canonicalized.
+        * - user: A MediaWiki username or IP. Will be returned normalized but not canonicalized.
         * - upload: An uploaded file. Will be returned as a WebRequestUpload object.
         *   Cannot be used with PARAM_ISMULTI.
         */
@@ -214,7 +214,6 @@ abstract class ApiBase extends ContextSource {
                }
        }
 
-
        /************************************************************************//**
         * @name   Methods to implement
         * @{
@@ -638,6 +637,17 @@ abstract class ApiBase extends ContextSource {
         * @{
         */
 
+       /**
+        * Indicate if the module supports dynamically-determined parameters that
+        * cannot be included in self::getAllowedParams().
+        * @return string|array|Message|null Return null if the module does not
+        *  support additional dynamic parameters, otherwise return a message
+        *  describing them.
+        */
+       public function dynamicParameterDocumentation() {
+               return null;
+       }
+
        /**
         * This method mangles parameter name based on the prefix supplied to the constructor.
         * Override this method to change parameter name during runtime
@@ -1461,6 +1471,33 @@ abstract class ApiBase extends ContextSource {
                );
        }
 
+       /**
+        * Throw a UsageException, which will (if uncaught) call the main module's
+        * error handler and die with an error message including block info.
+        *
+        * @since 1.27
+        * @param Block $block The block used to generate the UsageException
+        * @throws UsageException always
+        */
+       public function dieBlocked( Block $block ) {
+               // Die using the appropriate message depending on block type
+               if ( $block->getType() == Block::TYPE_AUTO ) {
+                       $this->dieUsage(
+                               'Your IP address has been blocked automatically, because it was used by a blocked user',
+                               'autoblocked',
+                               0,
+                               array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
+                       );
+               } else {
+                       $this->dieUsage(
+                               'You have been blocked from editing',
+                               'blocked',
+                               0,
+                               array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
+                       );
+               }
+       }
+
        /**
         * Get error (as code, string) from a Status object.
         *
index 636baa7..e3d73a2 100644 (file)
@@ -141,7 +141,7 @@ class ApiBlock extends ApiBase {
        public function getAllowedParams() {
                return array(
                        'user' => array(
-                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_TYPE => 'user',
                                ApiBase::PARAM_REQUIRED => true
                        ),
                        'expiry' => 'never',
index 1368bda..a044be2 100644 (file)
@@ -59,10 +59,8 @@ class ApiCreateAccount extends ApiBase {
 
                $params = $this->extractRequestParams();
 
-               // Init session if necessary
-               if ( session_id() == '' ) {
-                       wfSetupSession();
-               }
+               // Make sure session is persisted
+               MediaWiki\Session\SessionManager::getGlobalSession()->persist();
 
                if ( $params['mailpassword'] && !$params['email'] ) {
                        $this->dieUsageMsg( 'noemail' );
index b1a9c98..9da040c 100644 (file)
@@ -33,7 +33,6 @@ class ApiFormatRaw extends ApiFormatBase {
        private $errorFallback;
        private $mFailWithHTTPError = false;
 
-
        /**
         * @param ApiMain $main
         * @param ApiFormatBase|null $errorFallback Object to fall back on for errors
index b1942bc..bbea20b 100644 (file)
@@ -98,7 +98,8 @@ class ApiHelp extends ApiBase {
                }
 
                $out = $context->getOutput();
-               $out->addModules( 'mediawiki.apihelp' );
+               $out->addModuleStyles( 'mediawiki.hlist' );
+               $out->addModuleStyles( 'mediawiki.apihelp' );
                if ( !empty( $options['toc'] ) ) {
                        $out->addModules( 'mediawiki.toc' );
                }
@@ -391,8 +392,9 @@ class ApiHelp extends ApiBase {
                        }
 
                        $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
+                       $dynamicParams = $module->dynamicParameterDocumentation();
                        $groups = array();
-                       if ( $params ) {
+                       if ( $params || $dynamicParams !== null ) {
                                $help['parameters'] .= Html::openElement( 'div',
                                        array( 'class' => 'apihelp-block apihelp-parameters' )
                                );
@@ -653,6 +655,17 @@ class ApiHelp extends ApiBase {
                                        }
                                }
 
+                               if ( $dynamicParams !== null ) {
+                                       $dynamicParams = ApiBase::makeMessage( $dynamicParams, $context, array(
+                                               $module->getModulePrefix(),
+                                               $module->getModuleName(),
+                                               $module->getModulePath()
+                                       ) );
+                                       $help['parameters'] .= Html::element( 'dt', null, '*' );
+                                       $help['parameters'] .= Html::rawElement( 'dd',
+                                               array( 'class' => 'description' ), $dynamicParams->parse() );
+                               }
+
                                $help['parameters'] .= Html::closeElement( 'dl' );
                                $help['parameters'] .= Html::closeElement( 'div' );
                        }
index eb376d3..860e3b2 100644 (file)
@@ -24,6 +24,7 @@
  *
  * @file
  */
+
 use MediaWiki\Logger\LoggerFactory;
 
 /**
@@ -62,26 +63,72 @@ class ApiLogin extends ApiBase {
 
                $result = array();
 
-               // Init session if necessary
-               if ( session_id() == '' ) {
-                       wfSetupSession();
+               // Make sure session is persisted
+               $session = MediaWiki\Session\SessionManager::getGlobalSession();
+               $session->persist();
+
+               // Make sure it's possible to log in
+               if ( !$session->canSetUser() ) {
+                       $this->getResult()->addValue( null, 'login', array(
+                               'result' => 'Aborted',
+                               'reason' => 'Cannot log in when using ' .
+                                       $session->getProvider()->describe( Language::factory( 'en' ) ),
+                       ) );
+
+                       return;
                }
 
+               $authRes = false;
                $context = new DerivativeContext( $this->getContext() );
-               $context->setRequest( new DerivativeRequest(
-                       $this->getContext()->getRequest(),
-                       array(
-                               'wpName' => $params['name'],
-                               'wpPassword' => $params['password'],
-                               'wpDomain' => $params['domain'],
-                               'wpLoginToken' => $params['token'],
-                               'wpRemember' => ''
-                       )
-               ) );
-               $loginForm = new LoginForm();
-               $loginForm->setContext( $context );
+               $loginType = 'N/A';
+
+               // Check login token
+               $token = LoginForm::getLoginToken();
+               if ( !$token ) {
+                       LoginForm::setLoginToken();
+                       $authRes = LoginForm::NEED_TOKEN;
+               } elseif ( !$params['token'] ) {
+                       $authRes = LoginForm::NEED_TOKEN;
+               } elseif ( $token !== $params['token'] ) {
+                       $authRes = LoginForm::WRONG_TOKEN;
+               }
+
+               // Try bot passwords
+               if ( $authRes === false && $this->getConfig()->get( 'EnableBotPasswords' ) &&
+                       strpos( $params['name'], BotPassword::getSeparator() ) !== false
+               ) {
+                       $status = BotPassword::login(
+                               $params['name'], $params['password'], $this->getRequest()
+                       );
+                       if ( $status->isOk() ) {
+                               $session = $status->getValue();
+                               $authRes = LoginForm::SUCCESS;
+                               $loginType = 'BotPassword';
+                       } else {
+                               LoggerFactory::getInstance( 'authmanager' )->info(
+                                       'BotPassword login failed: ' . $status->getWikiText()
+                               );
+                       }
+               }
+
+               // Normal login
+               if ( $authRes === false ) {
+                       $context->setRequest( new DerivativeRequest(
+                               $this->getContext()->getRequest(),
+                               array(
+                                       'wpName' => $params['name'],
+                                       'wpPassword' => $params['password'],
+                                       'wpDomain' => $params['domain'],
+                                       'wpLoginToken' => $params['token'],
+                                       'wpRemember' => ''
+                               )
+                       ) );
+                       $loginForm = new LoginForm();
+                       $loginForm->setContext( $context );
+                       $authRes = $loginForm->authenticateUserData();
+                       $loginType = 'LoginForm';
+               }
 
-               $authRes = $loginForm->authenticateUserData();
                switch ( $authRes ) {
                        case LoginForm::SUCCESS:
                                $user = $context->getUser();
@@ -107,16 +154,16 @@ class ApiLogin extends ApiBase {
                                // SessionManager/AuthManager are *really* going to break it.
                                $result['lgtoken'] = $user->getToken();
                                $result['cookieprefix'] = $this->getConfig()->get( 'CookiePrefix' );
-                               $result['sessionid'] = session_id();
+                               $result['sessionid'] = $session->getId();
                                break;
 
                        case LoginForm::NEED_TOKEN:
                                $result['result'] = 'NeedToken';
-                               $result['token'] = $loginForm->getLoginToken();
+                               $result['token'] = LoginForm::getLoginToken();
 
                                // @todo: See above about deprecation
                                $result['cookieprefix'] = $this->getConfig()->get( 'CookiePrefix' );
-                               $result['sessionid'] = session_id();
+                               $result['sessionid'] = $session->getId();
                                break;
 
                        case LoginForm::WRONG_TOKEN:
@@ -187,6 +234,7 @@ class ApiLogin extends ApiBase {
                LoggerFactory::getInstance( 'authmanager' )->info( 'Login attempt', array(
                        'event' => 'login',
                        'successful' => $authRes === LoginForm::SUCCESS,
+                       'loginType' => $loginType,
                        'status' => LoginForm::$statusCodes[$authRes],
                ) );
        }
index bf0ca9c..b40f5a3 100644 (file)
 class ApiLogout extends ApiBase {
 
        public function execute() {
+               // Make sure it's possible to log out
+               $session = MediaWiki\Session\SessionManager::getGlobalSession();
+               if ( !$session->canSetUser() ) {
+                       $this->dieUsage(
+                               'Cannot log out when using ' .
+                                       $session->getProvider()->describe( Language::factory( 'en' ) ),
+                               'cannotlogout'
+                       );
+               }
+
                $user = $this->getUser();
                $oldName = $user->getName();
                $user->logout();
index 49b9786..6ddc28a 100644 (file)
@@ -769,7 +769,7 @@ class ApiMain extends ApiBase {
                                        return;
                                }
                                // Logged out, send normal public headers below
-                       } elseif ( session_id() != '' ) {
+                       } elseif ( MediaWiki\Session\SessionManager::getGlobalSession()->isPersistent() ) {
                                // Logged in or otherwise has session (e.g. anonymous users who have edited)
                                // Mark request private
                                $response->header( "Cache-Control: $privateCache" );
@@ -1253,6 +1253,8 @@ class ApiMain extends ApiBase {
                $module = $this->setupModule();
                $this->mModule = $module;
 
+               $this->setRequestExpectations( $module );
+
                $this->checkExecutePermissions( $module );
 
                if ( !$this->checkMaxLag( $module, $params ) ) {
@@ -1284,6 +1286,24 @@ class ApiMain extends ApiBase {
                }
        }
 
+       /**
+        * Set database connection, query, and write expectations given this module request
+        * @param ApiBase $module
+        */
+       protected function setRequestExpectations( ApiBase $module ) {
+               $limits = $this->getConfig()->get( 'TrxProfilerLimits' );
+               $trxProfiler = Profiler::instance()->getTransactionProfiler();
+               if ( $this->getRequest()->wasPosted() ) {
+                       if ( $module->isWriteMode() ) {
+                               $trxProfiler->setExpectations( $limits['POST'], __METHOD__ );
+                       } else {
+                               $trxProfiler->setExpectations( $limits['POST-nonwrite'], __METHOD__ );
+                       }
+               } else {
+                       $trxProfiler->setExpectations( $limits['GET'], __METHOD__ );
+               }
+       }
+
        /**
         * Log the preceding request
         * @param float $time Time in seconds
@@ -1768,15 +1788,6 @@ class ApiMain extends ApiBase {
                $this->getModuleManager()->addModule( $name, 'format', $class );
        }
 
-       /**
-        * Get the array mapping module names to class names
-        * @deprecated since 1.21, Use getModuleManager()'s methods instead.
-        * @return array
-        */
-       function getModules() {
-               return $this->getModuleManager()->getNamesWithClasses( 'action' );
-       }
-
        /**
         * Returns the list of supported formats in form ( 'format' => 'ClassName' )
         *
index a808ac0..18ca0ab 100644 (file)
@@ -387,6 +387,20 @@ class ApiParamInfo extends ApiBase {
                }
                ApiResult::setIndexedTagName( $ret['parameters'], 'param' );
 
+               $dynamicParams = $module->dynamicParameterDocumentation();
+               if ( $dynamicParams !== null ) {
+                       if ( $this->helpFormat === 'none' ) {
+                               $ret['dynamicparameters'] = true;
+                       } else {
+                               $dynamicParams = ApiBase::makeMessage( $dynamicParams, $this->context, array(
+                                       $module->getModulePrefix(),
+                                       $module->getModuleName(),
+                                       $module->getModulePath()
+                               ) );
+                               $this->formatHelpMessages( $ret, 'dynamicparameters', array( $dynamicParams ) );
+                       }
+               }
+
                return $ret;
        }
 
index 902bca7..8015d65 100644 (file)
@@ -77,6 +77,7 @@ class ApiQuery extends ApiBase {
                'allpages' => 'ApiQueryAllPages',
                'allredirects' => 'ApiQueryAllLinks',
                'allrevisions' => 'ApiQueryAllRevisions',
+               'mystashedfiles' => 'ApiQueryMyStashedFiles',
                'alltransclusions' => 'ApiQueryAllLinks',
                'allusers' => 'ApiQueryAllUsers',
                'backlinks' => 'ApiQueryBacklinks',
@@ -184,17 +185,6 @@ class ApiQuery extends ApiBase {
                return $this->mPageSet;
        }
 
-       /**
-        * Get the array mapping module names to class names
-        * @deprecated since 1.21, use getModuleManager()'s methods instead
-        * @return array Array(modulename => classname)
-        */
-       public function getModules() {
-               wfDeprecated( __METHOD__, '1.21' );
-
-               return $this->getModuleManager()->getNamesWithClasses();
-       }
-
        /**
         * Get the generators array mapping module names to class names
         * @deprecated since 1.21, list of generators is maintained by ApiPageSet
index d004020..229e3d1 100644 (file)
@@ -273,6 +273,7 @@ class ApiQueryBlocks extends ApiQueryBase {
                                ApiBase::PARAM_ISMULTI => true
                        ),
                        'users' => array(
+                               ApiBase::PARAM_TYPE => 'user',
                                ApiBase::PARAM_ISMULTI => true
                        ),
                        'ip' => array(
index 8dbd812..267cc6d 100644 (file)
@@ -515,6 +515,11 @@ class ApiQueryImageInfo extends ApiQueryBase {
                        }
                        $vals['url'] = wfExpandUrl( $file->getFullUrl(), PROTO_CURRENT );
                        $vals['descriptionurl'] = wfExpandUrl( $file->getDescriptionUrl(), PROTO_CURRENT );
+
+                       $shortDescriptionUrl = $file->getDescriptionShortUrl();
+                       if ( $shortDescriptionUrl !== null ) {
+                               $vals['descriptionshorturl'] = wfExpandUrl( $shortDescriptionUrl, PROTO_CURRENT );
+                       }
                }
 
                if ( $sha1 ) {
index 38be99a..a76012a 100644 (file)
@@ -165,7 +165,7 @@ class ApiQueryLogEvents extends ApiQueryBase {
                        if ( $userid ) {
                                $this->addWhereFld( 'log_user', $userid );
                        } else {
-                               $this->addWhereFld( 'log_user_text', IP::sanitizeIP( $user ) );
+                               $this->addWhereFld( 'log_user_text', $user );
                        }
                }
 
@@ -430,7 +430,9 @@ class ApiQueryLogEvents extends ApiQueryBase {
                                ),
                                ApiBase::PARAM_HELP_MSG => 'api-help-param-direction',
                        ),
-                       'user' => null,
+                       'user' => array(
+                               ApiBase::PARAM_TYPE => 'user',
+                       ),
                        'title' => null,
                        'namespace' => array(
                                ApiBase::PARAM_TYPE => 'namespace'
diff --git a/includes/api/ApiQueryMyStashedFiles.php b/includes/api/ApiQueryMyStashedFiles.php
new file mode 100644 (file)
index 0000000..d33dbfd
--- /dev/null
@@ -0,0 +1,151 @@
+<?php
+/**
+ * API for MediaWiki 1.27+
+ *
+ * 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
+ */
+
+/**
+ * action=query&list=mystashedfiles module, gets all stashed files for
+ * the current user.
+ *
+ * @ingroup API
+ */
+class ApiQueryMyStashedFiles extends ApiQueryBase {
+
+       public function __construct( ApiQuery $query, $moduleName ) {
+               parent::__construct( $query, $moduleName, 'msf' );
+       }
+
+       public function execute() {
+               $user = $this->getUser();
+
+               if ( $user->isAnon() ) {
+                       $this->dieUsage( 'The upload stash is only available to logged-in users.', 'stashnotloggedin' );
+               }
+
+               // Note: If user is logged in but cannot upload, they can still see
+               // the list of stashed uploads...but it will probably be empty.
+
+               $db = $this->getDB();
+               $params = $this->extractRequestParams();
+
+               $this->addTables( 'uploadstash' );
+
+               $this->addFields( array( 'us_id', 'us_key', 'us_status' ) );
+
+               $this->addWhere( array( 'us_user' => $user->getId() ) );
+
+               if ( $params['continue'] !== null ) {
+                       $cont = explode( '|', $params['continue'] );
+                       $this->dieContinueUsageIf( count( $cont ) != 1 );
+                       $cont_from = (int)$cont[0];
+                       $this->dieContinueUsageIf( strval( $cont_from ) !== $cont[0] );
+                       $this->addWhere( "us_id >= $cont_from" );
+               }
+
+               $this->addOption( 'LIMIT', $params['limit'] + 1 );
+               $this->addOption( 'ORDER BY', 'us_id' );
+
+               $prop = array_flip( $params['prop'] );
+               $this->addFieldsIf(
+                       array(
+                               'us_size',
+                               'us_image_width',
+                               'us_image_height',
+                               'us_image_bits'
+                       ),
+
+                       isset( $prop['size'] )
+               );
+               $this->addFieldsIf( array( 'us_mime', 'us_media_type' ), isset( $prop['type'] ) );
+
+               $res = $this->select( __METHOD__ );
+               $result = $this->getResult();
+               $count = 0;
+
+               foreach ( $res as $row ) {
+                       if ( ++$count > $params['limit'] ) {
+                               // We've reached the one extra which shows that there are
+                               // additional files to be had. Stop here...
+                               $this->setContinueEnumParameter( 'continue', $row->us_id );
+                               break;
+                       }
+
+                       $item = array(
+                               'filekey' => $row->us_key,
+                               'status' => $row->us_status,
+                       );
+
+                       if ( isset( $prop['size'] ) ) {
+                               $item['size'] = (int) $row->us_size;
+                               $item['width'] = (int) $row->us_image_width;
+                               $item['height'] = (int) $row->us_image_height;
+                               $item['bits'] = (int) $row->us_image_bits;
+                       }
+
+                       if ( isset( $prop['type'] ) ) {
+                               $item['mimetype'] = $row->us_mime;
+                               $item['mediatype'] = $row->us_media_type;
+                       }
+
+                       $fit = $result->addValue( array( 'query', $this->getModuleName() ), null, $item );
+
+                       if ( !$fit ) {
+                               $this->setContinueEnumParameter( 'continue', $row->us_id );
+                               break;
+                       }
+               }
+
+               $result->addIndexedTagName( array( 'query', $this->getModuleName() ), 'file' );
+       }
+
+       public function getAllowedParams() {
+               return array(
+                       'prop' => array(
+                               ApiBase::PARAM_ISMULTI => true,
+                               ApiBase::PARAM_DFLT => '',
+                               ApiBase::PARAM_TYPE => array( 'size', 'type' ),
+                               ApiBase::PARAM_HELP_MSG_PER_VALUE => array(),
+                       ),
+
+                       'limit' => array(
+                               ApiBase::PARAM_TYPE => 'limit',
+                               ApiBase::PARAM_DFLT => 10,
+                               ApiBase::PARAM_MIN => 1,
+                               ApiBase::PARAM_MAX => ApiBase::LIMIT_BIG1,
+                               ApiBase::PARAM_MAX2 => ApiBase::LIMIT_BIG2,
+                       ),
+
+                       'continue' => array(
+                               ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
+                       ),
+               );
+       }
+
+       protected function getExamplesMessages() {
+               return array(
+                       'action=query&list=mystashedfiles&msfprop=size'
+                               => 'apihelp-query+mystashedfiles-example-simple',
+               );
+       }
+
+       public function getHelpUrls() {
+               return 'https://www.mediawiki.org/wiki/API:mystashedfiles';
+       }
+}
index c99d87c..49d4c8c 100644 (file)
@@ -224,7 +224,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        $this->addWhereIf( 'page_is_redirect = 1', isset( $show['redirect'] ) );
 
                        if ( isset( $show['unpatrolled'] ) ) {
-                               // See ChangesList:isUnpatrolled
+                               // See ChangesList::isUnpatrolled
                                if ( $user->useRCPatrol() ) {
                                        $this->addWhere( 'rc_patrolled = 0' );
                                } elseif ( $user->useNPPatrol() ) {
@@ -289,7 +289,7 @@ class ApiQueryRecentChanges extends ApiQueryGeneratorBase {
                        $this->addFieldsIf( 'rc_user_text', $this->fld_user );
                        $this->addFieldsIf( array( 'rc_minor', 'rc_type', 'rc_bot' ), $this->fld_flags );
                        $this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes );
-                       $this->addFieldsIf( 'rc_patrolled', $this->fld_patrolled );
+                       $this->addFieldsIf( array( 'rc_patrolled', 'rc_log_type' ), $this->fld_patrolled );
                        $this->addFieldsIf(
                                array( 'rc_logid', 'rc_log_type', 'rc_log_action', 'rc_params' ),
                                $this->fld_loginfo
index 1ef0f35..27a28e1 100644 (file)
@@ -461,6 +461,7 @@ class ApiQueryContributions extends ApiQueryBase {
                                ApiBase::PARAM_HELP_MSG => 'api-help-param-continue',
                        ),
                        'user' => array(
+                               ApiBase::PARAM_TYPE => 'user',
                                ApiBase::PARAM_ISMULTI => true
                        ),
                        'userprefix' => null,
index db5fb65..ea9d48d 100644 (file)
@@ -317,6 +317,7 @@ class ApiQueryUsers extends ApiQueryBase {
                        ),
                        'attachedwiki' => null,
                        'users' => array(
+                               ApiBase::PARAM_TYPE => 'user',
                                ApiBase::PARAM_ISMULTI => true
                        ),
                        'token' => array(
index 75fc33e..ffbd75a 100644 (file)
@@ -105,7 +105,7 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase {
                        $this->addFieldsIf( 'rc_user', $this->fld_user || $this->fld_userid );
                        $this->addFieldsIf( 'rc_user_text', $this->fld_user );
                        $this->addFieldsIf( 'rc_comment', $this->fld_comment || $this->fld_parsedcomment );
-                       $this->addFieldsIf( 'rc_patrolled', $this->fld_patrol );
+                       $this->addFieldsIf( array( 'rc_patrolled', 'rc_log_type' ), $this->fld_patrol );
                        $this->addFieldsIf( array( 'rc_old_len', 'rc_new_len' ), $this->fld_sizes );
                        $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp );
                        $this->addFieldsIf(
index bd5fe08..71af80c 100644 (file)
@@ -364,9 +364,13 @@ class ApiResult implements ApiSerializable {
                        }
                }
                if ( is_array( $value ) ) {
+                       // Work around PHP bug 45959 by copying to a temporary
+                       // (in this case, foreach gets $k === "1" but $tmp[$k] assigns as if $k === 1)
+                       $tmp = array();
                        foreach ( $value as $k => $v ) {
-                               $value[$k] = self::validateValue( $v );
+                               $tmp[$k] = self::validateValue( $v );
                        }
+                       $value = $tmp;
                } elseif ( is_float( $value ) && !is_finite( $value ) ) {
                        throw new InvalidArgumentException( "Cannot add non-finite floats to ApiResult" );
                } elseif ( is_string( $value ) ) {
index b70b536..4db3ca1 100644 (file)
@@ -36,30 +36,12 @@ class ApiRevisionDelete extends ApiBase {
 
                $params = $this->extractRequestParams();
                $user = $this->getUser();
-
                if ( !$user->isAllowed( RevisionDeleter::getRestriction( $params['type'] ) ) ) {
                        $this->dieUsageMsg( 'badaccess-group0' );
                }
 
                if ( $user->isBlocked() ) {
-                       $block = $user->getBlock();
-
-                       // Die using the appropriate message depending on block type
-                       if ( $block->getType() == TYPE_AUTO ) {
-                               $this->dieUsage(
-                                       'Your IP address has been blocked automatically, because it was used by a blocked user',
-                                       'autoblocked',
-                                       0,
-                                       array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
-                               );
-                       } else {
-                               $this->dieUsage(
-                                       'You have been blocked from editing',
-                                       'blocked',
-                                       0,
-                                       array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
-                               );
-                       }
+                       $this->dieBlocked( $user->getBlock() );
                }
 
                if ( !$params['ids'] ) {
index 7037fb6..0fa2e31 100644 (file)
@@ -126,7 +126,7 @@ class ApiRollback extends ApiBase {
                                ApiBase::PARAM_ISMULTI => true,
                        ),
                        'user' => array(
-                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_TYPE => 'user',
                                ApiBase::PARAM_REQUIRED => true
                        ),
                        'summary' => '',
index 4157de0..4bf799e 100644 (file)
@@ -40,24 +40,7 @@ class ApiTag extends ApiBase {
                }
 
                if ( $user->isBlocked() ) {
-                       $block = $user->getBlock();
-
-                       // Die using the appropriate message depending on block type
-                       if ( $block->getType() == TYPE_AUTO ) {
-                               $this->dieUsage(
-                                       'Your IP address has been blocked automatically, because it was used by a blocked user',
-                                       'autoblocked',
-                                       0,
-                                       array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
-                               );
-                       } else {
-                               $this->dieUsage(
-                                       'You have been blocked from editing',
-                                       'blocked',
-                                       0,
-                                       array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) )
-                               );
-                       }
+                       $this->dieBlocked( $user->getBlock() );
                }
 
                // validate and process each revid, rcid and logid
index cd50ee6..469fe7f 100644 (file)
@@ -33,18 +33,13 @@ class ApiUndelete extends ApiBase {
                $this->useTransactionalTimeLimit();
 
                $params = $this->extractRequestParams();
-
-               if ( !$this->getUser()->isAllowed( 'undelete' ) ) {
+               $user = $this->getUser();
+               if ( !$user->isAllowed( 'undelete' ) ) {
                        $this->dieUsageMsg( 'permdenied-undelete' );
                }
 
-               if ( $this->getUser()->isBlocked() ) {
-                       $this->dieUsage(
-                               'You have been blocked from editing',
-                               'blocked',
-                               0,
-                               array( 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $this->getUser()->getBlock() ) )
-                       );
+               if ( $user->isBlocked() ) {
+                       $this->dieBlocked( $user->getBlock() );
                }
 
                $titleObj = Title::newFromText( $params['title'] );
index 3ccdde2..815ef0b 100644 (file)
@@ -49,6 +49,14 @@ class ApiUserrights extends ApiBase {
        }
 
        public function execute() {
+               $pUser = $this->getUser();
+
+               // Deny if the user is blocked and doesn't have the full 'userrights' permission.
+               // This matches what Special:UserRights does for the web UI.
+               if ( $pUser->isBlocked() && !$pUser->isAllowed( 'userrights' ) ) {
+                       $this->dieBlocked( $pUser->getBlock() );
+               }
+
                $params = $this->extractRequestParams();
 
                $user = $this->getUrUser( $params );
@@ -104,7 +112,7 @@ class ApiUserrights extends ApiBase {
        public function getAllowedParams() {
                return array(
                        'user' => array(
-                               ApiBase::PARAM_TYPE => 'string',
+                               ApiBase::PARAM_TYPE => 'user',
                        ),
                        'userid' => array(
                                ApiBase::PARAM_TYPE => 'integer',
index 24adee5..a7e5754 100644 (file)
@@ -8,6 +8,15 @@
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentación]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Llista d'alderique]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Anuncios de la API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Fallos y solicitúes]\n</div>\n<strong>Estau:</strong> Toles carauterístiques qu'apaecen nesta páxina tendríen de funcionar, pero la API inda ta en desendolcu activu, y puede camudar en cualquier momentu. Suscríbete a la [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ llista de corréu mediawiki-api-announce] p'avisos sobro anovamientos.\n\n<strong>Solicitúes incorreutes:</strong> Cuando s'unvíen solicitúes incorreutes a la API, unvíase una cabecera HTTP cola clave \"MediaWiki-API-Error\" y, darréu, tanto'l valor de la cabecera como'l códigu d'error devueltu pondránse al mesmu valor. Pa más información, consulta [[mw:API:Errors_and_warnings|API: Errores y avisos]].",
        "apihelp-main-param-action": "Qué aición facer.",
        "apihelp-main-param-format": "El formatu de la salida.",
+       "apihelp-block-description": "Bloquiar a un usuariu.",
+       "apihelp-block-param-user": "El nome d'usuariu, dirección IP o intervalu d'IP que quies bloquiar.",
+       "apihelp-block-param-expiry": "Fecha de caducidá. Puede ser relativa (por casu, <kbd>5 meses</kbd> o <kbd>2 selmanes</kbd>) o absoluta (por casu, 2016-01-16T12:34:56Z). Si s'establez a <kbd>infinitu</kbd>, <kbd>indefiníu</kbd>, o <kbd>nunca</kbd>, el bloquéu nun caducará nunca.",
+       "apihelp-block-param-reason": "Motivu del bloquéu.",
+       "apihelp-block-param-anononly": "Bloquiar solo los usuarios anónimos (esto ye, desactivar ediciones anónimes dende esta dirección IP).",
+       "apihelp-block-param-nocreate": "Torgar la creación de cuentes.",
+       "apihelp-block-param-autoblock": "Bloquiar automáticamente la última dirección IP usada y les siguientes direcciones IP de les que traten d'aniciar sesión darréu.",
+       "apihelp-block-param-noemail": "Torgar que l'usuariu unvie corréu al traviés de la wiki (Rique'l permisu <code>blockemail</code>).",
+       "apihelp-block-param-hidename": "Despintar el nome d'usuariu del rexistru de bloquéu (Rique'l permisu <code>hideuser</code>).",
        "apihelp-createaccount-param-name": "Nome d'usuariu.",
        "apihelp-createaccount-param-language": "Códigu de llingua p'afitar como predetermináu al usuariu (opcional, predetermina la llingua del conteníu).",
        "apihelp-disabled-description": "Esti módulu deshabilitóse."
diff --git a/includes/api/i18n/bg.json b/includes/api/i18n/bg.json
new file mode 100644 (file)
index 0000000..7ab62b5
--- /dev/null
@@ -0,0 +1,46 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Vodnokon4e"
+               ]
+       },
+       "apihelp-main-param-action": "Кое действие да се извърши.",
+       "apihelp-block-description": "Блокиране на потребител.",
+       "apihelp-block-param-user": "Потребителско име, IP адрес или диапазон от IP адреси, които искате да блокирате.",
+       "apihelp-block-param-reason": "Причина за блокиране.",
+       "apihelp-block-param-nocreate": "Забрана за създаване на потребителски сметки.",
+       "apihelp-createaccount-description": "Създаване на нова потребителска сметка.",
+       "apihelp-createaccount-param-name": "Потребителско име.",
+       "apihelp-createaccount-param-email": "Адрес на електронна поща на потребителя (незадължително).",
+       "apihelp-createaccount-param-realname": "Истинско име на потребителя (незадължително).",
+       "apihelp-delete-description": "Изтриване на страница.",
+       "apihelp-edit-description": "Създаване и редактиране на страници.",
+       "apihelp-edit-param-text": "Съдържание на страница.",
+       "apihelp-edit-param-minor": "Малка промяна.",
+       "apihelp-edit-param-notminor": "Значителна промяна.",
+       "apihelp-edit-param-bot": "Отбелязване на редакцията като бот.",
+       "apihelp-emailuser-description": "Изпращане на е-писмо до потребител.",
+       "apihelp-emailuser-param-target": "Получател на имейла.",
+       "apihelp-emailuser-param-subject": "Заглавие на тема.",
+       "apihelp-emailuser-param-text": "Съдържание на писмото.",
+       "apihelp-emailuser-param-ccme": "Изпращане на копие от това писмо до мен.",
+       "apihelp-expandtemplates-param-title": "Заглавие на страница.",
+       "apihelp-feedrecentchanges-param-hideminor": "Скриване на малки редакции.",
+       "apihelp-feedrecentchanges-param-hidebots": "Скриване на промени, направени от ботове.",
+       "apihelp-feedrecentchanges-param-hideanons": "Скриване на промени, направени от анонимни потребители.",
+       "apihelp-feedrecentchanges-param-hideliu": "Скриване на промени, направени от регистрирани потребители.",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Скриване на проверени промени.",
+       "apihelp-feedrecentchanges-param-hidemyself": "Скриване на промените, направени от настоящия потребител.",
+       "apihelp-feedrecentchanges-param-tagfilter": "Филтриране по етикети.",
+       "apihelp-feedrecentchanges-example-simple": "Показване на последни промени.",
+       "apihelp-feedrecentchanges-example-30days": "Показване на последните промени в рамките на 30 дни.",
+       "apihelp-login-param-name": "Потребителско име.",
+       "apihelp-login-param-password": "Парола.",
+       "apihelp-move-description": "Преместване на страница.",
+       "apihelp-move-param-reason": "Причина за преименуването.",
+       "apihelp-move-param-movetalk": "Преименуване на беседата, ако има такава.",
+       "apihelp-move-param-movesubpages": "Преименуване на подстраници, ако е приложимо.",
+       "apihelp-move-param-noredirect": "Не създавай пренасочване.",
+       "apihelp-move-param-ignorewarnings": "Пренебрегване на всякакви предупреждения.",
+       "apihelp-protect-example-protect": "Защита на страница."
+}
index 5b05755..45d0576 100644 (file)
@@ -8,6 +8,21 @@
        "apihelp-main-param-format": "Гойту формат.",
        "apihelp-main-param-curtimestamp": "Хилламийн юкъатоха ханна йолу билгало",
        "apihelp-createaccount-param-name": "Декъашхочун цӀе.",
+       "apihelp-delete-description": "ДӀаяккха агӀо.",
+       "apihelp-edit-example-edit": "АгӀо таян",
+       "apihelp-emailuser-description": "Декъашхочунга кехат",
+       "apihelp-emailuser-param-target": "Электронан кехатан адрес.",
+       "apihelp-emailuser-param-subject": "Хьедаран корта.",
+       "apihelp-emailuser-param-text": "Кехатан чулацам",
+       "apihelp-expandtemplates-param-title": "АгӀонан корта.",
+       "apihelp-feedrecentchanges-param-tagfilter": "Тегийн луьттург.",
+       "apihelp-login-example-login": "ЧугӀо",
+       "apihelp-logout-description": "ЧугӀой сессийн хаамаш дӀацӀанбе.",
+       "apihelp-move-description": "АгӀон цӀе хийца.",
+       "apihelp-opensearch-param-search": "Лахаран могӀа.",
+       "apihelp-parse-example-page": "АгӀо зер",
+       "apihelp-parse-example-text": "Wikitext зер.",
+       "apihelp-protect-example-protect": "Ларъе агӀо.",
        "apihelp-userrights-param-userid": "Декъашхочун ID.",
        "api-help-datatypes-header": "Хаамийн тайпанаш"
 }
index fc258e2..2ca10c0 100644 (file)
        "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-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-query+alldeletedrevisions-param-user": "Nur Versionen von diesem Benutzer auflisten.",
        "apihelp-query+alldeletedrevisions-param-excludeuser": "Schließt Bearbeitungen des angegebenen Benutzers aus.",
        "apihelp-query+alldeletedrevisions-param-namespace": "Nur Seiten in diesem Namensraum auflisten.",
+       "apihelp-query+alldeletedrevisions-param-generatetitles": "Wenn als Generator verwendet, werden eher Titel als Bearbeitungs-IDs erzeugt.",
        "apihelp-query+alldeletedrevisions-example-user": "Liste die letzten 50 gelöschten Beiträge, sortiert nach Benutzer <kbd>Beispiel</kbd>.",
        "apihelp-query+alldeletedrevisions-example-ns-main": "Liste die ersten 50 gelöschten Bearbeitungen im Hauptnamensraum.",
        "apihelp-query+allfileusages-description": "Liste alle Dateiverwendungen, einschließlich nicht-vorhandener.",
        "apihelp-query+allmessages-param-prop": "Welche Eigenschaften abgerufen werden sollen.",
        "apihelp-query+allmessages-param-enableparser": "Setzen, um den Parser zu aktivieren. Dies wird den Wikitext der Nachricht vorverarbeiten (magische Worte ersetzen, Vorlagen berücksichtigen, usw.).",
        "apihelp-query+allmessages-param-nocontent": "Wenn gesetzt, füge nicht den Inhalt der Nachricht der Ausgabe hinzu.",
+       "apihelp-query+allmessages-param-includelocal": "Schließt auch lokale Nachrichten ein. Zum Beispiel Nachrichten die es nicht in der Software gibt, die es aber als MediaWiki: - Seite gibt. Dies listet alle MediaWiki: - Seiten auf. Daher werden auch diejenigen aufgelistet, die eigentlich keine Nachrichten sind, wie [[MediaWiki:Common.js|Common.js]].",
        "apihelp-query+allmessages-param-args": "Argumente die in der Nachricht ersetzt werden sollen.",
        "apihelp-query+allmessages-param-filter": "Gebe nur Nachrichten mit Namen, die diese Zeichenfolge enthalten, zurück.",
        "apihelp-query+allmessages-param-customised": "Gebe nur Nachrichten in diesem Anpassungszustand zurück.",
        "apihelp-query+allrevisions-param-user": "Liste nur Bearbeitungen von diesem Benutzer auf.",
        "apihelp-query+allrevisions-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
        "apihelp-query+allrevisions-param-namespace": "Nur Seiten dieses Namensraums auflisten.",
+       "apihelp-query+allrevisions-param-generatetitles": "Wenn als Generator verwendet, werden eher Titel als Bearbeitungs-IDs erzeugt.",
        "apihelp-query+allrevisions-example-user": "Liste die letzten 50 Beiträge, sortiert nach Benutzer <kbd>Beispiel</kbd> auf.",
        "apihelp-query+allrevisions-example-ns-main": "Liste die ersten 50 Bearbeitungen im Hauptnamensraum auf.",
+       "apihelp-query+alltransclusions-description": "Liste alle Transklusionen auf (eingebettete Seiten die &#123;&#123;x&#125;&#125; benutzen), einschließlich nicht vorhandener.",
        "apihelp-query+alltransclusions-param-from": "Der Titel der Transklusion bei dem die Auflistung beginnen soll.",
        "apihelp-query+alltransclusions-param-to": "Der Titel der Transklusion bei dem die Auflistung enden soll.",
        "apihelp-query+alltransclusions-param-prefix": "Suche nach allen transkludierten Titeln die mit diesem Wert beginnen.",
        "apihelp-query+alltransclusions-param-namespace": "Der aufzulistende Namensraum.",
        "apihelp-query+alltransclusions-param-limit": "Wie viele Gesamtobjekte zurückgegeben werden sollen.",
        "apihelp-query+alltransclusions-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+alltransclusions-example-B": "Liste transkludierte Titel, einschließlich fehlender, mit den Seiten-IDs von denen sie stammen, beginne bei <kbd>B</kbd>.",
        "apihelp-query+alltransclusions-example-unique": "Einzigartige eingebundene Titel auflisten.",
+       "apihelp-query+alltransclusions-example-unique-generator": "Ruft alle transkludierten Titel ab und markiert die fehlenden.",
+       "apihelp-query+alltransclusions-example-generator": "Ruft Seiten ab welche die Transklusionen beinhalten.",
+       "apihelp-query+allusers-description": "Auflisten aller registrierten Benutzer.",
+       "apihelp-query+allusers-param-from": "Der Benutzername, bei dem die Auflistung beginnen soll.",
+       "apihelp-query+allusers-param-to": "Der Benutzername, bei dem die Auflistung enden soll.",
+       "apihelp-query+allusers-param-prefix": "Sucht nach allen Benutzern, die mit diesem Wert beginnen.",
+       "apihelp-query+allusers-param-dir": "Sortierrichtung.",
+       "apihelp-query+allusers-param-group": "Nur Benutzer der angegebenen Gruppen einbeziehen.",
+       "apihelp-query+allusers-param-excludegroup": "Benutzer dieser Gruppen ausschließen.",
+       "apihelp-query+allusers-param-prop": "Welche Informationsteile einbinden:",
+       "apihelp-query+allusers-paramvalue-prop-blockinfo": "Fügt die Informationen über eine aktuelle Sperre des Benutzer hinzu.",
+       "apihelp-query+allusers-paramvalue-prop-groups": "Listet Gruppen auf denen der Benutzer angehört. Dies verwendet mehr Serverressourcen und kann weniger Ergebnisse als die Grenze zurückliefern.",
+       "apihelp-query+allusers-paramvalue-prop-implicitgroups": "Listet alle Gruppen auf, denen Benutzer automatisch angehört.",
+       "apihelp-query+allusers-paramvalue-prop-rights": "Listet die Berechtigungen auf, die der Benutzer hat.",
+       "apihelp-query+allusers-paramvalue-prop-editcount": "Fügt den Bearbeitungszähler des Benutzers hinzu.",
+       "apihelp-query+allusers-paramvalue-prop-registration": "Fügt, falls vorhanden, den Zeitstempel hinzu, wann der Benutzer registriert wurde (kann leer sein).",
+       "apihelp-query+allusers-paramvalue-prop-centralids": "Fügt die zentralen IDs und den Anhang-Status des Benutzers hinzu.",
        "apihelp-query+allusers-param-limit": "Wie viele Benutzernamen insgesamt zurückgegeben werden sollen.",
+       "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+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+backlinks-param-namespace": "Der aufzulistende Namensraum.",
+       "apihelp-query+backlinks-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+backlinks-param-filterredir": "Wie nach Weiterleitungen gefiltert werden soll. Falls auf <kbd>nonredirects</kbd> gesetzt, wenn <var>$1redirect</var> aktiviert ist, wird dies nur auf die zweite Ebene angewandt.",
+       "apihelp-query+backlinks-param-limit": "Wie viele Seiten insgesamt zurückgegeben werden sollen. Falls <var>$1redirect<var> aktiviert ist, wird die Grenze auf jede Ebene einzeln angewandt (was bedeutet, dass bis zu 2 * <var>$1limit</var> Ergebnisse zurückgegeben werden können).",
+       "apihelp-query+backlinks-param-redirect": "Falls die verweisende Seite eine Weiterleitung ist, finde alle Seiten, die auf diese Weiterleitung ebenfalls verweisen. Die maximale Grenze wird halbiert.",
        "apihelp-query+backlinks-example-simple": "Links auf <kbd>Main page</kbd> anzeigen.",
+       "apihelp-query+backlinks-example-generator": "Hole Informationen über die Seiten, die auf die <kbd>Hauptseite</kbd> verweisen.",
+       "apihelp-query+blocks-description": "Liste alle gesperrten Benutzer und IP-Adressen auf.",
+       "apihelp-query+blocks-param-start": "Der Zeitstempel, bei dem die Aufzählung beginnen soll.",
+       "apihelp-query+blocks-param-end": "Der Zeitstempel, bei dem die Aufzählung beendet werden soll.",
+       "apihelp-query+blocks-param-ids": "Liste von Sperren-IDs, die aufglistet werden sollen (optional).",
+       "apihelp-query+blocks-param-users": "Liste von Benutzern, nach denen gesucht werden soll (optional).",
+       "apihelp-query+blocks-param-limit": "Die maximale Zahl der aufzulistenden Sperren.",
+       "apihelp-query+blocks-param-prop": "Zurückzugebende Eigenschaften:",
+       "apihelp-query+blocks-paramvalue-prop-id": "Fügt die ID der Sperre hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-user": "Fügt den Benutzernamen des gesperrten Benutzers hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-userid": "Fügt die Benutzer-ID des gesperrten Benutzers hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-by": "Fügt den Benutzernamen des sperrenden Benutzers hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-byid": "Fügt die Benutzer-ID des sperrenden Benutzers hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-timestamp": "Fügt den Zeitstempel wann die Sperre gesetzt wurde hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-expiry": "Fügt den Zeitstempel wann die Sperre abläuft hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-reason": "Fügt den angegebenen Grund für die Sperrung hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-range": "Fügt den von der Sperrung betroffenen Bereich von IP-Adressen hinzu.",
+       "apihelp-query+blocks-paramvalue-prop-flags": "Markiert die Sperre mit (autoblock, anononly, etc.).",
+       "apihelp-query+blocks-param-show": "Zeige nur Elemente, die diese Kriterien erfüllen. Um zum Beispiel unbestimmte Sperren von IP-Adressen zu sehen, setzte <kbd>$1show=ip|!temp</kbd>.",
        "apihelp-query+blocks-example-simple": "Sperren auflisten",
+       "apihelp-query+blocks-example-users": "Listet Sperren der Benutzer <kbd>Alice</kbd> und <kbd>Bob</kbd> auf.",
+       "apihelp-query+categories-description": "Liste alle Kategorien auf, zu denen die Seiten gehören.",
+       "apihelp-query+categories-param-prop": "Welche zusätzlichen Eigenschaften für jede Kategorie abrufen:",
+       "apihelp-query+categories-paramvalue-prop-timestamp": "Fügt einen Zeitstempel wann die Kategorie angelegt wurde hinzu.",
+       "apihelp-query+categories-param-show": "Welche Art von Kategorien gezeigt werden soll.",
+       "apihelp-query+categories-param-limit": "Wie viele Kategorien zurückgegeben werden sollen.",
+       "apihelp-query+categories-param-categories": "Liste nur diese Kategorien auf. Nützlich um zu prüfen, ob eine bestimmte Seite in einer bestimmten Kategorie enthalten ist.",
+       "apihelp-query+categories-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+categories-example-simple": "Rufe eine Liste von Kategorien ab, zu denen die Seite <kbd>Albert Einstein</kbd> gehört.",
+       "apihelp-query+categories-example-generator": "Rufe Informationen über alle Kategorien ab, die in der Seite <kbd>Albert Einstein</kbd> eingetragen sind.",
+       "apihelp-query+categoryinfo-description": "Gibt Informationen zu den angegebenen Kategorien zurück.",
+       "apihelp-query+categoryinfo-example-simple": "Erhalte Informationen über <kbd>Category:Foo</kbd> und <kbd>Category:Bar</kbd>.",
+       "apihelp-query+categorymembers-description": "Liste alle Seiten in der angegebenen Kategorie auf.",
+       "apihelp-query+categorymembers-param-pageid": "Seitenkennung der Kategorie, die aufgelistet werden soll. Darf nicht zusammen mit <var>$1title</var> verwendet werden.",
+       "apihelp-query+categorymembers-param-prop": "Welche Informationsteile einbinden:",
+       "apihelp-query+categorymembers-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
+       "apihelp-query+categorymembers-paramvalue-prop-title": "Fügt die Titel- und Namensraum-ID der Seite hinzu.",
+       "apihelp-query+categorymembers-paramvalue-prop-sortkey": "Fügt den Sortierungsschlüssel (hexadezimale Zeichenkette) hinzu, der verwendet wird, um innerhalb dieser Kategorie zu sortieren.",
+       "apihelp-query+categorymembers-paramvalue-prop-sortkeyprefix": "Fügt das Sortierungsschlüssel-Präfix hinzu, das verwendet wird, um innerhalb dieser Kategorie zu sortieren (für Menschen lesbarer Teil des Sortierungsschlüssels).",
+       "apihelp-query+categorymembers-paramvalue-prop-type": "Fügt den Typ, als der diese Seite bestimmt wurde, hinzu (Seite, Unterkategorie oder Datei).",
+       "apihelp-query+categorymembers-paramvalue-prop-timestamp": "Fügt den Zeitstempel wann die Seite eingebunden wurde hinzu.",
+       "apihelp-query+categorymembers-param-limit": "Die maximale Anzahl der zurückzugebenden Seiten.",
+       "apihelp-query+categorymembers-param-sort": "Eigenschaft, nach der sortiert werden soll.",
+       "apihelp-query+categorymembers-param-dir": "Sortierungsrichtung.",
+       "apihelp-query+categorymembers-param-start": "Zeitstempel bei dem die Auflistung beginnen soll. Darf nur zusammen mit <kbd>$1sort=timestamp</kbd> benutzt werden.",
+       "apihelp-query+categorymembers-param-end": "Zeitstempel bei dem die Auflistung enden soll. Darf nur zusammen mit <kbd>$1sort=timestamp</kbd> benutzt werden.",
+       "apihelp-query+categorymembers-param-starthexsortkey": "Sortierungsschlüssel bei dem die Auflistung beginnen soll, wie von <kbd>$1prop=sortkey</kbd> zurückgegeben. Darf nur zusammen mit <kbd>$1sort=sortkey</kbd> verwendet werden.",
+       "apihelp-query+categorymembers-param-endhexsortkey": "Suchschlüssel bei dem die Auflistung enden soll, wie von <kbd>$1prop=sortkey</kbd> zurückgegeben. Darf nur zusammen mit <kbd>$1sort=sortkey</kbd> verwendet werden.",
        "apihelp-query+categorymembers-param-startsortkey": "Stattdessen $1starthexsortkey verwenden.",
        "apihelp-query+categorymembers-param-endsortkey": "Stattdessen $1endhexsortkey verwenden.",
+       "apihelp-query+categorymembers-example-simple": "Rufe die ersten 10 Seiten von <kbd>Category:Physics</kbd> ab.",
+       "apihelp-query+categorymembers-example-generator": "Rufe die Seiteninformationen zu den ersten 10 Seiten von<kbd>Category:Physics</kbd> ab.",
+       "apihelp-query+contributors-description": "Rufe die Liste der angemeldeten Bearbeiter und die Zahl anonymer Bearbeiter einer Seite ab.",
        "apihelp-query+contributors-param-limit": "Wie viele Spender zurückgegeben werden sollen.",
+       "apihelp-query+contributors-example-simple": "Zeige Mitwirkende der Seite <kbd>Main Page</kbd>.",
+       "apihelp-query+deletedrevisions-param-start": "Der Zeitstempel bei dem die Auflistung beginnen soll. Wird bei der Verarbeitung einer Liste von Bearbeitungs-IDs ignoriert.",
+       "apihelp-query+deletedrevisions-param-end": "Der Zeitstempel bei dem die Auflistung enden soll. Wird bei der Verarbeitung einer List von Bearbeitungs-IDs ignoriert.",
+       "apihelp-query+deletedrevisions-param-tag": "Listet nur Bearbeitungen auf, die die angegebene Markierung haben.",
        "apihelp-query+deletedrevisions-param-user": "Nur Versionen von diesem Benutzer auflisten.",
+       "apihelp-query+deletedrevisions-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
+       "apihelp-query+deletedrevisions-example-titles": "Listet die gelöschten Bearbeitungen der Seiten <kbd>Main Page</kbd> und <kbd>Talk:Main Page</kbd> samt Inhalt auf.",
+       "apihelp-query+deletedrevisions-example-revids": "Liste Informationen zur gelöschten Bearbeitung <kbd>123456</kbd>.",
+       "apihelp-query+deletedrevs-description": "Liste gelöschte Bearbeitungen.\n\nArbeitet in drei Modi:\n# Listet gelöschte Bearbeitungen des angegeben Titels auf, sortiert nach dem Zeitstempel.\n# Listet gelöschte Beiträge des angegebenen Benutzers auf, sortiert nach dem Zeitstempel (keine Titel bestimmt)\n# Listet alle gelöschten Bearbeitungen im angegebenen Namensraum auf, sortiert nach Titel und Zeitstempel (keine Titel bestimmt, $1user nicht gesetzt).\n\nBestimmte Parameter wirken nur bei bestimmten Modi und werden in anderen nicht berücksichtigt.",
+       "apihelp-query+deletedrevs-paraminfo-modes": "{{PLURAL:$1|Modus|Modi}}: $2",
+       "apihelp-query+deletedrevs-param-start": "Der Zeitstempel bei dem die Auflistung beginnen soll.",
+       "apihelp-query+deletedrevs-param-end": "Der Zeitstempel bei dem die Auflistung enden soll.",
        "apihelp-query+deletedrevs-param-from": "Auflistung bei diesem Titel beginnen.",
        "apihelp-query+deletedrevs-param-to": "Auflistung bei diesem Titel beenden.",
+       "apihelp-query+deletedrevs-param-prefix": "Suche nach allen Seitentiteln, die mit dem angegebenen Wert beginnen.",
+       "apihelp-query+deletedrevs-param-unique": "Listet nur eine Bearbeitung für jede Seite auf.",
+       "apihelp-query+deletedrevs-param-tag": "Listet nur Bearbeitungen auf, die die angegebene Markierung haben.",
+       "apihelp-query+deletedrevs-param-user": "Liste nur Bearbeitungen von diesem Benutzer auf.",
+       "apihelp-query+deletedrevs-param-excludeuser": "Schließe Bearbeitungen dieses Benutzers bei der Auflistung aus.",
+       "apihelp-query+deletedrevs-param-namespace": "Nur Seiten dieses Namensraums auflisten.",
+       "apihelp-query+deletedrevs-param-limit": "Die maximale Anzahl aufzulistendender Bearbeitungen.",
+       "apihelp-query+deletedrevs-example-mode1": "Liste die letzten gelöschten Bearbeitungen der Seiten <kbd>Main Page</kbd> und <kbd>Talk:Main Page</kbd> samt Inhalt (Modus 1).",
+       "apihelp-query+deletedrevs-example-mode2": "Liste die letzten 50 gelöschten Beiträge von <kbd>Bob</kbd> auf (Modus 2).",
+       "apihelp-query+deletedrevs-example-mode3-main": "Liste die ersten 50 gelöschten Bearbeitungen im Hauptnamensraum (Modus 3).",
+       "apihelp-query+deletedrevs-example-mode3-talk": "Liste die ersten 50 gelöschten Seiten im {{ns:talk}}-Namensraum (Modus 3).",
+       "apihelp-query+disabled-description": "Dieses Abfrage-Modul wurde deaktiviert.",
+       "apihelp-query+duplicatefiles-description": "Liste alle Dateien auf die, basierend auf der Prüfsumme, Duplikate der angegebenen Dateien sind.",
+       "apihelp-query+duplicatefiles-param-limit": "Wie viele doppelte Dateien zurückgeben.",
+       "apihelp-query+duplicatefiles-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+duplicatefiles-param-localonly": "Sucht nur nach Dateien im lokalen Repositorium.",
        "apihelp-query+duplicatefiles-example-simple": "Sucht nach Duplikaten von [[:File:Albert Einstein Head.jpg]].",
        "apihelp-query+duplicatefiles-example-generated": "Sucht nach Duplikaten aller Dateien.",
+       "apihelp-query+embeddedin-description": "Finde alle Seiten, die den angegebenen Titel einbetten (transkludieren).",
+       "apihelp-query+embeddedin-param-title": "Titel nach dem gesucht werden soll. Darf nicht zusammen mit $1pageid verwendet werden.",
+       "apihelp-query+embeddedin-param-pageid": "Seitenkennung nach der gesucht werden soll. Darf nicht zusammen mit $1title verwendet werden.",
        "apihelp-query+embeddedin-param-namespace": "Der aufzulistende Namensraum.",
+       "apihelp-query+embeddedin-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+embeddedin-param-filterredir": "Wie Weiterleitungen behandelt werden sollen.",
        "apihelp-query+embeddedin-param-limit": "Wie viele Seiten insgesamt zurückgegeben werden sollen.",
+       "apihelp-query+embeddedin-example-simple": "Zeige Seiten, die <kbd>Template:Stub</kbd> transkludieren.",
+       "apihelp-query+embeddedin-example-generator": "Rufe Informationen über Seiten ab, die <kbd>Template:Stub</kbd> transkludieren.",
+       "apihelp-query+extlinks-description": "Gebe alle externen URLs (nicht Interwiki) der angegebenen Seiten zurück.",
        "apihelp-query+extlinks-param-limit": "Wie viele Links zurückgegeben werden sollen.",
+       "apihelp-query+extlinks-param-query": "Suchbegriff ohne Protokoll. Nützlich um zu prüfen, ob eine bestimmte Seite eine bestimmte externe URL enthält.",
+       "apihelp-query+extlinks-example-simple": "Rufe eine Liste erxterner Verweise auf <kbd>Main Page</kbd> ab.",
+       "apihelp-query+exturlusage-description": "Listet Seiten auf, die die angegebene URL beinhalten.",
+       "apihelp-query+exturlusage-param-prop": "Welche Informationsteile einbinden:",
+       "apihelp-query+exturlusage-paramvalue-prop-ids": "Fügt die ID der Seite hinzu.",
+       "apihelp-query+exturlusage-paramvalue-prop-title": "Fügt die Titel- und Namensraum-ID der Seite hinzu.",
+       "apihelp-query+exturlusage-paramvalue-prop-url": "Fügt die URL, die in der Seite verwendet wird, hinzu.",
+       "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+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+filearchive-param-prefix": "Nach allen Bildtiteln, die mit diesem Wert beginnen suchen.",
        "apihelp-query+filearchive-param-limit": "Wie viele Bilder insgesamt zurückgegeben werden sollen.",
+       "apihelp-query+filearchive-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+filearchive-param-sha1": "SHA1-Prüfsumme des Bildes. Überschreibt $1sha1base36.",
+       "apihelp-query+filearchive-param-sha1base36": "SHA1-Prüfsumme des Bildes in Base-36 (in MediaWiki verwendet).",
+       "apihelp-query+filearchive-param-prop": "Welche Bildinformationen abgerufen werden sollen:",
        "apihelp-query+filearchive-paramvalue-prop-sha1": "Ergänzt die SHA-1-Prüfsumme für das Bild.",
+       "apihelp-query+filearchive-paramvalue-prop-timestamp": "Fügt einen Zeitstempel für die hochgeladene Version hinzu.",
+       "apihelp-query+filearchive-paramvalue-prop-user": "Fügt den Benutzer hinzu, der die Bildversion hochgeladen hat.",
+       "apihelp-query+filearchive-paramvalue-prop-size": "Fügt die Größe des Bilde in Bytes sowie die Höhe, Breite und (falls zutreffend) die Seitenzahl hinzu.",
        "apihelp-query+filearchive-paramvalue-prop-dimensions": "Alias für die Größe.",
+       "apihelp-query+filearchive-paramvalue-prop-description": "Fügt die Beschreibung der Bildversion hinzu.",
+       "apihelp-query+filearchive-paramvalue-prop-parseddescription": "Analysiert die Beschreibung der Version.",
+       "apihelp-query+filearchive-paramvalue-prop-mime": "Fügt den MIME-Typ des Bildes hinzu.",
        "apihelp-query+filearchive-paramvalue-prop-mediatype": "Ergänzt den Medientyp des Bildes.",
+       "apihelp-query+filearchive-paramvalue-prop-metadata": "Listet die Exif-Metadaten dieser Bildversion auf.",
        "apihelp-query+filearchive-paramvalue-prop-bitdepth": "Ergänzt die Bittiefe der Version.",
+       "apihelp-query+filearchive-paramvalue-prop-archivename": "Fügt den Dateinamen der Archivversion für die nicht-neuesten Versionen hinzu.",
        "apihelp-query+filearchive-example-simple": "Eine Liste aller gelöschten Dateien auflisten",
+       "apihelp-query+filerepoinfo-description": "Gebe Metainformationen über Bild-Repositorien zurück, die im Wiki eingerichtet sind.",
        "apihelp-query+filerepoinfo-example-simple": "Ruft Informationen über Dateirepositorien ab.",
+       "apihelp-query+fileusage-description": "Alle Seiten finden, die die angegebenen Dateien verwenden.",
+       "apihelp-query+fileusage-param-prop": "Zurückzugebende Eigenschaften:",
        "apihelp-query+fileusage-paramvalue-prop-pageid": "Seitenkennung jeder Seite.",
        "apihelp-query+fileusage-paramvalue-prop-title": "Titel jeder Seite.",
+       "apihelp-query+fileusage-paramvalue-prop-redirect": "Markieren, falls die Seite eine Weiterleitung ist.",
+       "apihelp-query+fileusage-param-namespace": "Nur Seiten dieser Namensräume einbinden.",
        "apihelp-query+fileusage-param-limit": "Wie viel zurückgegeben werden soll.",
+       "apihelp-query+fileusage-example-simple": "Zeige eine Liste von Seiten, die [[:File:Example.jpg]] verwenden.",
+       "apihelp-query+fileusage-example-generator": "Zeige Informationen über Seiten, die [[:File:Example.jpg]] verwenden.",
        "apihelp-query+imageinfo-description": "Gibt Informationen und alle Versionen der Datei zurück.",
        "apihelp-query+imageinfo-param-prop": "Welche Dateiinformationen abgerufen werden sollen:",
+       "apihelp-query+imageinfo-paramvalue-prop-timestamp": "Fügt einen Zeitstempel für die hochgeladene Version hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-user": "Fügt den Benutzer zu jeder hochgeladenen Dateiversion hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-userid": "Füge die ID des Benutzers zu jeder hochgeladenen Dateiversion hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-comment": "Kommentar zu der Version.",
+       "apihelp-query+imageinfo-paramvalue-prop-parsedcomment": "Analysiere den Kommentar zu dieser Version.",
+       "apihelp-query+imageinfo-paramvalue-prop-url": "Gibt die URL zur Datei- und Beschreibungsseite zurück.",
+       "apihelp-query+imageinfo-paramvalue-prop-size": "Fügt die Größe der Datei in Bytes und (falls zutreffend) in Höhe, Breite und Seitenzahl hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-dimensions": "Alias für die Größe.",
+       "apihelp-query+imageinfo-paramvalue-prop-sha1": "Fügt die SHA-1-Prüfsumme für die Datei hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-mime": "Fügt den MIME-Typ dieser Datei hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-mediatype": "Fügt den Medientyp dieser Datei hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-metadata": "Listet die Exif-Metadaten dieser Dateiversion auf.",
+       "apihelp-query+imageinfo-paramvalue-prop-commonmetadata": "Listet allgemeine Metadaten des Dateiformats dieser Dateiversion auf.",
+       "apihelp-query+imageinfo-paramvalue-prop-extmetadata": "Listet formatierte Metadaten kombiniert aus mehreren Quellen auf. Die Ergebnisse sind im HTML-Format.",
+       "apihelp-query+imageinfo-paramvalue-prop-archivename": "Fügt den Dateinamen der Archivversion für die nicht-letzten Versionen hinzu.",
+       "apihelp-query+imageinfo-paramvalue-prop-bitdepth": "Fügt die Bittiefe der Version hinzu.",
        "apihelp-query+imageinfo-param-limit": "Wie viele Dateiversionen pro Datei zurückgegeben werden sollen.",
        "apihelp-query+imageinfo-param-start": "Zeitstempel, von dem die Liste beginnen soll.",
        "apihelp-query+imageinfo-param-end": "Zeitstempel, an dem die Liste enden soll.",
        "apihelp-query+imageinfo-param-urlheight": "Ähnlich wie $1urlwidth.",
+       "apihelp-query+imageinfo-param-localonly": "Suche nur nach Dateien im lokalen Repositorium.",
+       "apihelp-query+imageinfo-example-simple": "Rufe Informationen über die aktuelle Version von [[:File:Albert Einstein Head.jpg]] ab.",
+       "apihelp-query+imageinfo-example-dated": "Rufe Informationen über Versionen von [[:File:Test.jpg]] von 2008 und später ab.",
+       "apihelp-query+images-description": "Gibt alle Dateien zurück, die in den angegebenen Seiten enthalten sind.",
+       "apihelp-query+images-param-limit": "Wie viele Dateien zurückgegeben werden sollen.",
+       "apihelp-query+images-param-images": "Nur diese Dateien auflisten. Nützlich um zu prüfen, ob eine bestimmte Seite eine bestimmte Datei enthält.",
+       "apihelp-query+images-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+images-example-simple": "Rufe eine Liste von Dateien ab, die auf der [[Main Page]] verwendet werden.",
+       "apihelp-query+images-example-generator": "Rufe Informationen über alle Dateien ab, die auf der [[Main Page]] verwendet werden.",
+       "apihelp-query+imageusage-description": "Finde alle Seiten, die den angegebenen Bildtitel verwenden.",
+       "apihelp-query+imageusage-param-title": "Titel nach dem gesucht werden soll. Darf nicht zusammen mit $1pageid verwendet werden.",
+       "apihelp-query+imageusage-param-pageid": "Seitenkennung nach der gesucht werden soll. Darf nicht zusammen mit $1title verwendet werden.",
+       "apihelp-query+imageusage-param-namespace": "Der aufzulistende Namensraum.",
+       "apihelp-query+imageusage-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+imageusage-param-redirect": "Falls die verweisende Seite eine Weiterleitung ist, finde alle Seiten, die ebenfalls auf diese Weiterleitung verweisen. Die maximale Grenze wird halbiert.",
+       "apihelp-query+imageusage-example-simple": "Zeige Seiten, die [[:File:Albert Einstein Head.jpg]] verwenden.",
        "apihelp-query+info-description": "Ruft Basisinformationen über die Seite ab.",
+       "apihelp-query+info-param-prop": "Welche zusätzlichen Eigenschaften abgerufen werden sollen:",
+       "apihelp-query+info-paramvalue-prop-protection": "Liste die Schutzstufe jeder Seite auf.",
+       "apihelp-query+info-paramvalue-prop-talkid": "Die Seitenkennung der Diskussionsseite für jede Nicht-Diskussionsseite.",
+       "apihelp-query+info-paramvalue-prop-watched": "Liste den Überwachungszustand jeder Seite auf.",
        "apihelp-query+info-paramvalue-prop-watchers": "Die Anzahl der Beobachter, falls erlaubt.",
+       "apihelp-query+info-paramvalue-prop-notificationtimestamp": "Der Beobachtungslisten-Benachrichtigungs-Zeitstempel jeder Seite.",
+       "apihelp-query+info-paramvalue-prop-subjectid": "Die Seitenkennung der Elternseite jeder Diskussionsseite.",
+       "apihelp-query+info-paramvalue-prop-readable": "Ob der Benutzer diese Seite betrachten darf.",
+       "apihelp-query+info-paramvalue-prop-displaytitle": "Gibt die Art und Weise an, in der der Seitentitel tatsächlich angezeigt wird.",
        "apihelp-query+info-param-testactions": "Überprüft, ob der aktuelle Benutzer gewisse Aktionen auf der Seite ausführen kann.",
        "apihelp-query+iwbacklinks-param-prefix": "Präfix für das Interwiki.",
        "apihelp-query+iwbacklinks-paramvalue-prop-iwprefix": "Ergänzt das Präfix des Interwikis.",
        "apihelp-query+iwbacklinks-paramvalue-prop-iwtitle": "Ergänzt den Titel des Interwikis.",
+       "apihelp-query+iwbacklinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+iwlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
+       "apihelp-query+iwlinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+langbacklinks-param-limit": "Wie viele Gesamtseiten zurückgegeben werden sollen.",
+       "apihelp-query+langbacklinks-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+langbacklinks-example-simple": "Ruft Seiten ab, die auf [[:fr:Test]] verlinken.",
        "apihelp-query+langlinks-param-limit": "Wie viele Sprachlinks zurückgegeben werden sollen.",
        "apihelp-query+langlinks-paramvalue-prop-url": "Ergänzt die vollständige URL.",
+       "apihelp-query+langlinks-param-dir": "Die Auflistungsrichtung.",
+       "apihelp-query+links-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+links-example-simple": "Links von der <kbd>Hauptseite</kbd> abrufen",
        "apihelp-query+linkshere-description": "Alle Seiten finden, die auf die angegebenen Seiten verlinken.",
        "apihelp-query+logevents-description": "Ereignisse von den Logbüchern abrufen.",
+       "apihelp-query+logevents-example-simple": "Listet die letzten Logbuch-Ereignisse auf.",
        "apihelp-query+pageswithprop-paramvalue-prop-ids": "Fügt die Seitenkennung hinzu.",
+       "apihelp-query+pageswithprop-param-limit": "Die maximale Anzahl zurückzugebender Seiten.",
        "apihelp-query+prefixsearch-param-search": "Such-Zeichenfolge.",
+       "apihelp-query+prefixsearch-param-offset": "Anzahl der zu überspringenden Ergebnisse.",
        "apihelp-query+recentchanges-paramvalue-prop-timestamp": "Ergänzt den Zeitstempel für die Bearbeitung.",
        "apihelp-query+recentchanges-paramvalue-prop-tags": "Listet Markierungen für den Eintrag auf.",
        "apihelp-query+recentchanges-example-simple": "Listet die letzten Änderungen auf.",
        "apihelp-query+siteinfo-example-simple": "Websiteinformationen abrufen",
        "apihelp-query+tags-description": "Änderungs-Tags auflisten.",
        "apihelp-query+tags-example-simple": "Verfügbare Tags auflisten",
+       "apihelp-query+templates-param-dir": "Die Auflistungsrichtung.",
        "apihelp-query+usercontribs-description": "Alle Bearbeitungen von einem Benutzer abrufen.",
        "apihelp-query+usercontribs-param-limit": "Die maximale Anzahl der zurückzugebenden Beiträge.",
        "apihelp-query+usercontribs-param-start": "Der zurückzugebende Start-Zeitstempel.",
        "apihelp-query+usercontribs-paramvalue-prop-ids": "Fügt die Seiten- und Versionskennung hinzu.",
        "apihelp-query+usercontribs-paramvalue-prop-timestamp": "Ergänzt den Zeitstempel der Bearbeitung.",
        "apihelp-query+usercontribs-paramvalue-prop-comment": "Fügt den Kommentar der Bearbeitung hinzu.",
+       "apihelp-query+userinfo-paramvalue-prop-blockinfo": "Markiert, ob der aktuelle Benutzer gesperrt ist, von wem und aus welchem Grund.",
        "apihelp-query+userinfo-paramvalue-prop-editcount": "Ergänzt den Bearbeitungszähler des aktuellen Benutzers.",
        "apihelp-query+userinfo-paramvalue-prop-realname": "Fügt den bürgerlichen Namen des Benutzers hinzu.",
        "apihelp-query+userinfo-example-simple": "Informationen über den aktuellen Benutzer abrufen",
+       "apihelp-query+userinfo-example-data": "Ruft zusätzliche Informationen über den aktuellen Benutzer ab.",
        "apihelp-query+users-description": "Informationen über eine Liste von Benutzern abrufen.",
+       "apihelp-query+users-param-prop": "Welche Informationsteile einbezogen werden sollen:",
+       "apihelp-query+users-paramvalue-prop-blockinfo": "Markiert, ob der Benutzer gesperrt ist, von wem und aus welchem Grund.",
+       "apihelp-query+users-paramvalue-prop-groups": "Listet alle Gruppen auf, zu denen jeder Benutzer gehört.",
+       "apihelp-query+users-paramvalue-prop-implicitgroups": "Listet alle Gruppen auf, bei denen der Benutzer automatisch Mitglied ist.",
+       "apihelp-query+users-paramvalue-prop-rights": "Listet alle Rechte auf, die jeder Benutzer hat.",
+       "apihelp-query+users-paramvalue-prop-editcount": "Ergänzt den Bearbeitungszähler des Benutzers.",
        "apihelp-query+users-example-simple": "Gibt Informationen für den Benutzer <kbd>Example</kbd> zurück.",
        "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-text": "Seiteninhalt.",
        "apihelp-tag-param-reason": "Grund für die Änderung.",
        "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-reason": "Grund für die Freigabe.",
        "apihelp-unblock-example-id": "Sperrkennung #<kbd>105</kbd> freigeben.",
        "apihelp-undelete-param-reason": "Grund für die Wiederherstellung.",
        "apihelp-upload-param-filename": "Ziel-Dateiname.",
+       "apihelp-upload-param-text": "Erster Seitentext für neue Dateien.",
        "apihelp-upload-param-watch": "Die Seite beobachten.",
+       "apihelp-upload-param-ignorewarnings": "Ignoriert Warnungen.",
        "apihelp-upload-param-file": "Dateiinhalte.",
        "apihelp-upload-param-url": "URL, von der die Datei abgerufen werden soll.",
+       "apihelp-upload-param-filesize": "Dateigröße des gesamten Uploads.",
+       "apihelp-upload-param-checkstatus": "Ruft nur den Hochladestatus für den angegebenen Dateischlüssel ab.",
        "apihelp-upload-example-url": "Von einer URL hochladen",
+       "apihelp-upload-example-filekey": "Vervollständigt einen Upload, der aufgrund von Warnungen fehlgeschlagen ist.",
+       "apihelp-userrights-description": "Ändert die Gruppenzugehörigkeit eines Benutzers.",
        "apihelp-userrights-param-user": "Benutzername.",
        "apihelp-userrights-param-userid": "Benutzerkennung.",
+       "apihelp-userrights-param-add": "Fügt den Benutzer zu diesen Gruppen hinzu.",
+       "apihelp-userrights-param-remove": "Entfernt den Benutzer von diesen Gruppen.",
+       "apihelp-userrights-param-reason": "Grund für die Änderung.",
        "apihelp-watch-example-watch": "Die Seite <kbd>Main Page</kbd> beobachten.",
        "apihelp-watch-example-unwatch": "Die Seite <kbd>Main Page</kbd> nicht beobachten.",
        "apihelp-format-example-generic": "Das Abfrageergebnis im $1-Format ausgeben.",
        "api-help-datatypes-header": "Datentypen",
        "api-help-param-type-limit": "Typ: Ganzzahl oder <kbd>max</kbd>",
        "api-help-param-type-integer": "Typ: {{PLURAL:$1|1=Ganzzahl|2=Liste von Ganzzahlen}}",
+       "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-can-be-empty": "{{PLURAL:$1|0=Muss leer sein|Kann leer sein oder $2}}",
        "api-help-param-limit": "Nicht mehr als $1 erlaubt.",
index ea85411..2fb9a50 100644 (file)
        "apihelp-query+allrevisions-example-user": "List the last 50 contributions by user <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "List the first 50 revisions in the main namespace.",
 
+       "apihelp-query+mystashedfiles-description": "Get a list of files in the current user's upload stash.",
+       "apihelp-query+mystashedfiles-param-prop": "Which properties to fetch for the files.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "Fetch the file size and image dimensions.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "Fetch the file's MIME type and media type.",
+       "apihelp-query+mystashedfiles-param-limit": "How many files to get.",
+       "apihelp-query+mystashedfiles-example-simple": "Get the filekey, file size, and pixel size of files in the current user's upload stash.",
+
        "apihelp-query+alltransclusions-description": "List all transclusions (pages embedded using &#123;&#123;x&#125;&#125;), including non-existing.",
        "apihelp-query+alltransclusions-param-from": "The title of the transclusion to start enumerating from.",
        "apihelp-query+alltransclusions-param-to": "The title of the transclusion to stop enumerating at.",
index 91d8faf..779a1e2 100644 (file)
        "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+alltransclusions-param-prefix": "Buscar todos los títulos transcluídos que comiencen con este valor.",
+       "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-example-unique": "Listar títulos transcluidos de forma única.",
-       "apihelp-query+alltransclusions-example-unique-generator": "Obtiene todos los títulos transcluídos, marcando los que faltan.",
+       "apihelp-query+alltransclusions-example-unique-generator": "Obtiene todos los títulos transcluidos, marcando los que faltan.",
        "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+tags-paramvalue-prop-displayname": "Agrega el mensaje de sistema para la etiqueta.",
        "apihelp-query+tags-paramvalue-prop-source": "Obtiene las fuentes de la etiqueta, que pueden incluir <samp>extension</samp> para etiquetas definidas por extensiones y <samp>manual</samp> para etiquetas que pueden aplicarse manualmente por los usuarios.",
        "apihelp-query+tags-paramvalue-prop-active": "Si la etiqueta aún se sigue aplicando.",
-       "apihelp-query+templates-description": "Devuelve todas las páginas transcluídas en las páginas dadas.",
+       "apihelp-query+templates-description": "Devuelve todas las páginas transcluidas en las páginas dadas.",
        "apihelp-query+templates-param-limit": "Cuántas plantillas se devolverán.",
        "apihelp-query+transcludedin-description": "Encuentra todas las páginas que transcluyan las páginas dadas.",
        "apihelp-query+transcludedin-param-prop": "Qué propiedades se obtendrán:",
index dc8b4b5..6b678ef 100644 (file)
        "apihelp-query+allrevisions-param-generatetitles": "Utilisé comme générateur, génère des titres plutôt que des IDs de révision.",
        "apihelp-query+allrevisions-example-user": "Lister les 50 dernières contributions de l’utilisateur <kbd>Exemple</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "Lister les 50 premières révisions dans l’espace de noms principal.",
+       "apihelp-query+mystashedfiles-description": "Obtenir une liste des fichiers dans le cache de téléchargement de l’utilisateur actuel",
+       "apihelp-query+mystashedfiles-param-prop": "Quelles propriétés récupérer pour les fichiers.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "Récupérer la taille du fichier et les dimensions de l’image.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "Récupérer le type MIME du fichier et son type de média.",
+       "apihelp-query+mystashedfiles-param-limit": "Combien de fichiers obtenir.",
+       "apihelp-query+mystashedfiles-example-simple": "Obtenir la clé du fichier, sa taille, et la taille en pixel des fichiers dans le cache de téléchargement de l’utilisateur actuel.",
        "apihelp-query+alltransclusions-description": "Lister toutes les transclusions (pages intégrées en utilisant &#123;&#123;x&#125;&#125;), y compris les inexistantes.",
        "apihelp-query+alltransclusions-param-from": "Le titre de la transclusion depuis lequel commencer l’énumération.",
        "apihelp-query+alltransclusions-param-to": "Le titre de la transclusion auquel arrêter l’énumération.",
        "apihelp-query+pageswithprop-param-dir": "Dans quelle direction trier.",
        "apihelp-query+pageswithprop-example-simple": "Lister les 10 premières pages en utilisant <code>&#123;&#123;DISPLAYTITLE:&#125;&#125;</code>.",
        "apihelp-query+pageswithprop-example-generator": "Obtenir des informations supplémentaires sur les 10 premières pages utilisant <code>_&#95;NOTOC_&#95;</code>.",
-       "apihelp-query+prefixsearch-description": "Effectuer une recherche de préfixe sur les titres de page.",
+       "apihelp-query+prefixsearch-description": "Effectuer une recherche de préfixe sur les titres de page.\n\nMalgré les similarités dans le nom, ce module n’est pas destiné à être l’équivalent de [[Special:PrefixIndex]] ; pour cela, voyez <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> avec le paramètre <kbd>apprefix</kbd>. Le but de ce module est similaire à <kbd>[[Special:ApiHelp/opensearch|action=opensearch]]</kbd> : prendre l’entrée utilisateur et fournir les meilleurs titres s’en approchant. Selon le serveur du moteur de recherche, cela peut inclure corriger des fautes de frappe, éviter des redirections, ou d’autres heuristiques.",
        "apihelp-query+prefixsearch-param-search": "Chaîne de recherche.",
        "apihelp-query+prefixsearch-param-namespace": "Espaces de nom à rechercher.",
        "apihelp-query+prefixsearch-param-limit": "Nombre maximal de résultats à renvoyer.",
index 9d69078..ed1dd44 100644 (file)
        "apihelp-edit-param-appendtext": "Engadir este texto no final da páxina. Ignorar $1text.\n\nUse $1section=new para engadir unha nova sección, máis que este parámetro.",
        "apihelp-edit-param-undo": "Desfacer esta revisión. Ignorar $1text, $1prependtext e $1appendtext.",
        "apihelp-edit-param-undoafter": "Desfacer tódalas revisións dende $1undo ata esta. Se non está definido, só desfacer unha revisión.",
-       "apihelp-edit-param-redirect": "Resolver redireccións automáticamente",
+       "apihelp-edit-param-redirect": "Resolver redireccións automaticamente",
        "apihelp-edit-param-contentformat": "Formato de serialización de contido utilizado para o texto de entrada.",
        "apihelp-edit-param-contentmodel": "Modelo de contido para o novo contido.",
        "apihelp-edit-param-token": "O identificador debería enviarse empre como o último parámetro, ou polo menos despois do parámetro $1text.",
        "apihelp-feedrecentchanges-param-target": "Mostrar só os cambios nas páxinas ligadas a esta.",
        "apihelp-feedrecentchanges-param-showlinkedto": "Mostrar os cambios nas páxinas ligadas coa páxina seleccionada.",
        "apihelp-feedrecentchanges-param-categories": "Só mostrar cambios en páxinas pertencentes a todas estas categorías.",
+       "apihelp-feedrecentchanges-param-categories_any": "Só mostrar cambios en páxinas pertencentes a calquera das categorías.",
        "apihelp-feedrecentchanges-example-simple": "Mostrar os cambios recentes",
        "apihelp-feedrecentchanges-example-30days": "Mostrar os cambios recentes limitados a 30 días",
        "apihelp-feedwatchlist-description": "Devolve o fluxo dunha lista de vixiancia.",
        "apihelp-query+allrevisions-param-generatetitles": "Usado como xenerador, xenera títulos no canto de IDs de revisión.",
        "apihelp-query+allrevisions-example-user": "Listar as últimas 50 contribucións do usuario <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "Listar as 50 primeiras revisións do espazo de nomes principal.",
+       "apihelp-query+mystashedfiles-param-prop": "Que propiedades obter para os ficheiros.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "Consultar o tamaño de ficheiro e as dimensións da imaxe.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "Consultar o tipo MIME do ficheiro e tipo multimedia.",
+       "apihelp-query+mystashedfiles-param-limit": "Cantos ficheiros devolver.",
        "apihelp-query+alltransclusions-description": "Listar todas as transclusións (páxinas integradas usando &#123;&#123;x&#125;&#125;), incluíndo as eliminadas.",
        "apihelp-query+alltransclusions-param-from": "Título da transclusión na que comezar a enumerar.",
        "apihelp-query+alltransclusions-param-to": "Título da transclusión na que rematar de enumerar.",
        "apihelp-query+allusers-paramvalue-prop-rights": "Lista os dereitos que ten o usuario.",
        "apihelp-query+allusers-paramvalue-prop-editcount": "Engade o número de edicións do usuario.",
        "apihelp-query+allusers-paramvalue-prop-registration": "Engade o selo de tempo do momento no que se rexistrou o usuario, se está dispoñible (pode ser branco).",
+       "apihelp-query+allusers-paramvalue-prop-centralids": "Engade os identificadores centrais e o estado de acoplamento do usuario.",
        "apihelp-query+allusers-param-limit": "Número total de nomes de usuario a devolver.",
        "apihelp-query+allusers-param-witheditsonly": "Só listar usuarios que teñan feito edicións.",
        "apihelp-query+allusers-param-activeusers": "Só listar usuarios activos {{PLURAL:$1|no último día|nos $1 últimos días}}.",
+       "apihelp-query+allusers-param-attachedwiki": "Con <kbd>$1prop=centralids</kbd>, \ntamén indica se o usuario está acoplado á wiki identificada por este identificador.",
        "apihelp-query+allusers-example-Y": "Listar usuarios que comecen por <kbd>Y</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+pageswithprop-param-dir": "En que dirección ordenar.",
        "apihelp-query+pageswithprop-example-simple": "Lista as dez primeiras páxinas que usan  <code>&#123;&#123;DISPLAYTITLE:&#125;&#125;</code>.",
        "apihelp-query+pageswithprop-example-generator": "Obter información adicional das dez primeiras páxinas que usan <code>_&#95;NOTOC_&#95;</code>.",
-       "apihelp-query+prefixsearch-description": "Facer unha busca de prefixo nos títulos das páxinas.",
+       "apihelp-query+prefixsearch-description": "Facer unha busca de prefixo nos títulos das páxinas.\nA pesar das semellanzas nos nomes, este módulo non pretende ser equivalente a [[Special:PrefixIndex]]; para iso consulte <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> co parámetro <kbd>apprefix</kbd>. O propósito deste módulo é semellante ó de <kbd>[[Special:ApiHelp/opensearch|action=opensearch]]</kbd>: para coller a entrada do usuario e proporcionar mellores os títulos que mellor se lle adapten. Dependendo do motor de buscas do servidor, isto pode incluír corrección de erros, evitar as redireccións, ou outras heurísticas.",
        "apihelp-query+prefixsearch-param-search": "Buscar texto.",
        "apihelp-query+prefixsearch-param-namespace": "Espazo de nomes no que buscar.",
        "apihelp-query+prefixsearch-param-limit": "Número máximo de resultados a visualizar.",
        "apihelp-rollback-description": "Desfacer a última modificación da páxina.\n\nSe o último usuario que modificou a páxina fixo varias modificacións nunha fila, desfaranse todas.",
        "apihelp-rollback-param-title": "Título da páxina a desfacer. Non pode usarse xunto con <var>$1pageid</var>.",
        "apihelp-rollback-param-pageid": "ID da páxina a desfacer. Non pode usarse xunto con <var>$1title</var>.",
+       "apihelp-rollback-param-tags": "Etiquetas a aplicar á reversión.",
        "apihelp-rollback-param-user": "Nome do usuario cuxas modificacións van a desfacerse.",
        "apihelp-rollback-param-summary": "Personalizar o resumo de edición. Se está baleiro, usarase o resumo por defecto.",
        "apihelp-rollback-param-markbot": "Marcar as edicións revertidas e a reversión como edicións de bot.",
index 54093ee..a38fba0 100644 (file)
        "apihelp-query+allrevisions-param-generatetitles": "בעת שימוש בתור מחולל, לחולל כותרת במקום מזהי גרסה.",
        "apihelp-query+allrevisions-example-user": "לרשום את 50 התרומות האחרונות של משתמש <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "רשימת 50 הגרסאות הראשונות במרחב הראשי.",
+       "apihelp-query+mystashedfiles-description": "קבלת רשימת קבצים בסליק ההעלאה של המשתמש הנוכחי.",
+       "apihelp-query+mystashedfiles-param-prop": "אילו מאפיינים לאחזר עבור הקבצים.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "אחזור גודל הקובץ וממדי התמונה.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "אחזור סוג ה־MIME של הקובץ וסוג המדיה.",
+       "apihelp-query+mystashedfiles-param-limit": "כמה קבצים לקבל.",
+       "apihelp-query+mystashedfiles-example-simple": "לקבל מפתח קובץ, גודל קובץ וגודל בפיקסלים של קבצים בסליק ההעלאה של המשתמש הנוכחי.",
        "apihelp-query+alltransclusions-description": "רשימת כל ההכללות (דפים שמוטבעים באמצעות &#123;&#123;x&#125;&#125;), כולל כאלה שאינם קיימים.",
        "apihelp-query+alltransclusions-param-from": "מאיזו כותרת ההכללה להתחיל למנות.",
        "apihelp-query+alltransclusions-param-to": "כותרת ההכללה שהמנייה תיפסק בה.",
        "apihelp-query+pageswithprop-param-dir": "באיזה כיוון לסדר.",
        "apihelp-query+pageswithprop-example-simple": "הצגת עשרת הדפים הראשונים שעושים שימוש ב־<code>&#123;&#123;DISPLAYTITLE:&#125;&#125;</code>.",
        "apihelp-query+pageswithprop-example-generator": "קבלת מידע נוסף על עשרת הדפים הראשונים המשתמשים ב־<code>_&#95;NOTOC_&#95;</code>.",
-       "apihelp-query+prefixsearch-description": "ביצוע חיפוש תחילית של כותרות דפים.",
+       "apihelp-query+prefixsearch-description": "ביצוע חיפוש תחילית של כותרות דפים.\n\nלמרות הדמיון בשם, המודול הזה אינו אמור להיות שווה ל־[[Special:PrefixIndex]] (\"מיוחד:דפים המתחילים ב\"); לדבר כזה, ר' <kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> עם הפרמטר <kbd>apprefix</kbd>. מטרת המודול הזה דומה ל־<kbd>[[Special:ApiHelp/opensearch|action=opensearch]]</kbd>: לקבל קלט ממשתמש ולספק את הכותרות המתאימות ביותר. בהתאם לשרת מנוע החיפוש, זה יכול לכלול תיקון שגיאות כתיב, הימנעות מדפי הפניה והירסטיקות אחרות.",
        "apihelp-query+prefixsearch-param-search": "מחרוזת לחיפוש.",
        "apihelp-query+prefixsearch-param-namespace": "שמות מתחם לחיפוש.",
        "apihelp-query+prefixsearch-param-limit": "מספר התוצאות המרבי להחזרה.",
        "apihelp-query+siteinfo-example-interwiki": "אחזור תחיליות בינוויקי מקומיות.",
        "apihelp-query+siteinfo-example-replag": "בדיקת שיהוי השכפול הנוכחי.",
        "apihelp-query+stashimageinfo-description": "החזרת מידע על הקובץ עבור הקבצים המוסלקים.",
-       "apihelp-query+stashimageinfo-param-filekey": "×\9eפת×\97 ×©×\9e×\96×\94×\94 ×\94×¢×\9c×\90×\94 ×§×\95×\93×\9eת ×©×\94×\95× ×\97×\94 ×\91צ×\93 באופן זמני.",
+       "apihelp-query+stashimageinfo-param-filekey": "×\9eפת×\97 ×©×\9e×\96×\94×\94 ×\94×¢×\9c×\90×\94 ×§×\95×\93×\9eת ×©×\94×\95ס×\9cק×\94 באופן זמני.",
        "apihelp-query+stashimageinfo-param-sessionkey": "כינוי ל־$1filekey, לתאימות אחורה.",
        "apihelp-query+stashimageinfo-example-simple": "החזרת מידע על קובץ מוסלק.",
        "apihelp-query+stashimageinfo-example-params": "החזרת תמונות ממוזערות עבור שני קבצים מוסלקים.",
        "apihelp-upload-param-ignorewarnings": "להתעלם מכל האזהרות.",
        "apihelp-upload-param-file": "תוכן הקובץ.",
        "apihelp-upload-param-url": "URL לאחזור הקובץ.",
-       "apihelp-upload-param-filekey": "×\9eפת×\97 ×©×\9e×\96×\94×\94 ×\94×¢×\9c×\90×\94 ×§×\95×\93×\9eת ×©×\94×\95× ×\97×\94 ×\91צ×\93 באופן זמני.",
+       "apihelp-upload-param-filekey": "×\9eפת×\97 ×©×\9e×\96×\94×\94 ×\94×¢×\9c×\90×\94 ×§×\95×\93×\9eת ×©×\94×\95ס×\9cק×\94 באופן זמני.",
        "apihelp-upload-param-sessionkey": "אותו דבר כמו $1filekey, מושאר לצור תאימות אחורה.",
-       "apihelp-upload-param-stash": "×\90×\9d ×\96×\94 ×\9e×\95×\92×\93ר, ×\94שרת ×\99ס×\9c×\99ק ×\90ת ×\94ק×\95×\91×¥ ×\96×\9e× ×\99ת במקום להוסיף אותו למאגר.",
+       "apihelp-upload-param-stash": "×\90×\9d ×\96×\94 ×\9e×\95×\92×\93ר, ×\94שרת ×\99ס×\9c×\99ק ×\96×\9e× ×\99ת ×\90ת ×\94ק×\95×\91×¥ במקום להוסיף אותו למאגר.",
        "apihelp-upload-param-filesize": "גודל הקובץ של כל ההעלאה.",
        "apihelp-upload-param-offset": "היסט החתיכה בבתים.",
        "apihelp-upload-param-chunk": "תוכן החתיכה.",
index 8b93819..5eaa449 100644 (file)
        "apihelp-delete-example-simple": "<kbd>Main Page</kbd> törlése.",
        "apihelp-edit-example-edit": "Lap szerkesztése",
        "apihelp-expandtemplates-param-title": "Lap címe.",
-       "apihelp-userrights-param-userid": "Felhasználói azonosító."
+       "apihelp-userrights-param-userid": "Felhasználói azonosító.",
+       "api-help-title": "MediaWiki API súgó",
+       "api-help-lead": "Ez egy automatikusan generált MediaWiki API-dokumentációs lap.\n\nDokumentáció és példák: https://www.mediawiki.org/wiki/API",
+       "api-help-main-header": "Fő modul",
+       "api-help-flag-deprecated": "Ez a modul elavult.",
+       "api-help-flag-internal": "<strong>Ez a modul belső használatú vagy nem stabil.</strong> A működése értesítés nélkül változhat.",
+       "api-help-flag-readrights": "Ez a modul olvasási jogot igényel.",
+       "api-help-flag-writerights": "Ez a modul írási jogot igényel.",
+       "api-help-flag-mustbeposted": "Ez a modul csak POST kéréseket fogad el.",
+       "api-help-flag-generator": "Ez a modul használható generátorként.",
+       "api-help-source": "Forrás: $1",
+       "api-help-source-unknown": "Forrás: <span class=\"apihelp-unknown\">ismeretlen</span>",
+       "api-help-license": "Licenc: [[$1|$2]]",
+       "api-help-license-noname": "Licenc: [[$1|Lásd a linken]]",
+       "api-help-license-unknown": "Licenc: <span class=\"apihelp-unknown\">ismeretlen</span>",
+       "api-help-parameters": "{{PLURAL:$1|Paraméter|Paraméterek}}:",
+       "api-help-param-deprecated": "Elavult.",
+       "api-help-param-required": "Ez a paraméter kötelező.",
+       "api-help-datatypes-header": "Adattípusok",
+       "api-help-param-type-limit": "Típus: egész vagy <kbd>max</kbd>",
+       "api-help-param-type-integer": "Típus: {{PLURAL:$1|1=egész|2=egészek listája}}",
+       "api-help-param-type-boolean": "Típus: logikai ([[Special:ApiHelp/main#main/datatypes|részletek]])",
+       "api-help-param-type-timestamp": "Típus: {{PLURAL:$1|1=időbélyeg|2=időbélyegek listája}} ([[Special:ApiHelp/main#main/datatypes|engedélyezett formátumok]])",
+       "api-help-param-type-user": "Típus: {{PLURAL:$1|1=felhasználónév|2=felhasználónevek listája}}",
+       "api-help-param-list": "{{PLURAL:$1|1=A következő értékek egyike|2=Értékek (elválasztó: <kbd>{{!}}</kbd>)}}: $2",
+       "api-help-param-list-can-be-empty": "{{PLURAL:$1|0=Üresnek kell lennie|Lehet üres vagy $2}}",
+       "api-help-param-limit": "Nem engedélyezett több mint $1.",
+       "api-help-param-limit2": "Nem engedélyezett több mint $1 (botoknak $2).",
+       "api-help-param-integer-min": "Az {{PLURAL:$1|1=érték nem lehet kisebb|2=értékek nem lehetnek kisebbek}} mint $2.",
+       "api-help-param-integer-max": "Az {{PLURAL:$1|1=érték nem lehet nagyobb|2=értékek nem lehetnek nagyobbak}} mint $3.",
+       "api-help-param-integer-minmax": "{{PLURAL:$1|1=Az értéknek $2 és $3 között kell lennie.|2=Az értékeknek $2 és $3 között kell lenniük.}}"
 }
index c4f7a3f..24f5157 100644 (file)
@@ -10,7 +10,8 @@
                        "Valepert",
                        "Sannita",
                        "Macofe",
-                       "Nemo bis"
+                       "Nemo bis",
+                       "JackLantern"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentazione (in inglese)]]\n* [[mw:API:FAQ|FAQ (in inglese)]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api Mailing list]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce Annunci sull'API]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bug & richieste]\n</div>\n<strong>Stato:</strong> Tutte le funzioni e caratteristiche mostrate su questa pagina dovrebbero funzionare, ma l'API è ancora in fase d'attivo sviluppo, e potrebbe cambiare in qualsiasi momenento. Iscriviti alla [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce mailing list] per essere informato sugli aggiornamenti.\n\n<strong>Istruzioni sbagliate:</strong> quando vengono impartite all'API delle istruzioni sbagliate, un'intestazione HTTP verrà inviata col messaggio \"MediaWiki-API-Error\" e sia al valore dell'intestazione sia al codice d'errore verrà impostato lo stesso valore. Per maggiori informazioni leggi [[mw:API:Errors_and_warnings|API:Errori ed avvertimenti (in inglese)]].",
@@ -93,6 +94,7 @@
        "apihelp-expandtemplates-param-prop": "Quale informazione ottenere.\n\nNota che se non è selezionato alcun valore, il risultato conterrà il codice wiki, ma l'output sarà in un formato obsoleto.",
        "apihelp-expandtemplates-paramvalue-prop-wikitext": "Il wikitext espanso.",
        "apihelp-expandtemplates-paramvalue-prop-volatile": "Se l'output sia volatile e non debba essere riutilizzato altrove all'interno della pagina.",
+       "apihelp-feedcontributions-param-feedformat": "Il formato del feed.",
        "apihelp-feedcontributions-param-year": "Dall'anno (e precedenti).",
        "apihelp-feedcontributions-param-month": "Dal mese (e precedenti).",
        "apihelp-feedcontributions-param-deletedonly": "Mostra solo i contribuiti cancellati.",
        "apihelp-feedwatchlist-param-linktosections": "Collega direttamente alla sezione modificata, se possibile.",
        "apihelp-filerevert-description": "Ripristina un file ad una versione precedente.",
        "apihelp-filerevert-param-filename": "Nome del file di destinazione, senza il prefisso 'File:'.",
+       "apihelp-filerevert-param-comment": "Commento sul caricamento.",
+       "apihelp-filerevert-param-archivename": "Nome dell'archivio della versione da ripristinare.",
        "apihelp-filerevert-example-revert": "Ripristina <kbd>Wiki.png</kbd> alla versione del <kbd>2011-03-05T15:27:40Z</kbd>.",
        "apihelp-help-example-submodules": "Aiuto per <kbd>action=query</kbd> e tutti i suoi sotto-moduli.",
        "apihelp-imagerotate-description": "Ruota una o più immagini.",
        "apihelp-login-param-name": "Nome utente.",
        "apihelp-login-param-password": "Password.",
        "apihelp-login-param-domain": "Dominio (opzionale).",
+       "apihelp-login-example-login": "Entra.",
        "apihelp-move-description": "Sposta una pagina.",
+       "apihelp-move-param-reason": "Motivo della rinomina.",
+       "apihelp-move-param-movetalk": "Rinomina la pagina di discussione, se esiste.",
+       "apihelp-move-param-movesubpages": "Rinomina sottopagine, se applicabile.",
+       "apihelp-move-param-noredirect": "Non creare un rinvio.",
        "apihelp-move-param-watch": "Aggiunge la pagina e il redirect agli osservati speciali dell'utente attuale.",
        "apihelp-move-param-ignorewarnings": "Ignora i messaggi di avvertimento del sistema.",
+       "apihelp-opensearch-param-format": "Il formato dell'output.",
        "apihelp-opensearch-example-te": "Trova le pagine che iniziano con <kbd>Te</kbd>.",
        "apihelp-options-example-reset": "Reimposta tutte le preferenze.",
+       "apihelp-paraminfo-param-helpformat": "Formato delle stringhe di aiuto.",
+       "apihelp-parse-param-summary": "Oggetto da analizzare.",
+       "apihelp-parse-example-text": "Analizza wikitext.",
+       "apihelp-parse-example-texttitle": "Analizza wikitext, specificando il titolo della pagina.",
+       "apihelp-parse-example-summary": "Analizza un oggetto.",
        "apihelp-protect-example-protect": "Proteggi una pagina.",
        "apihelp-query-param-export": "Esporta la versione attuale di tutte le pagine ottenute o generate.",
        "apihelp-query+allcategories-param-dir": "Direzione dell'ordinamento.",
        "apihelp-query+allfileusages-paramvalue-prop-title": "Aggiunge il titolo del file.",
        "apihelp-query+allfileusages-param-limit": "Quanti elementi totali restituire.",
        "apihelp-query+allfileusages-param-dir": "La direzione in cui elencare.",
+       "apihelp-query+allimages-param-sort": "Proprietà di ordinamento.",
        "apihelp-query+allimages-param-dir": "La direzione in cui elencare.",
        "apihelp-query+alllinks-paramvalue-prop-title": "Aggiunge il titolo del collegamento.",
        "apihelp-query+alllinks-param-limit": "Quanti elementi totali restituire.",
        "apihelp-query+alllinks-param-dir": "La direzione in cui elencare.",
+       "apihelp-query+allmessages-param-lang": "Restituisci messaggi in questa lingua.",
        "apihelp-query+allpages-param-filterredir": "Quali pagine elencare.",
        "apihelp-query+allpages-param-dir": "La direzione in cui elencare.",
        "apihelp-query+allredirects-paramvalue-prop-title": "Aggiunge il titolo del redirect.",
        "apihelp-query+allrevisions-param-namespace": "Elenca solo le pagine in questo namespace.",
        "apihelp-query+allrevisions-example-user": "Elenca gli ultimi 50 contributi dell'utente <kbd>Example</kbd>.",
        "apihelp-query+allrevisions-example-ns-main": "Elenca solo le prime 50 versioni nel namespace principale.",
+       "apihelp-query+mystashedfiles-param-prop": "Quali proprietà recuperare per il file.",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "Recupera la dimensione del file e le dimensioni dell'immagine.",
+       "apihelp-query+mystashedfiles-param-limit": "Quanti file restituire.",
        "apihelp-query+alltransclusions-paramvalue-prop-title": "Aggiunge il titolo dell'inclusione.",
        "apihelp-query+alltransclusions-param-limit": "Quanti elementi totali restituire.",
        "apihelp-query+alltransclusions-param-dir": "La direzione in cui elencare.",
        "api-help-param-continue": "Quando più risultati sono disponibili, usa questo per continuare.",
        "api-help-param-no-description": "<span class=\"apihelp-empty\">(nessuna descrizione)</span>",
        "api-help-examples": "{{PLURAL:$1|Esempio|Esempi}}:",
+       "api-help-permissions": "{{PLURAL:$1|Permesso|Permessi}}:",
        "api-credits-header": "Crediti"
 }
index 287e147..949f4aa 100644 (file)
        "apihelp-query+allrevisions-param-generatetitles": "ジェネレーターとして使用する場合、版IDではなくページ名を生成します。",
        "apihelp-query+allrevisions-example-user": "利用者 <kbd>Example</kbd> による直近の50版を一覧表示する。",
        "apihelp-query+allrevisions-example-ns-main": "標準名前空間にある最初の50版を一覧表示する。",
+       "apihelp-query+mystashedfiles-param-limit": "取得するファイルの数。",
        "apihelp-query+alltransclusions-param-prefix": "この値で始まるすべてのトランスクルードされているページを検索する。",
        "apihelp-query+alltransclusions-param-prop": "どの情報を結果に含めるか:",
        "apihelp-query+alltransclusions-param-namespace": "列挙する前空間。",
        "apihelp-query+categorymembers-description": "与えられたカテゴリ内のすべてのページを一覧表示します。",
        "apihelp-query+categorymembers-param-title": "一覧表示するカテゴリ (必須)。<kbd>{{ns:category}}:</kbd> 接頭辞を含まなければなりません。<var>$1pageid</var> とは同時に使用できません。",
        "apihelp-query+categorymembers-param-pageid": "一覧表示するカテゴリのページID. <var>$1title</var> とは同時に使用できません。",
+       "apihelp-query+categorymembers-param-prop": "どの情報を結果に含めるか:",
        "apihelp-query+categorymembers-paramvalue-prop-ids": "ページIDを追加します。",
        "apihelp-query+categorymembers-paramvalue-prop-title": "ページ名と名前空間IDを追加します。",
        "apihelp-query+categorymembers-paramvalue-prop-sortkey": "カテゴリでのソートに使用するソートキーを追加します(16進数文字列)。",
        "apihelp-query+pageswithprop-param-limit": "返すページの最大数。",
        "apihelp-query+pageswithprop-example-simple": "<code>&#123;&#123;DISPLAYTITLE:&#125;&#125;</code> を使用している最初の10ページを一覧表示する。",
        "apihelp-query+pageswithprop-example-generator": "<code>_&#95;NOTOC_&#95;</code> を使用している最初の10ページについての追加情報を取得する。",
-       "apihelp-query+prefixsearch-description": "ページ名の先頭一致検索を行います。",
+       "apihelp-query+prefixsearch-description": "ページ名の先頭一致検索を行います。\n\n名前が似ていますが、このモジュールは[[Special:PrefixIndex]]と等価であることを意図しません。そのような目的では<kbd>[[Special:ApiHelp/query+allpages|action=query&list=allpages]]</kbd> を <kbd>apprefix</kbd> パラメーターと共に使用してください。このモジュールの目的は <kbd>[[Special:ApiHelp/opensearch|action=opensearch]]</kbd> と似ています: 利用者から入力を受け取り、最も適合するページ名を提供するというものです。検索エンジンのバックエンドによっては、誤入力の訂正や、転送の回避、その他のヒューリスティクスが適用されることがあります。",
        "apihelp-query+prefixsearch-param-search": "検索文字列。",
        "apihelp-query+prefixsearch-param-namespace": "検索する名前空間。",
        "apihelp-query+prefixsearch-param-limit": "返す結果の最大数。",
index cf45cae..1497e34 100644 (file)
@@ -10,7 +10,8 @@
                        "Hwangjy9",
                        "Kurousagi",
                        "Revi",
-                       "Yearning"
+                       "Yearning",
+                       "Priviet"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|설명문서]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api 메일링 리스트]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API 공지 사항] * [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R 버그 및 요청] </div>\n<strong>상태:</strong> 이 페이지에 표시된 모든 기능은 정상적으로 작동하지만, API는 여전히 활발하게 개발되고 있으며, 언제든지 변경될 수 있습니다. 업데이트 정보를 받아보려면 [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ the mediawiki-api-announce 메일링 리스트]를 구독하십시오.\n\n<strong>잘못된 요청:</strong> API에 잘못된 요청이 전송되면 HTTP 헤더에서 \"MediaWiki-API-Error\" 키를 보내고, 헤더 값과 오류 코드가 같게 설정됩니다. 자세한 정보에 대해서는 [[mw:API:Errors_and_warnings|API:오류와 경고]]를 참조하십시오.",
        "apihelp-query+pageswithprop-param-limit": "나타낼 문서의 최대 수입니다.",
        "apihelp-query+pageswithprop-param-dir": "정렬 순서",
        "apihelp-query+prefixsearch-param-search": "문자열 검색",
+       "apihelp-rollback-param-tags": "되돌리기를 적용하기 위해 태그합니다.",
        "apihelp-unblock-description": "사용자를 차단 해제합니다.",
        "apihelp-rawfm-description": "출력 데이터, 디버깅 요소를 포함, (HTML에 포함된)JSON형식.",
        "api-help-title": "미디어위키 API 도움말",
index c334d38..e7bfbe1 100644 (file)
@@ -5,6 +5,7 @@
                        "Macofe"
                ]
        },
+       "apihelp-main-param-curtimestamp": "Den aktuellen Zäitstempel an d'Resultat integréieren.",
        "apihelp-block-description": "E Benotzer spären.",
        "apihelp-block-param-user": "Benotzernumm, IP-Adress oder IP-Beräich deen Dir späre wëllt.",
        "apihelp-block-param-reason": "Grond fir ze spären.",
@@ -13,6 +14,8 @@
        "apihelp-block-param-reblock": "Wann de Benotzer scho gespaart ass, déi aktuell Spär iwwerschreiwen.",
        "apihelp-block-param-watchuser": "Dem Benotzer oder der IP-Adress hier Benotzer- an Diskussiouns-Säiten iwwerwaachen.",
        "apihelp-compare-param-fromtitle": "Éischten Titel fir ze vergläichen.",
+       "apihelp-compare-param-fromrev": "Éischt Versioun fir ze vergläichen.",
+       "apihelp-compare-param-totitle": "Zweeten Titel fir ze vergläichen.",
        "apihelp-compare-param-torev": "Zweet Versioun fir ze vergläichen.",
        "apihelp-createaccount-description": "En neie Benotzerkont uleeën.",
        "apihelp-createaccount-param-name": "Benotzernumm.",
        "apihelp-createaccount-param-realname": "Richtegen Numm vum Benotzer (fakultativ).",
        "apihelp-delete-description": "Eng Säit läschen.",
        "apihelp-delete-param-watch": "D'Säit op dem aktuelle Benotzer seng Iwwerwaachungslëscht dobäisetzen.",
+       "apihelp-delete-param-unwatch": "D'Säit vun der Iwwerwaachungslëscht vum aktuelle Benotzer erofhuelen.",
        "apihelp-delete-example-simple": "D'<kbd>Main Page</kbd> läschen.",
        "apihelp-disabled-description": "Dëse Modul gouf ausgeschalt.",
        "apihelp-edit-param-sectiontitle": "Den Titel fir en neien Abschnitt.",
        "apihelp-edit-param-text": "Säiteninhalt.",
        "apihelp-edit-param-minor": "Kleng Ännerung.",
+       "apihelp-edit-param-notminor": "Keng kleng Ännerung",
        "apihelp-edit-param-bot": "Dës Ännerung als Bot-Ännerung markéieren.",
+       "apihelp-edit-param-createonly": "D'Säit net ännere wann et se scho gëtt.",
        "apihelp-edit-param-watch": "D'Säit op dem aktuelle Benotzer seng Iwwerwaachungslëscht dobäisetzen.",
        "apihelp-edit-example-edit": "Eng Säit änneren",
        "apihelp-emailuser-example-email": "Dem Benotzer <kbd>WikiSysop</kbd> eng E-Mail mam Text <kbd>Content</kbd> schécken.",
@@ -33,6 +39,9 @@
        "apihelp-expandtemplates-paramvalue-prop-ttl": "D'Maximalzäit no där den Tëschespäicher vum Resultat net méi valabel si soll.",
        "apihelp-feedcontributions-param-year": "Vum Joer (a virdrun).",
        "apihelp-feedcontributions-param-month": "Vum Mount (a virdrun).",
+       "apihelp-feedcontributions-param-deletedonly": "Nëmme geläscht Kontributioune weisen.",
+       "apihelp-feedcontributions-param-toponly": "Nëmmen Ännerunge weisen déi déi lescht Versioun sinn.",
+       "apihelp-feedrecentchanges-param-days": "Deeg, op déi d'Resultater limitéiert gi sollen",
        "apihelp-feedrecentchanges-param-hideminor": "Kleng Ännerunge verstoppen.",
        "apihelp-feedrecentchanges-param-hidebots": "Ännerunge vu Botte verstoppen.",
        "apihelp-feedrecentchanges-param-hideanons": "Ännerunge vun anonyme Benotzer verstoppen.",
@@ -42,6 +51,7 @@
        "apihelp-feedrecentchanges-param-categories": "Nëmmen Ännerunge vu Säiten aus all dëse Kategorië weisen.",
        "apihelp-feedrecentchanges-param-categories_any": "Nëmmen Ännerunge vu Säiten aus enger vun dëse Kategorië weisen.",
        "apihelp-feedrecentchanges-example-simple": "Rezent Ännerunge weisen",
+       "apihelp-filerevert-param-comment": "Bemierkung eroplueden.",
        "apihelp-help-example-main": "Hëllef fir den Haaptmodul.",
        "apihelp-help-example-recursive": "All Hëllef op enger Säit",
        "apihelp-imagerotate-description": "Eent oder méi Biller dréinen.",
        "apihelp-login-param-name": "Benotzernumm.",
        "apihelp-login-param-password": "Passwuert.",
        "apihelp-login-example-login": "Aloggen.",
+       "apihelp-logout-example-logout": "Den aktuelle Benotzer ausloggen.",
        "apihelp-move-description": "Eng Säit réckelen.",
+       "apihelp-move-param-reason": "Grond fir d'Ëmbenennen.",
        "apihelp-move-param-movetalk": "D'Diskussiounssäit ëmbenennen, wann et se gëtt.",
+       "apihelp-move-param-noredirect": "Keng Viruleedung uleeën.",
        "apihelp-move-param-ignorewarnings": "All Warnungen ignoréieren.",
        "apihelp-options-description": "Astellunge fir den aktuelle Benotzer änneren.\n\nNëmmen Optiounen aus dem Haaptdeel (core) oder aus enger vun den installéierten Erweiderunge, oder Optioune mat Schlësselen déi viragestallt si mat <code>userjs-</code> (geduecht fir mat Benotzer-Scripte benotzt ze ginn), kënnen agestallt ginn.",
        "apihelp-options-param-optionname": "Den Numm vun der Optioun deen op de Wäert vun <var>$1optionvalue</var> gesat gi muss",
index b2c230a..a368b3e 100644 (file)
        "apihelp-block-description": "بستن کاربر.",
        "apihelp-createaccount-param-name": ":نؤم بهرۀگر-کاربر",
        "apihelp-delete-description": "حةذف وةڵگة",
+       "apihelp-disabled-description": "اێ پودمانە إکار کەتێە(غیرفعال بیە).",
        "apihelp-edit-description": "دؤرس کردن و دۀسکاری وۀلگۀ",
        "apihelp-edit-param-sectiontitle": "نام سۀر وۀلگ تازۀ",
        "apihelp-edit-example-edit": ".دةسکاری وةڵگة",
        "apihelp-emailuser-param-subject": "موضوع سةر وةڵگ",
        "apihelp-emailuser-param-text": "متن رایانه.",
+       "apihelp-help-example-main": "راهنما برای پودمان اصلی",
        "apihelp-login-param-name": "نام کاربری",
        "apihelp-login-param-password": ".رمز",
-       "apihelp-login-example-login": "نؤم هۀتن.",
+       "apihelp-login-example-login": "إنۆم هەتِن.",
        "apihelp-logout-description": "دۀرچئن و پاک کردن داده متن",
        "apihelp-logout-example-logout": "خروج کاربر فعلی",
        "apihelp-options-example-reset": "بازنشانی همه تنظیمات."
diff --git a/includes/api/i18n/my.json b/includes/api/i18n/my.json
new file mode 100644 (file)
index 0000000..63d9df6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       "@metadata": {
+               "authors": [
+                       "9.sinistra",
+                       "Ninjastrikers"
+               ]
+       },
+       "apihelp-feedrecentchanges-param-hideanons": "အမည်မသိ အသုံးပြုသူများ ပြုလုပ်သည့် ပြောင်းလဲချက်များကို ဝှက်ရန်",
+       "apihelp-feedrecentchanges-param-hideliu": "မှတ်ပုံတင်ထားသော အသုံးပြုသူများ ပြုလုပ်ထားခဲ့သည့် ပြောင်းလဲမှုများကို ဝှက်ရန်"
+}
index 44d04fe..b300f88 100644 (file)
@@ -7,6 +7,7 @@
        },
        "apihelp-main-param-action": "Quale aziona d'avess'a fà.",
        "apihelp-main-param-format": "Qualu furmato avess'ascì d'output.",
+       "apihelp-main-param-maxlag": "'O massimo lag ca se putess'ausà quanno MediaWiki s'installasse ncopp'a nu cluster replicato 'e database. Pe' puté sarvà aziune ca causassero cchiù lag 'e replicato, stu parammetro putesse fà 'o cliente aspettà nfin'a quanno 'o tiempo 'e replicaziona fosse meno ca nu valore specificato. Si nce stesse cchiù assaje tiempo 'e lag, nu codece 'errore <samp>maxlag</samp> se turnasse comm'a na mmasciata tipo <samp>Aspettanno 'o $host: nu $lag secunde 'e lag</samp>.<br />Vedite [[mw:Manual:Maxlag_parameter|Manuale: Parammetro Maxlag]] pe' n'avé cchiù nfurmaziune.",
        "apihelp-main-param-servedby": "Include 'o risultato 'e nomme d' 'o host ca servette 'a richiesta.",
        "apihelp-main-param-curtimestamp": "Include dint' 'o risultato 'o timestamp 'e mò.",
        "apihelp-block-description": "Blocca n'utente.",
@@ -17,6 +18,7 @@
        "apihelp-block-param-autoblock": "Automaticamende blocca l'urdeme indirizze IP ausate, e tuttuquante ll'indirizze IP addò tentasse 'e trasì.",
        "apihelp-block-param-noemail": "Scanza st'utente 'e mannà mmasciate pe' bbìa d' 'o wiki. (Servisse 'o <code>blockemail</code> buono).",
        "apihelp-block-param-hidename": "Annascunne 'o nomme utente d' 'o riggistro 'e blocche (Addimanna 'e premmesse 'e <code>hideuser</code>).",
+       "apihelp-block-param-reblock": "Si l'utente è già bluccato, sovrascrive 'o blocco esistente.",
        "apihelp-checktoken-param-type": "Tipo 'e token ncurzo 'e test.",
        "apihelp-checktoken-param-token": "Token 'a testà.",
        "apihelp-checktoken-param-maxtokenage": "Massima ammaturità cunzentuta p' 'o token, 'n secunde.",
index 2eb761d..c2c6d7e 100644 (file)
@@ -10,7 +10,8 @@
                        "Macofe",
                        "SPQRobin",
                        "HanV",
-                       "Rangekill"
+                       "Rangekill",
+                       "Robin van der Vliet"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Documentatie]]\n* [[mw:API:FAQ|FAQ]]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api E-maillijst]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-api-announce API-aankondigingen]\n* [https://phabricator.wikimedia.org/maniphest/query/GebfyV4uCaLd/#R Bugs & verzoeken]\n</div>\n<strong>Status:</strong> Alle functies die op deze pagina worden weergegeven horen te werken. Aan de API wordt actief gewerkt, en deze kan gewijzigd worden. Abonneer u op  de [https://lists.wikimedia.org/pipermail/mediawiki-api-announce/ e-maillijst mediawiki-api-announce] voor meldingen over aanpassingen.\n\n<strong>Foutieve verzoeken:</strong> als de API foutieve verzoeken ontvangt, wordt er geantwoord met een HTTP-header met de sleutel \"MediaWiki-API-Error\" en daarna worden de waarde van de header en de foutcode op dezelfde waarde ingesteld. Zie [[mw:API:Errors_and_warnings|API: Errors and warnings]] voor meer informatie.",
        "apihelp-main-param-servedby": "Voeg de hostnaam van de server die de aanvraag heeft afgehandeld toe aan het antwoord.",
        "apihelp-main-param-curtimestamp": "Huidige tijd aan het antwoord toevoegen.",
        "apihelp-block-description": "Gebruiker blokkeren.",
+       "apihelp-block-param-user": "Gebruikersnaam, IP-adres of IP-range om te blokkeren.",
        "apihelp-block-param-reason": "Reden voor blokkade.",
+       "apihelp-block-param-nocreate": "Voorkom registeren van accounts.",
        "apihelp-block-param-autoblock": "Blokkeer automatisch het laatst gebruikte IP-adres en ieder volgend IP-adres van waaruit ze proberen aan te melden.",
        "apihelp-block-param-reblock": "De huidige blokkade aanpassen als de gebruiker al geblokkeerd is.",
+       "apihelp-createaccount-description": "Een nieuw gebruikersaccount aanmaken.",
        "apihelp-createaccount-param-name": "Gebruikersnaam.",
+       "apihelp-createaccount-param-email": "E-mailadres van de gebruikers (optioneel).",
+       "apihelp-createaccount-param-realname": "Echte naam van de gebruiker (optioneel).",
        "apihelp-delete-description": "Verwijder een pagina.",
+       "apihelp-delete-param-watch": "De pagina aan de volglijst van de huidige gebruiker toevoegen.",
+       "apihelp-delete-param-unwatch": "De pagina van de volglijst van de huidige gebruiker verwijderen.",
        "apihelp-delete-example-simple": "Verwijder <kbd>Main Page</kbd>.",
        "apihelp-delete-example-reason": "Verwijder <kbd>Main Page</kbd> met als reden <kbd>Preparing for move</kbd>.",
        "apihelp-disabled-description": "Deze module is uitgeschakeld.",
+       "apihelp-edit-description": "Aanmaken en bewerken van pagina's.",
+       "apihelp-edit-param-title": "Titel van de pagina om te bewerken. Kan niet gebruikt worden samen met <var>$1pageid</var>.",
+       "apihelp-edit-param-sectiontitle": "De titel van de nieuwe sectie.",
        "apihelp-edit-param-text": "Pagina-inhoud.",
+       "apihelp-edit-param-tags": "Wijzigingslabels om aan de versie toe te voegen.",
        "apihelp-edit-param-minor": "Kleine bewerking.",
        "apihelp-edit-param-notminor": "Geen kleine bewerking.",
        "apihelp-edit-param-bot": "Markeer deze bewerking als bot.",
@@ -40,6 +52,7 @@
        "apihelp-edit-param-nocreate": "Geef een foutmelding als de pagina niet bestaat.",
        "apihelp-edit-param-watch": "Voeg de pagina toe aan de volglijst van de huidige gebruiker.",
        "apihelp-edit-param-unwatch": "Verwijder de pagina van de volglijst van de huidige gebruiker.",
+       "apihelp-edit-param-redirect": "Automatisch doorverwijzingen oplossen.",
        "apihelp-edit-example-edit": "Pagina bewerken",
        "apihelp-emailuser-description": "Gebruiker e-mailen.",
        "apihelp-emailuser-param-target": "Gebruiker naar wie de e-mail moet worden gestuurd.",
        "apihelp-emailuser-param-text": "E-mailtekst.",
        "apihelp-emailuser-param-ccme": "Stuur mij een kopie van deze e-mail.",
        "apihelp-expandtemplates-param-title": "Paginanaam.",
+       "apihelp-expandtemplates-param-text": "Wikitekst om om te zetten.",
+       "apihelp-feedcontributions-description": "Haalt de feed van de gebruikersbijdragen op.",
        "apihelp-feedcontributions-param-year": "Van jaar (en eerder).",
        "apihelp-feedcontributions-param-month": "Van maand (en eerder).",
+       "apihelp-feedcontributions-param-deletedonly": "Alleen verwijderde bijdragen weergeven.",
+       "apihelp-feedrecentchanges-param-hideminor": "Kleine wijzigingen verbergen.",
+       "apihelp-feedrecentchanges-param-hidebots": "Wijzigingen gedaan door bots verbergen.",
+       "apihelp-feedrecentchanges-param-hideanons": "Wijzigingen gedaan door anonieme gebruikers verbergen.",
+       "apihelp-feedrecentchanges-param-hideliu": "Wijzigingen gedaan door geregistreerde gebruikers verbergen.",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Wijzigingen gemarkeerd als gecontroleerd verbergen.",
+       "apihelp-feedrecentchanges-param-hidemyself": "Wijzigingen door de huidige gebruiker verbergen.",
+       "apihelp-feedrecentchanges-param-hidecategorization": "Wijzigingen in categorielidmaatschap verbergen.",
        "apihelp-feedrecentchanges-param-tagfilter": "Filteren op label.",
+       "apihelp-feedrecentchanges-example-simple": "Recente wijzigingen weergeven.",
+       "apihelp-feedrecentchanges-example-30days": "Recente wijzigingen van de afgelopen 30 dagen weergeven.",
+       "apihelp-filerevert-description": "Een oude versie van een bestand herplaatsen.",
        "apihelp-import-param-namespace": "Importeren in deze naamruimte. Can niet samen gebruikt worden met <var>$1rootpage</var>.",
        "apihelp-import-param-rootpage": "Importeren als subpagina van deze pagina. Kan niet samen met <var>$1namespace</var> gebruikt worden.",
        "apihelp-login-param-name": "Gebruikersnaam.",
@@ -60,6 +86,8 @@
        "apihelp-logout-example-logout": "Meldt de huidige gebruiker af.",
        "apihelp-managetags-param-tag": "Label om aan te maken, te activeren of te deactiveren. Voor het aanmaken van een label, mag het niet bestaan. Voor het verwijderen van een label, moet het bestaan. Voor het activeren van een label, moet het bestaan en mag het niet gebruikt worden door een uitbreiding. Voor het deactiveren van een label, moet het gebruikt worden en handmatig gedefinieerd zijn.",
        "apihelp-move-description": "Pagina hernoemen.",
+       "apihelp-move-param-reason": "Reden voor de naamswijziging.",
+       "apihelp-move-param-noredirect": "Geen doorverwijzing achterlaten.",
        "apihelp-move-param-watch": "Pagina en de omleiding toevoegen aan de volglijst van de huidige gebruiker.",
        "apihelp-move-param-unwatch": "Verwijder de pagina en de doorverwijzing van de volglijst van de huidige gebruiker.",
        "apihelp-move-param-watchlist": "De pagina onvoorwaardelijk toevoegen aan of verwijderen van de volglijst van de huidige gebruiker, gebruik voorkeuren of wijzig het volgen niet.",
        "apihelp-options-param-optionvalue": "De waarde voor de optie opgegeven door <var>$1optienaam</var>, kan sluistekens (verticale streepjes) bevatten.",
        "apihelp-options-example-reset": "Alle voorkeuren opnieuw instellen.",
        "apihelp-options-example-change": "Voorkeuren wijzigen voor <kbd>skin</kbd> en <kbd>hideminor</kbd>.",
+       "apihelp-parse-paramvalue-prop-categorieshtml": "Vraagt een HTML-versie van de categorieën op.",
        "apihelp-parse-example-page": "Een pagina parseren.",
        "apihelp-parse-example-text": "Wikitext parseren.",
        "apihelp-parse-example-summary": "Een samenvatting parseren.",
+       "apihelp-patrol-description": "Een pagina of versie markeren als gecontroleerd.",
+       "apihelp-patrol-example-rcid": "Een recente wijziging markeren als gecontroleerd.",
+       "apihelp-patrol-example-revid": "Een versie markeren als gecontroleerd.",
+       "apihelp-protect-param-reason": "Reden voor opheffen van de beveiliging.",
        "apihelp-protect-example-protect": "Een pagina beveiligen",
        "apihelp-query+alldeletedrevisions-param-tag": "Alleen versies weergeven met dit label.",
        "apihelp-query+allmessages-param-enableparser": "Stel in om de parser in te schakelen, zorgt voor het voorverwerken van de wikitekst van een bericht (vervangen van magische woorden, de afhandeling van sjablonen, enzovoort).",
index e94d13b..9f77e10 100644 (file)
@@ -9,8 +9,10 @@
        "apihelp-block-description": "په يو کارن بنديز لگول.",
        "apihelp-block-param-user": "کارن-نوم، IP پته، يا IP سيمې باندې بنديز لگول.",
        "apihelp-block-param-reason": "د بنديز سبب.",
+       "apihelp-block-param-nocreate": "د گڼون جوړولو مخ نيول.",
        "apihelp-createaccount-param-name": "کارن-نوم.",
        "apihelp-delete-description": "يو مخ ړنگول.",
+       "apihelp-delete-example-simple": "<kbd>Main Page</kbd> ړنگول.",
        "apihelp-edit-description": "مخونه جوړول او سمول.",
        "apihelp-edit-param-sectiontitle": "د يوې نوې برخې سرليک.",
        "apihelp-edit-param-text": "مخ مېنځپانگه.",
        "apihelp-edit-param-bot": "دا سمون د روباټ په توگه په نښه کول.",
        "apihelp-edit-example-edit": "يو مخ سمول.",
        "apihelp-emailuser-description": "کارن ته برېښليک لېږل.",
+       "apihelp-emailuser-param-target": "هغه کارن چې برېښليک ورلېږې.",
+       "apihelp-emailuser-param-subject": "د سکالو سرليک.",
+       "apihelp-emailuser-param-text": "د برېښليک جوسه.",
+       "apihelp-emailuser-param-ccme": "د دې برېښليک يوه لمېسه ماته هم راولېږه.",
        "apihelp-expandtemplates-param-title": "د مخ سرليک.",
        "apihelp-feedrecentchanges-param-hideminor": "وړوکي بدلونونه پټول.",
        "apihelp-feedrecentchanges-param-hidebots": "د روباټونو لخوا ترسره شوي بدلونونه پټول.",
        "apihelp-feedrecentchanges-param-hidepatrolled": "څارل شوي بدلونونه پټول.",
        "apihelp-feedrecentchanges-param-hidemyself": "د اوسني کارن لخوا ترسره شوي بدلونونه پټول.",
        "apihelp-feedrecentchanges-param-tagfilter": "د نښلن له مخې چاڼول.",
+       "apihelp-feedrecentchanges-example-simple": "وروستي بدلونونه ښکاره کول.",
        "apihelp-login-param-name": "کارن نوم.",
        "apihelp-login-param-password": "پټنوم.",
        "apihelp-login-param-domain": "شپول (اختياري).",
        "apihelp-login-example-login": "ننوتل.",
        "apihelp-move-description": "يو مخ لېږدول.",
+       "apihelp-protect-example-protect": "يو مخ ژغورل.",
+       "apihelp-query+allpages-param-filterredir": "کوم مخونه چې لړليک کې راشي.",
        "apihelp-query+search-example-simple": "د <kbd>meaning</kbd> پلټل.",
        "apihelp-query+search-example-text": "د <kbd>مانا</kbd> لپاره متنونه پلټل.",
        "apihelp-query+watchlist-paramvalue-prop-title": "د يو مخ سرليک ورگډوي.",
@@ -44,6 +53,7 @@
        "apihelp-userrights-param-user": "کارن نوم.",
        "apihelp-userrights-param-userid": "کارن پېژند.",
        "apihelp-userrights-param-reason": "د بدلون سبب.",
+       "api-format-title": "د مېډياويکي API پايله",
        "api-help-title": "د مېډياويکي API لارښود",
        "api-help-main-header": "آر ماډيول",
        "api-help-source": "سرچينه: $1",
@@ -57,5 +67,6 @@
        "api-help-param-default": "تلواليز: $1",
        "api-help-param-default-empty": "تلواليز: <span class=\"apihelp-empty\">(تش)</span>",
        "api-help-examples": "{{PLURAL:$1|بېلگه|بېلگې}}:",
-       "api-help-permissions": "{{PLURAL:$1|پرېښه|پرېښې}}:"
+       "api-help-permissions": "{{PLURAL:$1|پرېښه|پرېښې}}:",
+       "api-credits-header": "کرېډټونه"
 }
index b1d27c8..a72b088 100644 (file)
@@ -6,7 +6,8 @@
                        "Cainamarques",
                        "Rhcastilhos",
                        "Macofe",
-                       "Almondega"
+                       "Almondega",
+                       "Raphaelras"
                ]
        },
        "apihelp-main-param-action": "Qual ação executar.",
        "apihelp-query+allfileusages-param-to": "O título do arquivo onde parar de enumerar.",
        "apihelp-query+allfileusages-paramvalue-prop-title": "Adiciona o título do arquivo.",
        "apihelp-query+allfileusages-param-limit": "Quantos itens retornar.",
+       "apihelp-query+allfileusages-example-unique": "Listar títulos únicos de arquivos",
+       "apihelp-query+allfileusages-example-generator": "Obter as páginas contendo os arquivos",
        "apihelp-query+allimages-param-user": "Retorna apenas os arquivos enviados por este usuário. Só pode ser usado com $1sort=timestamp. Não pode ser usado em conjunto com $1filterbots.",
        "apihelp-query+allimages-param-filterbots": "Como filtrar arquivos enviados por bots. Só pode ser usado com $1sort=timestamp. Não pode ser usado em conjunto com $1user.",
        "apihelp-query+allimages-param-mime": "Quais tipos MIME pesquisar, ex.: <kbd>image/jpeg</kbd>.",
        "apihelp-query+links-param-limit": "Quantos links retornar.",
        "apihelp-query+linkshere-param-limit": "Quantos retornar.",
        "apihelp-query+prefixsearch-param-limit": "O número máximo a se retornar.",
+       "apihelp-query+protectedtitles-param-limit": "Quantas páginas retornar.",
+       "apihelp-query+protectedtitles-paramvalue-prop-level": "Adicionar o nível de proteção",
+       "apihelp-query+protectedtitles-example-simple": "Listar títulos protegidos",
+       "apihelp-query+random-param-filterredir": "Como filtrar por redirecionamentos.",
+       "apihelp-query+recentchanges-param-user": "Listar apenas alterações de usuário.",
+       "apihelp-query+recentchanges-param-excludeuser": "Não listar as alterações deste usuário.",
+       "apihelp-query+recentchanges-param-tag": "Listar apenas as alterações marcadas com esta etiqueta.",
+       "apihelp-query+recentchanges-paramvalue-prop-flags": "Adicionar indicadores para a edição.",
+       "apihelp-query+recentchanges-paramvalue-prop-tags": "Listar as etiquetas para entrada.",
+       "apihelp-query+recentchanges-example-simple": "Listar mudanças recentes.",
+       "apihelp-query+redirects-paramvalue-prop-title": "Título de cada redirecionamento.",
+       "apihelp-query+redirects-paramvalue-prop-fragment": "Fragmento de cada redirecionamento, se há algum.",
+       "apihelp-query+redirects-param-namespace": "Listar páginas apenas neste espaço nominal.",
+       "apihelp-query+revisions-example-last5": "Mostrar as 5 últimas revisões do <kbd>Main Page</kbd>.",
+       "apihelp-query+revisions-example-first5": "Mostrar as 5 primeiras revisões do <kbd>Main Page</kbd>.",
+       "apihelp-query+revisions-example-first5-after": "Mostrar as 5 primeiras revisões do <kbd>Main Page</kbd> feitas depois de 05/01/2006.",
+       "apihelp-query+revisions-example-first5-not-localhost": "Mostrar as 5 primeiras revisões do <kbd>Main Page</kbd> que não foram feitas pelo usuário anônimo <kbd>127.0.0.1</kbd>.",
+       "apihelp-query+revisions-example-first5-user": "Mostrar as 5 primeiras revisões da <kbd>Main Page</kbd> que foram feitas pelo usuário <kbd>MediaWiki default</kbd>.",
+       "apihelp-query+revisions+base-param-prop": "Que propriedades mostrar para cada modificação:",
+       "apihelp-query+revisions+base-paramvalue-prop-content": "Texto da revisão.",
+       "apihelp-query+revisions+base-paramvalue-prop-tags": "Etiquetas para a revisão.",
+       "apihelp-query+search-description": "Fazer uma buscar completa de texto.",
        "apihelp-query+search-param-prop": "Que propriedades retornar:",
        "apihelp-query+search-paramvalue-prop-size": "Adiciona o tamanho da página em bytes.",
        "apihelp-query+search-paramvalue-prop-wordcount": "Adiciona a contagem de palavras da página.",
        "apihelp-query+search-paramvalue-prop-snippet": "Adiciona um fragmento analisado da página.",
        "apihelp-query+search-paramvalue-prop-titlesnippet": "Adiciona um fragmento analisado do título da página.",
        "apihelp-query+search-param-limit": "Quantas páginas retornar.",
+       "apihelp-query+search-example-simple": "Procurar por <kbd>meaning</kbd>.",
+       "apihelp-query+search-example-text": "Procurar textos para <kbd>meaning</kbd>.",
+       "apihelp-query+siteinfo-paramvalue-prop-general": "Informação geral de sistema",
+       "apihelp-query+siteinfo-paramvalue-prop-statistics": "Voltar às estatísticas do site.",
+       "apihelp-query+siteinfo-param-numberingroup": "Listar o número de usuários nos grupos de usuário.",
+       "apihelp-query+siteinfo-example-simple": "Obter informação do site.",
        "apihelp-query+templates-param-limit": "Quantas predefinições retornar.",
        "apihelp-query+transcludedin-param-limit": "Quantos retornar.",
+       "apihelp-query+users-description": "Obter informação sobre uma lista de usuários.",
        "apihelp-query+watchlist-param-limit": "Quantos resultados retornar por solicitação.",
+       "apihelp-query+watchlist-paramvalue-prop-title": "Adicionar título da página.",
+       "apihelp-query+watchlist-paramvalue-prop-comment": "Adicionar comentário à edição.",
+       "apihelp-query+watchlist-paramvalue-type-edit": "Edições comuns nas páginas.",
+       "apihelp-query+watchlist-paramvalue-type-external": "Alterações externas",
+       "apihelp-query+watchlist-paramvalue-type-new": "Criação de páginas.",
+       "apihelp-query+watchlist-paramvalue-type-log": "Registro de entradas.",
+       "apihelp-query+watchlist-paramvalue-type-categorize": "Alterações de membros pertencentes à uma categoria.",
        "apihelp-query+watchlistraw-param-limit": "Quantos resultados retornar por solicitação.",
        "apihelp-rollback-param-title": "Título da página para reverter. Não pode ser usado em conjunto com <var>$1pageid</var>.",
        "apihelp-rollback-param-pageid": "ID da página para reverter. Não pode ser usado em conjunto com <var>$1title</var>.",
+       "apihelp-setnotificationtimestamp-param-entirewatchlist": "Trabalhar em todas as páginas vigiadas.",
+       "apihelp-stashedit-param-sectiontitle": "O título para uma nova seção.",
+       "apihelp-stashedit-param-text": "Conteúdo da página",
+       "apihelp-stashedit-param-contentmodel": "Modelo de conteúdo do novo conteúdo.",
+       "apihelp-stashedit-param-contentformat": "Formato de serialização de conteúdo usado para o texto de entrada.",
+       "apihelp-tag-param-reason": "Motivo para a mudança.",
+       "apihelp-unblock-description": "Desbloquear usuário",
        "apihelp-unblock-param-id": "ID do bloco para desbloquear (obtido através de <kbd>list=blocks</kbd>). Não pode ser usado em conjunto com <var>$1user</var>.",
-       "apihelp-unblock-param-user": "Nome de usuário, endereço IP ou intervalo de IP para a se desbloquear. Não pode ser usado em conjunto com <var>$1id</var>."
+       "apihelp-unblock-param-user": "Nome de usuário, endereço IP ou intervalo de IP para a se desbloquear. Não pode ser usado em conjunto com <var>$1id</var>.",
+       "apihelp-unblock-param-reason": "Motivo para o desbloqueio.",
+       "apihelp-unblock-example-id": "Desbloquear bloqueio ID #<kbd>105</kbd>.",
+       "apihelp-undelete-param-reason": "Razão para restaurar.",
+       "apihelp-undelete-example-page": "Restaurar página <kbd>Main Page</kbd>.",
+       "apihelp-upload-param-watch": "Vigiar esta página",
+       "apihelp-upload-param-ignorewarnings": "Ignorar todos os avisos.",
+       "apihelp-userrights-param-user": "Nome de usuário.",
+       "apihelp-userrights-param-userid": "ID de usuário.",
+       "apihelp-userrights-param-add": "Adicionar o usuário para estes grupos.",
+       "apihelp-userrights-param-remove": "Remover o usuário destes grupos.",
+       "apihelp-userrights-param-reason": "Motivo para a mudança.",
+       "apihelp-none-description": "Nenhuma saída."
 }
index 010ff04..7863f69 100644 (file)
@@ -11,7 +11,8 @@
                        "Nemo bis",
                        "Amire80",
                        "Siebrand",
-                       "Purodha"
+                       "Purodha",
+                       "Tacsipacsi"
                ]
        },
        "apihelp-main-description": "{{doc-apihelp-description|main}}",
        "apihelp-imagerotate-example-simple": "{{doc-apihelp-example|imagerotate}}",
        "apihelp-imagerotate-example-generator": "{{doc-apihelp-example|imagerotate}}",
        "apihelp-import-description": "{{doc-apihelp-description|import}}",
-       "apihelp-import-param-summary": "{{doc-apihelp-param|import|summary}}",
+       "apihelp-import-param-summary": "{{doc-apihelp-param|import|summary|info=The parameter being documented here provides the summary used on the log messages about the import. The phrase \"Import summary\" here is grammatically equivalent to a phrase such as \"science book\", not \"eat food\".}}",
        "apihelp-import-param-xml": "{{doc-apihelp-param|import|xml}}",
        "apihelp-import-param-interwikisource": "{{doc-apihelp-param|import|interwikisource}}",
        "apihelp-import-param-interwikipage": "{{doc-apihelp-param|import|interwikipage}}",
        "apihelp-query+allrevisions-param-generatetitles": "{{doc-apihelp-param|query+allrevisions|generatetitles}}",
        "apihelp-query+allrevisions-example-user": "{{doc-apihelp-example|query+allrevisions}}",
        "apihelp-query+allrevisions-example-ns-main": "{{doc-apihelp-example|query+allrevisions}}",
+       "apihelp-query+mystashedfiles-description": "{{doc-apihelp-description|query+mystashedfiles}}",
+       "apihelp-query+mystashedfiles-param-prop": "{{doc-apihelp-param|query+mystashedfiles|prop|paramvalues=1}}",
+       "apihelp-query+mystashedfiles-paramvalue-prop-size": "{{doc-apihelp-paramvalue|query+mystashedfiles|prop|size}}",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "{{doc-apihelp-paramvalue|query+mystashedfiles|prop|type}}",
+       "apihelp-query+mystashedfiles-param-limit": "{{doc-apihelp-param|query+mystashedfiles|limit}}",
+       "apihelp-query+mystashedfiles-example-simple": "{{doc-apihelp-example|query+mystashedfiles}}",
        "apihelp-query+alltransclusions-description": "{{doc-apihelp-description|query+alltransclusions}}",
        "apihelp-query+alltransclusions-param-from": "{{doc-apihelp-param|query+alltransclusions|from}}",
        "apihelp-query+alltransclusions-param-to": "{{doc-apihelp-param|query+alltransclusions|to}}",
        "api-help-param-required": "Displayed in the API help for any required parameter",
        "api-help-datatypes-header": "Header for the data type section in the API help output",
        "api-help-datatypes": "{{technical}} {{doc-important|Do not translate or reformat dates inside &lt;kbd%gt; tags}} Documentation of certain API data types\nSee also:\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
-       "api-help-param-type-limit": "{{technical}} {{doc-important|Do not translate text inside &lt;kbd%gt; tags}} Used to indicate that a parameter is a \"limit\" type. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
+       "api-help-param-type-limit": "{{technical}} {{doc-important|Do not translate text inside &lt;kbd&gt; tags}} Used to indicate that a parameter is a \"limit\" type. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-integer": "{{technical}} Used to indicate that a parameter is an integer or list of integers. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-boolean": "{{technical}} {{doc-important|Do not translate <code>Special:ApiHelp</code> in this message.}} Used to indicate that a parameter is a boolean. Parameters:\n* $1 - Always 1.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
        "api-help-param-type-password": "{{ignored}}{{technical}} Used to indicate that a parameter is a password or list of passwords. Parameters:\n* $1 - 1 if the parameter takes one value, 2 if the parameter takes a list of values.\nSee also:\n* {{msg-mw|api-help-datatypes}}\n* [[Special:PrefixIndex/MediaWiki:api-help-param-type]]",
index 766da01..508a4c4 100644 (file)
        "apihelp-login-example-login": "Войти",
        "apihelp-logout-description": "Выйти и очистить данные сессии.",
        "apihelp-move-description": "Переместить страницу.",
+       "apihelp-move-param-to": "Заголовок, в который следует переименовать страницу.",
        "apihelp-move-param-reason": "Причина переименования.",
        "apihelp-move-param-movetalk": "Переименовать страницу обсуждения, если она есть.",
        "apihelp-move-param-movesubpages": "Переименовать подстраницы, если это применимо.",
        "apihelp-move-param-noredirect": "Не создавать перенаправление.",
+       "apihelp-move-param-watch": "Добавить страницу и перенаправление в список наблюдения текущего пользователя.",
+       "apihelp-move-param-unwatch": "Удалить страницу и перенаправление из списка наблюдения текущего пользователя.",
        "apihelp-move-param-ignorewarnings": "Игнорировать предупреждения",
        "apihelp-opensearch-param-search": "Строка поиска.",
        "apihelp-opensearch-param-limit": "Максимальное число возвращаемых результатов.",
        "apihelp-php-description": "Выходные данные в сериализованном формате PHP.",
        "apihelp-phpfm-description": "Выходные данные в сериализованном формате PHP (pretty-print in HTML).",
        "apihelp-xml-description": "Выходные данные в формате XML.",
-       "apihelp-yaml-description": "Выходные данные в формате yaml.",
        "api-format-title": "Результат MediaWiki API",
        "api-pageset-param-titles": "Список заголовков для работы.",
        "api-pageset-param-pageids": "Список страниц идентификаторов для работы.",
        "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=One value|2=Values (separate with <kbd>{{!}}</kbd>)}}: $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 для ботов).",
index 28e90de..d7c3d06 100644 (file)
@@ -16,6 +16,7 @@
        "apihelp-emailuser-description": "Слање имејла кориснику.",
        "apihelp-emailuser-param-target": "Корисник је послао имејл.",
        "apihelp-feedcontributions-param-year": "Од године (и раније).",
+       "apihelp-feedrecentchanges-param-hidepatrolled": "Сакриј патролиране измене.",
        "apihelp-filerevert-description": "Вратити датотеку у ранију верзију.",
        "apihelp-help-example-recursive": "Сва помоћ у једној страници.",
        "apihelp-login-param-name": "Корисничко име.",
index d727c8a..96a47fc 100644 (file)
@@ -2,9 +2,18 @@
        "@metadata": {
                "authors": [
                        "Veeven",
-                       "HAUSANRIK"
+                       "HAUSANRIK",
+                       "Ravichandra"
                ]
        },
+       "apihelp-block-description": "ఓ వాడుకరిని నిరోధించండి.",
+       "apihelp-block-param-reason": "నిరోధానికి కారణం.",
+       "apihelp-block-param-nocreate": "ఖాతా సృష్టింపుని నివారించు",
+       "apihelp-createaccount-param-name": "వాడుకరి పేరు:",
+       "apihelp-delete-description": "ఓ పేజీని తొలగించు.",
+       "apihelp-edit-param-minor": "చిన్న మార్పు",
+       "apihelp-edit-example-edit": "ఓ పేజీని మార్చు.",
+       "apihelp-emailuser-description": "వాడుకరికి ఈమెయిలు పంపించండి.",
        "apihelp-feedrecentchanges-example-simple": "ఇటీవలి మార్పులను చూడండి",
-       "apihelp-rawfm-description": "బయà°\9fà°\95à±\81 à°µà°\9aà±\8dà°\9aà°¿à°¨ à°¸à°®à°¾à°\9aారo, à°¡à±\80à°¬à°\97à±\8dà°\97à°¿à°\82à°\97à±\8d à°\85à°\82శమà±\81à°¤à±\8a à°\95లిపి, JSON à°ªà°¦à±\8dధతిలà±\8a (HTMLలో అందంగా-ముద్రించు)"
+       "apihelp-rawfm-description": "బయà°\9fà°\95à±\81 à°µà°\9aà±\8dà°\9aà°¿à°¨ à°¸à°®à°¾à°\9aారo, à°¡à±\80à°¬à°\97à±\8dà°\97à°¿à°\82à°\97à±\8d à°\85à°\82శమà±\81à°¤à±\8b à°\95లిపి, JSON à°ªà°¦à±\8dధతిలà±\8b (HTMLలో అందంగా-ముద్రించు)"
 }
index 95eea7d..a10452d 100644 (file)
@@ -5,7 +5,8 @@
                        "Sadrettin",
                        "Uğurkent",
                        "Gorizon",
-                       "HakanIST"
+                       "HakanIST",
+                       "Imabadplayer"
                ]
        },
        "apihelp-block-description": "Bir kullanıcıyı engelle.",
@@ -46,6 +47,7 @@
        "apihelp-move-param-noredirect": "Yönlendirme oluşturmayın.",
        "apihelp-opensearch-param-limit": "Verilecek azami sonuç sayısı.",
        "apihelp-options-example-reset": "Tüm tercihleri sıfırla",
+       "apihelp-query+mystashedfiles-param-limit": "Alınacak kaç dosya var",
        "api-help-title": "MediaWiki API yardımı",
        "api-help-parameters": "{{PLURAL:$1|Parametre|Parametre}}:",
        "api-help-param-limit": "$1 taneden fazla olamaz.",
diff --git a/includes/api/i18n/tt-cyrl.json b/includes/api/i18n/tt-cyrl.json
new file mode 100644 (file)
index 0000000..54c534c
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Ильнар"
+               ]
+       },
+       "apihelp-feedcontributions-param-newonly": "Битләр ясау үзгәртмәләрен генә күрсәтү."
+}
index ef69eda..8698dc8 100644 (file)
@@ -8,7 +8,9 @@
                        "Dars",
                        "Umherirrender",
                        "Macofe",
-                       "Mix Gerder"
+                       "Mix Gerder",
+                       "Piramidion",
+                       "Andriykopanytsia"
                ]
        },
        "apihelp-main-description": "<div class=\"hlist plainlinks api-main-links\">\n* [[mw:API:Main_page|Документація]]\n* [[mw:API:FAQ|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-шапку з ключем «MediaWiki-API-Error», а тоді і значення шапки, і код помилки, надіслані назад, будуть встановлені з тим же значенням. Більше інформації див. на [[mw:API:Errors_and_warnings|API: Errors and warnings]].",
        "apihelp-query+allusers-paramvalue-prop-rights": "Перераховує права, які користувач має.",
        "apihelp-query+allusers-paramvalue-prop-editcount": "Додає кількість редагувань користувача.",
        "apihelp-query+allusers-paramvalue-prop-registration": "Додає часову мітку, коли користувач зареєструвався, якщо доступно (може бути пустою).",
+       "apihelp-query+allusers-paramvalue-prop-centralids": "Додає центральні ідентифікатори і стан вкладення для користувача.",
        "apihelp-query+allusers-param-limit": "Скільки всього виводити імен користувачів.",
        "apihelp-query+allusers-param-witheditsonly": "Перерахувати лише користувачів, що зробили редагування.",
        "apihelp-query+allusers-param-activeusers": "Перерахувати лише користувачів, що були активні $1 {{PLURAL:$1|останній день|останні дні|останніх днів}}.",
        "apihelp-query+userinfo-paramvalue-prop-acceptlang": "Дублює шапку <code>Accept-Language</code>, надіслану клієнтом у структурованому форматі.",
        "apihelp-query+userinfo-paramvalue-prop-registrationdate": "ДОдає дату реєстрації користувача.",
        "apihelp-query+userinfo-paramvalue-prop-unreadcount": "Додає кількість непрочитаних сторінок у списку спостереження користувача (максимально $1; видає «<samp>$2</samp>», якщо більше).",
+       "apihelp-query+userinfo-paramvalue-prop-centralids": "Додає центральні ідентифікатори і стан вкладення для користувача.",
        "apihelp-query+userinfo-example-simple": "Отримати інформацію про поточного користувача.",
        "apihelp-query+userinfo-example-data": "Отримати додаткову інформацію про поточного користувача.",
        "apihelp-query+users-description": "Отримати інформацію про список користувачів.",
        "apihelp-query+users-paramvalue-prop-registration": "Додає часову мітку реєстрації користувача.",
        "apihelp-query+users-paramvalue-prop-emailable": "Помічає чи хоче користувач отримувати електронну пошту через [[Special:Emailuser]].",
        "apihelp-query+users-paramvalue-prop-gender": "Помічає стать користувача. Повертає \"male\", \"female\", або \"unknown\".",
+       "apihelp-query+users-paramvalue-prop-centralids": "Додає центральні ідентифікатори і стан вкладення для користувача.",
        "apihelp-query+users-param-users": "Список користувачів, для яких отримати інформацію.",
        "apihelp-query+users-param-token": "Використати натомість <kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>.",
        "apihelp-query+users-example-simple": "Вивести інформацію для користувача <kbd>Example</kbd>.",
        "apihelp-xml-param-includexmlnamespace": "Якщо вказано, додає простір назв XML.",
        "apihelp-xmlfm-description": "Вивести дані у форматі XML (вивід відформатованого коду за допомогою HTML).",
        "api-format-title": "Результат запиту до API MediaWiki",
-       "api-format-prettyprint-header": "Це HTML-представлення формату $1. HTML є гарним для налагодження, однак не придатний для прикладного використання.\n\nУкажіть значення для параметру <var>format</var>, для того щоб змінити формат. Для перегляду не-HTML-представлення формату, $1, вкажіть <kbd>format=$2</kbd>.\n\nДив. [[mw:API|повну документацію]], або [[Special:ApiHelp/main|довідку з API]] для детальнішої інформації.",
+       "api-format-prettyprint-header": "Це HTML-представлення формату $1. HTML є гарним для налагодження, однак не придатний для прикладного використання.\n\nУкажіть значення для параметра <var>format</var>, для того щоб змінити формат. Для перегляду не-HTML-представлення формату, $1, вкажіть <kbd>format=$2</kbd>.\n\nДив. [[mw:API|повну документацію]], або [[Special:ApiHelp/main|довідку з API]] для детальнішої інформації.",
        "api-format-prettyprint-header-only-html": "Це HTML-представлення призначене для налагодження, однак не придатне для прикладного використання.\n\nДив. [[mw:API|повну документацію]], або [[Special:ApiHelp/main|довідку з API]] для детальнішої інформації.",
        "api-pageset-param-titles": "Список назв над якими працювати.",
        "api-pageset-param-pageids": "Список ідентифікаторів сторінок над якими працювати.",
index df7756d..7f5e0a0 100644 (file)
        "apihelp-options-example-reset": "Mặc định lại các tùy chọn",
        "apihelp-paraminfo-param-helpformat": "Định dạng chuỗi trợ giúp.",
        "apihelp-parse-param-summary": "Lời tóm lược để phân tích.",
-       "apihelp-parse-param-section": "Chỉ truy xuất nội dung của số phần này; nếu có <kbd>new</kbd> thì tạo phần mới.\n\nPhần <kbd>new</kbd> chỉ được chấp nhận khi định rõ <var>text</var>.",
+       "apihelp-parse-param-section": "Chỉ phân tích nội dung của số phần này.\n\nNếu có <kbd>new</kbd> thì phân tích <var>$1text</var> và <var>$1sectiontitle</var> như thể thêm phần mới vào trang.\n\nPhần <kbd>new</kbd> chỉ được chấp nhận khi định rõ <var>text</var>.",
        "apihelp-parse-param-disablelimitreport": "Bỏ qua thông báo bộ tiền xử lý (“NewPP limit report”) khi cho ra kết quả bộ xử lý.",
        "apihelp-parse-example-page": "Phân tích trang.",
        "apihelp-parse-example-text": "Phân tích văn bản wiki.",
        "apihelp-json-description": "Cho ra dữ liệu dưới dạng JSON.",
        "apihelp-jsonfm-description": "Cho ra dữ liệu dưới dạng JSON (định dạng bằng HTML).",
        "apihelp-none-description": "Không cho ra gì.",
-       "apihelp-rawfm-description": "Cho ra dữ liệu với các phần tử gỡ lỗi dưới dạng JSON (định dạng bằng HTML).",
+       "apihelp-rawfm-description": "Cho ra dữ liệu bao gồm các phần tử gỡ lỗi dưới dạng JSON (định dạng bằng HTML).",
        "apihelp-xml-description": "Cho ra dữ liệu dưới dạng XML.",
        "apihelp-xmlfm-description": "Cho ra dữ liệu dưới dạng XML (định dạng bằng HTML).",
        "api-format-title": "Kết quả API MediaWiki",
index ba07419..fffe170 100644 (file)
@@ -15,7 +15,9 @@
                        "Zhxy 519",
                        "御坂美琴",
                        "RyRubyy",
-                       "Umherirrender"
+                       "Umherirrender",
+                       "Apflu",
+                       "Hzy980512"
                ]
        },
        "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: 错误与警告]]。",
@@ -72,7 +74,7 @@
        "apihelp-createaccount-example-pass": "创建用户<kbd>testuser</kbd>和密码<kbd>test123</kbd>。",
        "apihelp-createaccount-example-mail": "创建用户<kbd>testmailuser</kbd>并电邮发送一个随机生成的密码。",
        "apihelp-delete-description": "删除一个页面。",
-       "apihelp-delete-param-title": "你所希望删除的页面的标题。不能与<var>$1pageid</var>一起使用。",
+       "apihelp-delete-param-title": "要删除的页面标题。不能与<var>$1pageid</var>一起使用。",
        "apihelp-delete-param-pageid": "要删除的页面的页面 ID。不能与<var>$1title</var>一起使用。",
        "apihelp-delete-param-reason": "删除原因。如果未设置,将使用一个自动生成的原因。",
        "apihelp-delete-param-tags": "要在删除日志中应用到实体的更改标签。",
        "apihelp-query+allrevisions-param-generatetitles": "当作为生成器使用时,生成标题而不是修订ID。",
        "apihelp-query+allrevisions-example-user": "列出由用户<kbd>Example</kbd>作出的最近50次贡献。",
        "apihelp-query+allrevisions-example-ns-main": "列举主名字空间中的前50次修订。",
+       "apihelp-query+mystashedfiles-param-prop": "要检索文件的属性。",
+       "apihelp-query+mystashedfiles-paramvalue-prop-type": "检索文件的MIME类型和媒体类型。",
+       "apihelp-query+mystashedfiles-param-limit": "获取多少文件。",
        "apihelp-query+alltransclusions-description": "列出所有嵌入页面(使用&#123;&#123;x&#125;&#125;嵌入的页面),包括不存在的。",
        "apihelp-query+alltransclusions-param-from": "要列举的起始嵌入标题。",
        "apihelp-query+alltransclusions-param-to": "要列举的最终嵌入标题。",
        "apihelp-query+allusers-param-limit": "返回的总计用户数。",
        "apihelp-query+allusers-param-witheditsonly": "只列出有编辑的用户。",
        "apihelp-query+allusers-param-activeusers": "只列出最近$1{{PLURAL:$1|天}}内活跃的用户。",
+       "apihelp-query+allusers-param-attachedwiki": "与<kbd>$1prop=centralids</kbd>一起使用,也表明用户是否附加于此ID定义的wiki。",
        "apihelp-query+allusers-example-Y": "列出以<kbd>Y</kbd>开头的用户。",
        "apihelp-query+backlinks-description": "查找所有链接至指定页面的页面。",
        "apihelp-query+backlinks-param-title": "要搜索的标题。不能与<var>$1pageid</var>一起使用。",
        "apihelp-query+contributors-param-limit": "返回的贡献数。",
        "apihelp-query+contributors-example-simple": "显示<kbd>Main Page</kbd>的贡献。",
        "apihelp-query+deletedrevisions-description": "获得删除修订版本信息。\n\n可在很多途径中使用:\n# 获得一组页面的已删除修订,通过设置标题或页面ID。以标题和时间戳排序。\n# 通过设置它们的ID与修订ID获得关于一组已删除修订。以修订ID排序。",
+       "apihelp-query+deletedrevisions-param-start": "要开始枚举的时间戳。当处理修订ID列表时会被忽略。",
+       "apihelp-query+deletedrevisions-param-end": "要停止枚举的时间戳。当处理修订ID列表时会被忽略。",
        "apihelp-query+deletedrevisions-param-tag": "只列出被此标签标记的修订。",
        "apihelp-query+deletedrevisions-param-user": "只列出此用户做出的修订。",
        "apihelp-query+deletedrevisions-param-excludeuser": "不要列出此用户做出的修订。",
        "apihelp-query+deletedrevs-param-user": "只列出此用户做出的修订。",
        "apihelp-query+deletedrevs-param-excludeuser": "不要列出此用户做出的修订。",
        "apihelp-query+deletedrevs-param-namespace": "只列出此名字空间的页面。",
+       "apihelp-query+deletedrevs-param-limit": "要列出的最大修订数量。",
        "apihelp-query+deletedrevs-param-prop": "要获取的属性:\n;revid:添加被删除修订的修订ID。\n;parentid:添加上一修订的修订ID至页面。\n;user:添加做出修订的用户。\n;userid:添加做出修订的用户ID。\n;comment:添加修订摘要。\n;parsedcomment:添加解析过的修订摘要。\n;minor:如果修订是小编辑则加标签。\n;len:添加修订长度(字节)。\n;sha1:添加修订的SHA-1(base 16)。\n;content:添加修订内容。\n;token:<span class=\"apihelp-deprecated\">已弃用。</span>提供编辑令牌。\n;tags:修订标签。",
        "apihelp-query+deletedrevs-example-mode1": "列出最近已删除的对页面<kbd>Main Page</kbd>和<kbd>Talk:Main Page</kbd>的贡献,带内容(模式1)。",
        "apihelp-query+deletedrevs-example-mode2": "列出由<kbd>Bob</kbd>作出的最近50次已删除贡献(模式2)。",
        "apihelp-query+deletedrevs-example-mode3-main": "列出前50次主名字空间已删除贡献(模式3)。",
        "apihelp-query+deletedrevs-example-mode3-talk": "列出前50次{{ns:talk}}名字空间已删除页面(模式3)。",
        "apihelp-query+disabled-description": "此查询模块已被禁用。",
+       "apihelp-query+duplicatefiles-description": "根据哈希值列出此给定文件的所有副本。",
        "apihelp-query+duplicatefiles-param-limit": "返回多少重复文件。",
        "apihelp-query+duplicatefiles-param-dir": "罗列所采用的方向。",
        "apihelp-query+duplicatefiles-param-localonly": "只看本地存储库的文件。",
        "apihelp-query+imageinfo-example-dated": "取得有关[[:File:Test.jpg]]自2008年以来版本的信息。",
        "apihelp-query+images-description": "返回指定页面上包含的所有文件。",
        "apihelp-query+images-param-limit": "返回多少文件。",
-       "apihelp-query+images-param-images": "只列出这些文件。对于检查某一页面使用某一文件很有用。",
+       "apihelp-query+images-param-images": "只列出这些文件。对于检查某一页面是否使用某一文件很有用。",
        "apihelp-query+images-param-dir": "罗列所采用的方向。",
        "apihelp-query+images-example-simple": "获取[[Main Page]]使用的文件列表。",
        "apihelp-query+images-example-generator": "获取有关[[Main Page]]使用的文件的信息。",
        "apihelp-query+imageusage-param-dir": "罗列所采用的方向。",
        "apihelp-query+imageusage-param-filterredir": "如何过滤重定向。当$1redirect被启用时如果设置为nonredirects,这只会应用到第二级。",
        "apihelp-query+imageusage-param-limit": "返回总计页面数。如果<var>$1redirect</var>被启用,则限定分别适用于每一等级(这意味着将返回多达2 * <var>$1limit</var>个结果)。",
+       "apihelp-query+imageusage-param-redirect": "如果链接页面是重定向,则查找所有链接至该重定向的页面。最大限制减半。",
        "apihelp-query+imageusage-example-simple": "显示使用[[:File:Albert Einstein Head.jpg]]的页面。",
        "apihelp-query+imageusage-example-generator": "获取有关使用[[:File:Albert Einstein Head.jpg]]的页面的信息。",
        "apihelp-query+info-description": "获取基本页面信息。",
        "apihelp-query+iwlinks-param-title": "用于搜索的跨wiki链接。必须与<var>$1prefix</var>一起使用。",
        "apihelp-query+iwlinks-param-dir": "罗列所采用的方向。",
        "apihelp-query+iwlinks-example-simple": "从页面<kbd>Main Page</kbd>获得跨wiki链接。",
+       "apihelp-query+langbacklinks-description": "发现所有链接至指定语言链接的页面。\n\n可被用于查找所有带某一语言代码的链接,或所有至某一标题的链接(带指定语言)。不使用任何参数就意味着“所有语言链接”。\n\n注意这可能不考虑由扩展添加的语言链接。",
        "apihelp-query+langbacklinks-param-lang": "用于语言链接的语言。",
        "apihelp-query+langbacklinks-param-title": "要搜索的语言链接。必须与$1lang一起使用。",
        "apihelp-query+langbacklinks-param-limit": "返回的总计页面数。",
        "apihelp-query+links-description": "从指定页面返回所有链接。",
        "apihelp-query+links-param-namespace": "只显示这些名字空间的链接。",
        "apihelp-query+links-param-limit": "返回多少链接。",
-       "apihelp-query+links-param-titles": "只列出这些标题。对于检查某一页面使用某一标题很有用。",
+       "apihelp-query+links-param-titles": "只列出这些标题。对于检查某一页面是否使用某一标题很有用。",
        "apihelp-query+links-param-dir": "罗列所采用的方向。",
        "apihelp-query+links-example-simple": "从页面<kbd>Main Page</kbd>获取链接。",
        "apihelp-query+links-example-generator": "获取有关在页面<kbd>Main Page</kbd>中连接的页面的信息。",
        "apihelp-query+search-param-what": "要执行的搜索类型。",
        "apihelp-query+search-param-info": "要返回的元数据。",
        "apihelp-query+search-param-prop": "要返回的属性:",
-       "apihelp-query+search-paramvalue-prop-size": "æ·»å\8a é¡µé\9d¢å¤§å°\8fï¼\88å­\97è\8a\82ï¼\89。",
+       "apihelp-query+search-paramvalue-prop-size": "æ·»å\8a é¡µé\9d¢å¤§å°\8fï¼\8cå\8d\95ä½\8d为å­\97è\8a\82。",
        "apihelp-query+search-paramvalue-prop-wordcount": "Adds the word count of the page.",
        "apihelp-query+search-paramvalue-prop-timestamp": "Adds the timestamp of when the page was last edited.",
        "apihelp-query+search-paramvalue-prop-snippet": "Adds a parsed snippet of the page.",
        "apihelp-query+userinfo-paramvalue-prop-registrationdate": "添加用户的注册时间。",
        "apihelp-query+userinfo-paramvalue-prop-unreadcount": "Adds the count of unread pages on the user's watchlist (maximum $1; returns <samp>$2</samp> if more).",
        "apihelp-query+userinfo-paramvalue-prop-centralids": "添加中心ID并为用户附加状态。",
+       "apihelp-query+userinfo-param-attachedwiki": "与<kbd>$1prop=centralids</kbd>一起使用,表明用户是否附加于此ID定义的wiki。",
        "apihelp-query+userinfo-example-simple": "获取有关当前用户的信息。",
        "apihelp-query+userinfo-example-data": "获取有关当前用户的额外信息。",
        "apihelp-query+users-description": "获取有关列出用户的信息。",
        "apihelp-query+users-paramvalue-prop-emailable": "Tags if the user can and wants to receive email through [[Special:Emailuser]].",
        "apihelp-query+users-paramvalue-prop-gender": "Tags the gender of the user. Returns \"male\", \"female\", or \"unknown\".",
        "apihelp-query+users-paramvalue-prop-centralids": "添加中心ID并为用户附加状态。",
+       "apihelp-query+users-param-attachedwiki": "与<kbd>$1prop=centralids</kbd>一起使用,表明用户是否附加于此ID定义的wiki。",
+       "apihelp-query+users-param-users": "要获取信息的用户列表。",
        "apihelp-query+users-param-token": "请改用<kbd>[[Special:ApiHelp/query+tokens|action=query&meta=tokens]]</kbd>。",
        "apihelp-query+users-example-simple": "返回用户<kbd>Example</kbd>的信息。",
        "apihelp-query+watchlist-description": "在当前用户的监视列表中获取对页面的最近更改。",
        "apihelp-query+watchlistraw-param-fromtitle": "要列举的起始标题(带名字空间前缀)。",
        "apihelp-query+watchlistraw-param-totitle": "要列举的最终标题(带名字空间前缀)。",
        "apihelp-query+watchlistraw-example-simple": "列出当前用户的监视列表中的页面。",
+       "apihelp-query+watchlistraw-example-generator": "检索当前用户监视列表上的页面的页面信息。",
        "apihelp-revisiondelete-description": "删除和恢复修订版本。",
        "apihelp-revisiondelete-param-type": "正在执行的修订版本删除类型。",
        "apihelp-revisiondelete-param-target": "要进行修订版本删除的页面标题,如果对某一类型需要。",
        "apihelp-setnotificationtimestamp-example-page": "重置用于<kbd>Main page</kbd>的通知状态。",
        "apihelp-setnotificationtimestamp-example-pagetimestamp": "设置<kbd>Main page</kbd>的通知时间戳,这样所有从2012年1月1日起的编辑都会是未复核的。",
        "apihelp-setnotificationtimestamp-example-allpages": "重置在<kbd>{{ns:user}}</kbd>名字空间中的页面的通知状态。",
+       "apihelp-stashedit-description": "在分享的缓存中准备一次编辑。\n\n这是为了从编辑表单中通过AJAX使用,以改进页面保存的性能。",
        "apihelp-stashedit-param-title": "已开始编辑的页面标题。",
        "apihelp-stashedit-param-section": "段落数。<kbd>0</kbd>用于首段,<kbd>new</kbd>用于新的段落。",
        "apihelp-stashedit-param-sectiontitle": "新段落的标题。",
        "apihelp-upload-param-filesize": "全部上传的文件大小。",
        "apihelp-upload-param-offset": "块的偏移量(字节)。",
        "apihelp-upload-param-chunk": "大块内容。",
+       "apihelp-upload-param-async": "在可能的情况下,使潜在的大文件操作异步进行。",
+       "apihelp-upload-param-asyncdownload": "使取得URL非同步。",
        "apihelp-upload-param-leavemessage": "如果asyncdownload被使用,当完成时,在用户讨论页留下一条消息。",
        "apihelp-upload-param-statuskey": "检索此文件密钥的上传状态(通过URL上传)。",
        "apihelp-upload-param-checkstatus": "只检索指定文件密钥的上传状态。",
        "api-help-permissions-granted-to": "{{PLURAL:$1|授予}}:$2",
        "api-help-right-apihighlimits": "在API查询中使用更高的上限(慢查询:$1;快查询:$2)。慢查询的限制也适用于多值参数。",
        "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/。"
+       "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 4b0a81c..54e476f 100644 (file)
@@ -93,7 +93,7 @@
        "apihelp-feedcontributions-param-feedformat": "Feed 的格式。",
        "apihelp-feedcontributions-param-showsizediff": "顯示修訂版本之間的差異大小。",
        "apihelp-feedcontributions-example-simple": "返回使用者<kbd>Example</kbd>的貢獻。",
-       "apihelp-feedrecentchanges-description": "返回最近更改摘要。",
+       "apihelp-feedrecentchanges-description": "返回最近變更摘要。",
        "apihelp-feedrecentchanges-param-feedformat": "摘要格式。",
        "apihelp-feedrecentchanges-param-namespace": "用於限制結果的命名空間。",
        "apihelp-feedrecentchanges-param-invert": "除所選定者外的所有命名空間。",
        "apihelp-feedrecentchanges-param-hideanons": "隱藏匿名使用者做的變更。",
        "apihelp-feedrecentchanges-param-hideliu": "隱藏已註冊使用者做的變更。",
        "apihelp-feedrecentchanges-param-hidepatrolled": "隱藏已巡查的變更。",
-       "apihelp-feedrecentchanges-example-simple": "顯示最近變更。",
+       "apihelp-feedrecentchanges-example-simple": "顯示近期變更。",
        "apihelp-feedrecentchanges-example-30days": "顯示近期30天內的變動",
        "apihelp-feedwatchlist-description": "返回監視清單 feed。",
        "apihelp-feedwatchlist-param-feedformat": "Feed 的格式。",
        "apihelp-parse-example-texttitle": "解析 wikitext,指定頁面標題。",
        "apihelp-parse-example-summary": "解析一個摘要。",
        "apihelp-patrol-description": "巡查一個頁面或修訂。",
-       "apihelp-patrol-param-rcid": "要巡查的最近更改 ID。",
+       "apihelp-patrol-param-rcid": "要巡查的最近變更 ID。",
        "apihelp-patrol-param-revid": "要巡查的修訂 ID。",
-       "apihelp-patrol-example-rcid": "巡查一次最近更改。",
+       "apihelp-patrol-example-rcid": "巡查一次最近變更。",
        "apihelp-patrol-example-revid": "巡查一個修訂。",
        "apihelp-protect-description": "變更頁面的保護層級。",
        "apihelp-protect-param-title": "要(解除)保護頁面的標題。 不能與 $1pageid 一起使用。",
        "apihelp-query+categorymembers-param-limit": "回傳的頁面數量上限。",
        "apihelp-query+contributors-param-limit": "要回傳的貢獻人員數量。",
        "apihelp-query+duplicatefiles-param-limit": "要回傳的重複檔案數量。",
-       "apihelp-query+embeddedin-param-filterredir": "如何過濾重向。",
+       "apihelp-query+embeddedin-param-filterredir": "如何過濾重新導向。",
        "apihelp-query+embeddedin-param-limit": "要回傳的頁面總數。",
        "apihelp-query+extlinks-description": "回傳所有指定頁面的外部 URL (非 interwiki)。",
        "apihelp-query+extlinks-param-limit": "要回傳的連結數量。",
index 298f6e2..29db9c6 100644 (file)
@@ -178,7 +178,7 @@ class HTMLFileCache extends FileCacheBase {
                        return $text;
                }
 
-               wfDebug( __METHOD__ . "()\n", 'log' );
+               wfDebug( __METHOD__ . "()\n", 'private' );
 
                $now = wfTimestampNow();
                if ( $this->useGzip() ) {
index 2494ef1..1741e64 100644 (file)
@@ -364,12 +364,24 @@ class ChangesList extends ContextSource {
        }
 
        /**
-        * @param string $s HTML to update
+        * @param string $s Article link will be appended to this string, in place.
+        * @param RecentChange $rc
+        * @param bool $unpatrolled
+        * @param bool $watched
+        * @deprecated since 1.27, use getArticleLink instead.
+        */
+       public function insertArticleLink( &$s, RecentChange $rc, $unpatrolled, $watched ) {
+               $s .= $this->getArticleLink( $rc, $unpatrolled, $watched );
+       }
+
+       /**
         * @param RecentChange $rc
         * @param bool $unpatrolled
         * @param bool $watched
+        * @return string HTML
+        * @since 1.26
         */
-       public function insertArticleLink( &$s, &$rc, $unpatrolled, $watched ) {
+       public function getArticleLink( &$rc, $unpatrolled, $watched ) {
                $params = array();
                if ( $rc->getTitle()->isRedirect() ) {
                        $params = array( 'redirect' => 'no' );
@@ -389,23 +401,12 @@ class ChangesList extends ContextSource {
                # RTL/LTR marker
                $articlelink .= $this->getLanguage()->getDirMark();
 
+               # TODO: Deprecate the $s argument, it seems happily unused.
+               $s = '';
                Hooks::run( 'ChangesListInsertArticleLink',
                        array( &$this, &$articlelink, &$s, &$rc, $unpatrolled, $watched ) );
 
-               $s .= " $articlelink";
-       }
-
-       /**
-        * @param RecentChange $rc
-        * @param bool $unpatrolled
-        * @param bool $watched
-        * @return string
-        * @since 1.26
-        */
-       public function getArticleLink( RecentChange $rc, $unpatrolled, $watched ) {
-               $s = '';
-               $this->insertArticleLink( $s, $rc, $unpatrolled, $watched );
-               return $s;
+               return "{$s} {$articlelink}";
        }
 
        /**
@@ -480,20 +481,6 @@ class ChangesList extends ContextSource {
                }
        }
 
-       /**
-        * Check whether to enable recent changes patrol features
-        *
-        * @deprecated since 1.22
-        * @return bool
-        */
-       public static function usePatrol() {
-               global $wgUser;
-
-               wfDeprecated( __METHOD__, '1.22' );
-
-               return $wgUser->useRCPatrol();
-       }
-
        /**
         * Returns the string which indicates the number of watching users
         * @param int $count Number of user watching a page
@@ -638,9 +625,11 @@ class ChangesList extends ContextSource {
                if ( $rc instanceof RecentChange ) {
                        $isPatrolled = $rc->mAttribs['rc_patrolled'];
                        $rcType = $rc->mAttribs['rc_type'];
+                       $rcLogType = $rc->mAttribs['rc_log_type'];
                } else {
                        $isPatrolled = $rc->rc_patrolled;
                        $rcType = $rc->rc_type;
+                       $rcLogType = $rc->rc_log_type;
                }
 
                if ( !$isPatrolled ) {
@@ -650,6 +639,9 @@ class ChangesList extends ContextSource {
                        if ( $user->useNPPatrol() && $rcType == RC_NEW ) {
                                return true;
                        }
+                       if ( $user->useFilePatrol() && $rcLogType == 'upload' ) {
+                               return true;
+                       }
                }
 
                return false;
index ed374b0..1c49545 100644 (file)
@@ -161,19 +161,22 @@ class EnhancedChangesList extends ChangesList {
        protected function recentChangesBlockGroup( $block ) {
 
                # Add the namespace and title of the block as part of the class
-               $classes = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
+               $tableClasses = array( 'mw-collapsible', 'mw-collapsed', 'mw-enhanced-rc' );
                if ( $block[0]->mAttribs['rc_log_type'] ) {
                        # Log entry
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
+                       $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-log-'
                                . $block[0]->mAttribs['rc_log_type'] );
                } else {
-                       $classes[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
+                       $tableClasses[] = Sanitizer::escapeClass( 'mw-changeslist-ns'
                                . $block[0]->mAttribs['rc_namespace'] . '-' . $block[0]->mAttribs['rc_title'] );
                }
-               $classes[] = $block[0]->watched && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
-                       ? 'mw-changeslist-line-watched' : 'mw-changeslist-line-not-watched';
-               $r = Html::openElement( 'table', array( 'class' => $classes ) ) .
-                       Html::openElement( 'tr' );
+               if ( $block[0]->watched
+                       && $block[0]->mAttribs['rc_timestamp'] >= $block[0]->watched
+               ) {
+                       $tableClasses[] = 'mw-changeslist-line-watched';
+               } else {
+                       $tableClasses[] = 'mw-changeslist-line-not-watched';
+               }
 
                # Collate list of users
                $userlinks = array();
@@ -243,61 +246,44 @@ class EnhancedChangesList extends ChangesList {
                        array_push( $users, $text );
                }
 
-               $users = ' <span class="changedby">'
-                       . $this->msg( 'brackets' )->rawParams(
-                               implode( $this->message['semicolon-separator'], $users )
-                       )->escaped() . '</span>';
-
-               $tl = '<span class="mw-collapsible-toggle mw-collapsible-arrow ' .
-                       'mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>';
-               $r .= "<td>$tl</td>";
-
-               # Main line
-               $r .= '<td class="mw-enhanced-rc">' . $this->recentChangesFlags(
-                       $collectedRcFlags
-               );
-
-               # Timestamp
-               $r .= '&#160;' . $block[0]->timestamp . '&#160;</td><td>';
-
                # Article link
+               $articleLink = '';
+               $revDeletedMsg = false;
                if ( $namehidden ) {
-                       $r .= ' <span class="history-deleted">' .
-                               $this->msg( 'rev-deleted-event' )->escaped() . '</span>';
+                       $revDeletedMsg = $this->msg( 'rev-deleted-event' )->escaped();
                } elseif ( $allLogs ) {
-                       $r .= $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
+                       $articleLink = $this->maybeWatchedLink( $block[0]->link, $block[0]->watched );
                } else {
-                       $this->insertArticleLink( $r, $block[0], $block[0]->unpatrolled, $block[0]->watched );
+                       $articleLink = $this->getArticleLink( $block[0], $block[0]->unpatrolled, $block[0]->watched );
                }
 
-               $r .= $this->getLanguage()->getDirMark();
-
                $queryParams['curid'] = $curId;
 
                # Sub-entries
-               $lines = '';
+               $lines = array();
                foreach ( $block as $i => $rcObj ) {
                        $line = $this->getLineData( $block, $rcObj, $queryParams );
-                       $lines .= $line;
                        if ( !$line ) {
                                // completely ignore this RC entry if we don't want to render it
                                unset( $block[$i] );
                        }
+                       $lines[] = $line;
                }
                // Further down are some assumptions that $block is a 0-indexed array
                // with (count-1) as last key. Let's make sure it is.
                $block = array_values( $block );
-               if ( empty( $block ) ) {
+
+               if ( empty( $block ) || !$lines ) {
                        // if we can't show anything, don't display this block altogether
                        return '';
                }
 
-               $r .= $this->getLogText( $block, $queryParams, $allLogs,
-                       $collectedRcFlags['newpage'], $namehidden );
-
-               $r .= ' <span class="mw-changeslist-separator">. .</span> ';
+               $logText = $this->getLogText( $block, $queryParams, $allLogs,
+                       $collectedRcFlags['newpage'], $namehidden
+               );
 
                # Character difference (does not apply if only log items)
+               $charDifference = false;
                if ( $RCShowChangedSize && !$allLogs ) {
                        $last = 0;
                        $first = count( $block ) - 1;
@@ -309,37 +295,42 @@ class EnhancedChangesList extends ChangesList {
                                $first--;
                        }
                        # Get net change
-                       $chardiff = $this->formatCharacterDifference( $block[$first], $block[$last] );
-
-                       if ( $chardiff == '' ) {
-                               $r .= ' ';
-                       } else {
-                               $r .= ' ' . $chardiff . ' <span class="mw-changeslist-separator">. .</span> ';
-                       }
-               }
-
-               $r .= $users;
-               $r .= $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
-               $r .= '</td></tr>';
-
-               if ( !$lines ) {
-                       // if there are no lines to be rendered (all aborted by hook), don't render the block
-                       return '';
-               }
-
-               $r .= $lines;
-               $r .= "</table>\n";
+                       $charDifference = $this->formatCharacterDifference( $block[$first], $block[$last] );
+               }
+
+               $numberofWatchingusers = $this->numberofWatchingusers( $block[0]->numberofWatchingusers );
+               $usersList = $this->msg( 'brackets' )->rawParams(
+                       implode( $this->message['semicolon-separator'], $users )
+               )->escaped();
+
+               $templateParams = array(
+                       'articleLink' => $articleLink,
+                       'charDifference' => $charDifference,
+                       'collectedRcFlags' => $this->recentChangesFlags( $collectedRcFlags ),
+                       'languageDirMark' => $this->getLanguage()->getDirMark(),
+                       'lines' => $lines,
+                       'logText' => $logText,
+                       'numberofWatchingusers' => $numberofWatchingusers,
+                       'rev-deleted-event' => $revDeletedMsg,
+                       'tableClasses' => $tableClasses,
+                       'timestamp' => $block[0]->timestamp,
+                       'users' => $usersList,
+               );
 
                $this->rcCacheIndex++;
 
-               return $r;
+               $templateParser = new TemplateParser();
+               return $templateParser->processTemplate(
+                       'EnhancedChangesListGroup',
+                       $templateParams
+               );
        }
 
        /**
         * @param RCCacheEntry[] $block
         * @param RCCacheEntry $rcObj
         * @param array $queryParams
-        * @return string
+        * @return array
         * @throws Exception
         * @throws FatalError
         * @throws MWException
@@ -351,9 +342,13 @@ class EnhancedChangesList extends ChangesList {
                $classes = array();
                $type = $rcObj->mAttribs['rc_type'];
                $data = array();
+               $lineParams = array();
 
-               $trClass = $rcObj->watched && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
-                       ? ' class="mw-enhanced-watched"' : '';
+               if ( $rcObj->watched
+                       && $rcObj->mAttribs['rc_timestamp'] >= $rcObj->watched
+               ) {
+                       $lineParams['classes'] = array( 'mw-enhanced-watched' );
+               }
                $separator = ' <span class="mw-changeslist-separator">. .</span> ';
 
                $data['recentChangesFlags'] = array(
@@ -430,27 +425,23 @@ class EnhancedChangesList extends ChangesList {
                        array( $this, &$data, $block, $rcObj ) );
                if ( !$success ) {
                        // skip entry if hook aborted it
-                       return '';
+                       return array();
                }
 
-               $line = '<tr' . $trClass . '><td></td><td class="mw-enhanced-rc">';
                if ( isset( $data['recentChangesFlags'] ) ) {
-                       $line .= $this->recentChangesFlags( $data['recentChangesFlags'] );
+                       $lineParams['recentChangesFlags'] = $this->recentChangesFlags( $data['recentChangesFlags'] );
                        unset( $data['recentChangesFlags'] );
                }
-               $line .= '&#160;</td><td class="mw-enhanced-rc-nested">';
 
                if ( isset( $data['timestampLink'] ) ) {
-                       $line .= '<span class="mw-enhanced-rc-time">' . $data['timestampLink'] . '</span>';
+                       $lineParams['timestampLink'] = $data['timestampLink'];
                        unset( $data['timestampLink'] );
                }
 
                // everything else: makes it easier for extensions to add or remove data
-               $line .= implode( '', $data );
+               $lineParams['data'] = array_values( $data );
 
-               $line .= "</td></tr>\n";
-
-               return $line;
+               return $lineParams;
        }
 
        /**
index 31b355d..6f05dc4 100644 (file)
@@ -74,10 +74,16 @@ class OldChangesList extends ChangesList {
         */
        private function formatChangeLine( RecentChange $rc, array &$classes, $watched ) {
                $html = '';
+               $unpatrolled = $this->showAsUnpatrolled( $rc );
 
                if ( $rc->mAttribs['rc_log_type'] ) {
                        $logtitle = SpecialPage::getTitleFor( 'Log', $rc->mAttribs['rc_log_type'] );
                        $this->insertLog( $html, $logtitle, $rc->mAttribs['rc_log_type'] );
+                       $flags = $this->recentChangesFlags( array( 'unpatrolled' =>$unpatrolled,
+                               'bot' => $rc->mAttribs['rc_bot'] ), '' );
+                       if ( $flags !== '' ) {
+                               $html .= ' ' . $flags;
+                       }
                // Log entries (old format) or log targets, and special pages
                } elseif ( $rc->mAttribs['rc_namespace'] == NS_SPECIAL ) {
                        list( $name, $htmlubpage ) = SpecialPageFactory::resolveAlias( $rc->mAttribs['rc_title'] );
@@ -86,7 +92,6 @@ class OldChangesList extends ChangesList {
                        }
                // Regular entries
                } else {
-                       $unpatrolled = $this->showAsUnpatrolled( $rc );
                        $this->insertDiffHist( $html, $rc, $unpatrolled );
                        # M, N, b and ! (minor, new, bot and unpatrolled)
                        $html .= $this->recentChangesFlags(
@@ -98,7 +103,7 @@ class OldChangesList extends ChangesList {
                                ),
                                ''
                        );
-                       $this->insertArticleLink( $html, $rc, $unpatrolled, $watched );
+                       $html .= $this->getArticleLink( $rc, $unpatrolled, $watched );
                }
                # Edit/log timestamp
                $this->insertTimestamp( $html, $rc );
index 7c6fbb9..9ae30c0 100644 (file)
@@ -456,11 +456,13 @@ class RecentChange {
         * @return array Array of permissions errors, see Title::getUserPermissionsErrors()
         */
        public function doMarkPatrolled( User $user, $auto = false ) {
-               global $wgUseRCPatrol, $wgUseNPPatrol;
+               global $wgUseRCPatrol, $wgUseNPPatrol, $wgUseFilePatrol;
                $errors = array();
-               // If recentchanges patrol is disabled, only new pages
-               // can be patrolled
-               if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) ) {
+               // If recentchanges patrol is disabled, only new pages or new file versions
+               // can be patrolled, provided the appropriate config variable is set
+               if ( !$wgUseRCPatrol && ( !$wgUseNPPatrol || $this->getAttribute( 'rc_type' ) != RC_NEW ) &&
+                       ( !$wgUseFilePatrol || !( $this->getAttribute( 'rc_type' ) == RC_LOG &&
+                       $this->getAttribute( 'rc_log_type' ) == 'upload' ) ) ) {
                        $errors[] = array( 'rcpatroldisabled' );
                }
                // Automatic patrol needs "autopatrol", ordinary patrol needs "patrol"
@@ -700,10 +702,12 @@ class RecentChange {
         * @param string $params
         * @param int $newId
         * @param string $actionCommentIRC
+        * @param int $revId Id of associated revision, if any
         * @return RecentChange
         */
        public static function newLogEntry( $timestamp, &$title, &$user, $actionComment, $ip,
-               $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '' ) {
+               $type, $action, $target, $logComment, $params, $newId = 0, $actionCommentIRC = '',
+               $revId = 0 ) {
                global $wgRequest;
 
                # # Get pageStatus for email notification
@@ -727,6 +731,10 @@ class RecentChange {
                                break;
                }
 
+               // Allow unpatrolled status when an associated rev id is passed
+               // May be used in core by moves and uploads
+               $markPatrolled = ( $revId > 0 ) ? $user->isAllowed( 'autopatrol' ) : true;
+
                $rc = new RecentChange;
                $rc->mTitle = $target;
                $rc->mPerformer = $user;
@@ -741,11 +749,11 @@ class RecentChange {
                        'rc_user' => $user->getId(),
                        'rc_user_text' => $user->getName(),
                        'rc_comment' => $logComment,
-                       'rc_this_oldid' => 0,
+                       'rc_this_oldid' => $revId,
                        'rc_last_oldid' => 0,
                        'rc_bot' => $user->isAllowed( 'bot' ) ? $wgRequest->getBool( 'bot', true ) : 0,
                        'rc_ip' => self::checkIPAddress( $ip ),
-                       'rc_patrolled' => 1,
+                       'rc_patrolled' => $markPatrolled ? 1 : 0,
                        'rc_new' => 0, # obsolete
                        'rc_old_len' => null,
                        'rc_new_len' => null,
@@ -960,4 +968,3 @@ class RecentChange {
                return $unserializedParams;
        }
 }
-
index 5aac495..a8c9f7b 100644 (file)
@@ -212,6 +212,22 @@ class ChangeTags {
                        );
                }
 
+               if ( $log_id && !$rev_id ) {
+                       $rev_id = $dbw->selectField(
+                               'log_search',
+                               'ls_value',
+                               array( 'ls_field' => 'associated_rev_id', 'ls_log_id' => $log_id ),
+                               __METHOD__
+                       );
+               } elseif ( !$log_id && $rev_id ) {
+                       $log_id = $dbw->selectField(
+                               'log_search',
+                               'ls_log_id',
+                               array( 'ls_field' => 'associated_rev_id', 'ls_value' => $rev_id ),
+                               __METHOD__
+                       );
+               }
+
                // update the tag_summary row
                $prevTags = array();
                if ( !self::updateTagSummaryRow( $tagsToAdd, $tagsToRemove, $rc_id, $rev_id,
index 9ed9bc2..0bc8d0f 100644 (file)
@@ -25,7 +25,6 @@
  * @ingroup UtfNormal
  */
 
-
 use UtfNormal\Utils;
 
 /**
index a8850fc..df5f71c 100644 (file)
@@ -172,7 +172,6 @@ abstract class ContextSource implements IContextSource {
                return $this->getContext()->getStats();
        }
 
-
        /**
         * Get a Message object with context set
         * Parameters are the same as wfMessage()
index 9b6e1f3..afb5704 100644 (file)
@@ -123,8 +123,13 @@ class RequestContext implements IContextSource, MutableContext {
         */
        public function getRequest() {
                if ( $this->request === null ) {
-                       global $wgRequest; # fallback to $wg till we can improve this
-                       $this->request = $wgRequest;
+                       global $wgCommandLineMode;
+                       // create the WebRequest object on the fly
+                       if ( $wgCommandLineMode ) {
+                               $this->request = new FauxRequest( array() );
+                       } else {
+                               $this->request = new WebRequest();
+                       }
                }
 
                return $this->request;
@@ -508,7 +513,7 @@ class RequestContext implements IContextSource, MutableContext {
                return array(
                        'ip' => $this->getRequest()->getIP(),
                        'headers' => $this->getRequest()->getAllHeaders(),
-                       'sessionId' => session_id(),
+                       'sessionId' => MediaWiki\Session\SessionManager::getGlobalSession()->getId(),
                        'userId' => $this->getUser()->getId()
                );
        }
@@ -536,7 +541,9 @@ class RequestContext implements IContextSource, MutableContext {
         * @since 1.21
         */
        public static function importScopedSession( array $params ) {
-               if ( session_id() != '' && strlen( $params['sessionId'] ) ) {
+               if ( strlen( $params['sessionId'] ) &&
+                       MediaWiki\Session\SessionManager::getGlobalSession()->isPersistent()
+               ) {
                        // Sanity check to avoid sending random cookies for the wrong users.
                        // This method should only called by CLI scripts or by HTTP job runners.
                        throw new MWException( "Sessions can only be imported when none is active." );
@@ -558,23 +565,38 @@ class RequestContext implements IContextSource, MutableContext {
                        global $wgRequest, $wgUser;
 
                        $context = RequestContext::getMain();
+
                        // Commit and close any current session
-                       session_write_close(); // persist
-                       session_id( '' ); // detach
-                       $_SESSION = array(); // clear in-memory array
-                       // Remove any user IP or agent information
-                       $context->setRequest( new FauxRequest() );
+                       if ( MediaWiki\Session\PHPSessionHandler::isEnabled() ) {
+                               session_write_close(); // persist
+                               session_id( '' ); // detach
+                               $_SESSION = array(); // clear in-memory array
+                       }
+
+                       // Get new session, if applicable
+                       $session = null;
+                       if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID
+                               $manager = MediaWiki\Session\SessionManager::singleton();
+                               $session = $manager->getSessionById( $params['sessionId'], true )
+                                       ?: $manager->getEmptySession();
+                       }
+
+                       // Remove any user IP or agent information, and attach the request
+                       // with the new session.
+                       $context->setRequest( new FauxRequest( array(), false, $session ) );
                        $wgRequest = $context->getRequest(); // b/c
+
                        // Now that all private information is detached from the user, it should
                        // be safe to load the new user. If errors occur or an exception is thrown
                        // and caught (leaving the main context in a mixed state), there is no risk
                        // of the User object being attached to the wrong IP, headers, or session.
                        $context->setUser( $user );
                        $wgUser = $context->getUser(); // b/c
-                       if ( strlen( $params['sessionId'] ) ) { // don't make a new random ID
-                               wfSetupSession( $params['sessionId'] ); // sets $_SESSION
+                       if ( $session && MediaWiki\Session\PHPSessionHandler::isEnabled() ) {
+                               session_id( $session->getId() );
+                               MediaWiki\quietCall( 'session_start' );
                        }
-                       $request = new FauxRequest( array(), false, $_SESSION );
+                       $request = new FauxRequest( array(), false, $session );
                        $request->setIP( $params['ip'] );
                        foreach ( $params['headers'] as $name => $value ) {
                                $request->setHeader( $name, $value );
index 5443eeb..f09de4f 100644 (file)
@@ -103,6 +103,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function pendingWriteCallers() {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function isOpen() {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
@@ -429,6 +433,10 @@ class DBConnRef implements IDatabase {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
 
+       public function doAtomicSection( $fname, $callback ) {
+               return $this->__call( __FUNCTION__, func_get_args() );
+       }
+
        public function begin( $fname = __METHOD__ ) {
                return $this->__call( __FUNCTION__, func_get_args() );
        }
index dea4a59..1835958 100644 (file)
@@ -141,6 +141,13 @@ abstract class DatabaseBase implements IDatabase {
         */
        private $mTrxAutomaticAtomic = false;
 
+       /**
+        * Track the write query callers of the current transaction
+        *
+        * @var string[]
+        */
+       private $mTrxWriteCallers = array();
+
        /**
         * Track the seconds spent in write queries for the current transaction
         *
@@ -148,6 +155,9 @@ abstract class DatabaseBase implements IDatabase {
         */
        private $mTrxWriteDuration = 0.0;
 
+       /** @var IDatabase|null Lazy handle to the master DB this server replicates from */
+       private $lazyMasterHandle;
+
        /**
         * @since 1.21
         * @var resource File handle for upgrade
@@ -163,13 +173,6 @@ abstract class DatabaseBase implements IDatabase {
        /** @var TransactionProfiler */
        protected $trxProfiler;
 
-       /**
-        * A string describing the current software version, and possibly
-        * other details in a user-friendly way. Will be listed on Special:Version, etc.
-        * Use getServerVersion() to get machine-friendly information.
-        *
-        * @return string Version information from the database server
-        */
        public function getServerInfo() {
                return $this->getServerVersion();
        }
@@ -194,27 +197,6 @@ abstract class DatabaseBase implements IDatabase {
                return wfSetBit( $this->mFlags, DBO_DEBUG, $debug );
        }
 
-       /**
-        * Turns buffering of SQL result sets on (true) or off (false). Default is
-        * "on".
-        *
-        * Unbuffered queries are very troublesome in MySQL:
-        *
-        *   - If another query is executed while the first query is being read
-        *     out, the first query is killed. This means you can't call normal
-        *     MediaWiki functions while you are reading an unbuffered query result
-        *     from a normal wfGetDB() connection.
-        *
-        *   - Unbuffered queries cause the MySQL server to use large amounts of
-        *     memory and to hold broad locks which block other queries.
-        *
-        * If you want to limit client-side memory, it's almost always better to
-        * split up queries into batches using a LIMIT clause than to switch off
-        * buffering.
-        *
-        * @param null|bool $buffer
-        * @return null|bool The previous value of the flag
-        */
        public function bufferResults( $buffer = null ) {
                if ( is_null( $buffer ) ) {
                        return !(bool)( $this->mFlags & DBO_NOBUFFER );
@@ -239,45 +221,18 @@ abstract class DatabaseBase implements IDatabase {
                return wfSetBit( $this->mFlags, DBO_IGNORE, $ignoreErrors );
        }
 
-       /**
-        * Gets the current transaction level.
-        *
-        * Historically, transactions were allowed to be "nested". This is no
-        * longer supported, so this function really only returns a boolean.
-        *
-        * @return int The previous value
-        */
        public function trxLevel() {
                return $this->mTrxLevel;
        }
 
-       /**
-        * Get the UNIX timestamp of the time that the transaction was established
-        *
-        * This can be used to reason about the staleness of SELECT data
-        * in REPEATABLE-READ transaction isolation level.
-        *
-        * @return float|null Returns null if there is not active transaction
-        * @since 1.25
-        */
        public function trxTimestamp() {
                return $this->mTrxLevel ? $this->mTrxTimestamp : null;
        }
 
-       /**
-        * Get/set the table prefix.
-        * @param string $prefix The table prefix to set, or omitted to leave it unchanged.
-        * @return string The previous table prefix.
-        */
        public function tablePrefix( $prefix = null ) {
                return wfSetVar( $this->mTablePrefix, $prefix );
        }
 
-       /**
-        * Get/set the db schema.
-        * @param string $schema The database schema to set, or omitted to leave it unchanged.
-        * @return string The previous db schema.
-        */
        public function dbSchema( $schema = null ) {
                return wfSetVar( $this->mSchema, $schema );
        }
@@ -291,15 +246,6 @@ abstract class DatabaseBase implements IDatabase {
                $this->fileHandle = $fh;
        }
 
-       /**
-        * Get properties passed down from the server info array of the load
-        * balancer.
-        *
-        * @param string $name The entry of the info array to get, or null to get the
-        *   whole array
-        *
-        * @return array|mixed|null
-        */
        public function getLBInfo( $name = null ) {
                if ( is_null( $name ) ) {
                        return $this->mLBInfo;
@@ -312,14 +258,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Set the LB info array, or a member of it. If called with one parameter,
-        * the LB info array is set to that parameter. If it is called with two
-        * parameters, the member with the given name is set to the given value.
-        *
-        * @param string $name
-        * @param array $value
-        */
        public function setLBInfo( $name, $value = null ) {
                if ( is_null( $value ) ) {
                        $this->mLBInfo = $name;
@@ -328,13 +266,42 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
+       /**
+        * Set a lazy-connecting DB handle to the master DB (for replication status purposes)
+        *
+        * @param IDatabase $conn
+        * @since 1.27
+        */
+       public function setLazyMasterHandle( IDatabase $conn ) {
+               $this->lazyMasterHandle = $conn;
+       }
+
+       /**
+        * @return IDatabase|null
+        * @see setLazyMasterHandle()
+        * @since 1.27
+        */
+       public function getLazyMasterHandle() {
+               return $this->lazyMasterHandle;
+       }
+
        /**
         * @return TransactionProfiler
         */
        protected function getTransactionProfiler() {
-               return $this->trxProfiler
-                       ? $this->trxProfiler
-                       : Profiler::instance()->getTransactionProfiler();
+               if ( !$this->trxProfiler ) {
+                       $this->trxProfiler = new TransactionProfiler();
+               }
+
+               return $this->trxProfiler;
+       }
+
+       /**
+        * @param TransactionProfiler $profiler
+        * @since 1.27
+        */
+       public function setTransactionProfiler( TransactionProfiler $profiler ) {
+               $this->trxProfiler = $profiler;
        }
 
        /**
@@ -374,21 +341,10 @@ abstract class DatabaseBase implements IDatabase {
                return false;
        }
 
-       /**
-        * Returns true if this database does an implicit sort when doing GROUP BY
-        *
-        * @return bool
-        */
        public function implicitGroupby() {
                return true;
        }
 
-       /**
-        * Returns true if this database does an implicit order by when the column has an index
-        * For example: SELECT page_title FROM page LIMIT 1
-        *
-        * @return bool
-        */
        public function implicitOrderby() {
                return true;
        }
@@ -412,132 +368,56 @@ abstract class DatabaseBase implements IDatabase {
                return false;
        }
 
-       /**
-        * Return the last query that went through DatabaseBase::query()
-        * @return string
-        */
        public function lastQuery() {
                return $this->mLastQuery;
        }
 
-       /**
-        * Returns true if the connection may have been used for write queries.
-        * Should return true if unsure.
-        *
-        * @return bool
-        */
        public function doneWrites() {
                return (bool)$this->mDoneWrites;
        }
 
-       /**
-        * Returns the last time the connection may have been used for write queries.
-        * Should return a timestamp if unsure.
-        *
-        * @return int|float UNIX timestamp or false
-        * @since 1.24
-        */
        public function lastDoneWrites() {
                return $this->mDoneWrites ?: false;
        }
 
-       /**
-        * @return bool Whether there is a transaction open with possible write queries
-        * @since 1.27
-        */
        public function writesPending() {
                return $this->mTrxLevel && $this->mTrxDoneWrites;
        }
 
-       /**
-        * Returns true if there is a transaction open with possible write
-        * queries or transaction pre-commit/idle callbacks waiting on it to finish.
-        *
-        * @return bool
-        */
        public function writesOrCallbacksPending() {
                return $this->mTrxLevel && (
                        $this->mTrxDoneWrites || $this->mTrxIdleCallbacks || $this->mTrxPreCommitCallbacks
                );
        }
 
-       /**
-        * Get the time spend running write queries for this
-        *
-        * High times could be due to scanning, updates, locking, and such
-        *
-        * @return float|bool Returns false if not transaction is active
-        * @since 1.26
-        */
        public function pendingWriteQueryDuration() {
                return $this->mTrxLevel ? $this->mTrxWriteDuration : false;
        }
 
-       /**
-        * Is a connection to the database open?
-        * @return bool
-        */
+       public function pendingWriteCallers() {
+               return $this->mTrxLevel ? $this->mTrxWriteCallers : array();
+       }
+
        public function isOpen() {
                return $this->mOpened;
        }
 
-       /**
-        * Set a flag for this connection
-        *
-        * @param int $flag DBO_* constants from Defines.php:
-        *   - DBO_DEBUG: output some debug info (same as debug())
-        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *   - DBO_TRX: automatically start transactions
-        *   - 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
-        */
        public function setFlag( $flag ) {
                $this->mFlags |= $flag;
        }
 
-       /**
-        * Clear a flag for this connection
-        *
-        * @param int $flag DBO_* constants from Defines.php:
-        *   - DBO_DEBUG: output some debug info (same as debug())
-        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *   - DBO_TRX: automatically start transactions
-        *   - 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
-        */
        public function clearFlag( $flag ) {
                $this->mFlags &= ~$flag;
        }
 
-       /**
-        * Returns a boolean whether the flag $flag is set for this connection
-        *
-        * @param int $flag DBO_* constants from Defines.php:
-        *   - DBO_DEBUG: output some debug info (same as debug())
-        *   - DBO_NOBUFFER: don't buffer results (inverse of bufferResults())
-        *   - DBO_TRX: automatically start transactions
-        *   - DBO_PERSISTENT: use persistant database connection
-        * @return bool
-        */
        public function getFlag( $flag ) {
                return !!( $this->mFlags & $flag );
        }
 
-       /**
-        * General read-only accessor
-        *
-        * @param string $name
-        * @return string
-        */
        public function getProperty( $name ) {
                return $this->$name;
        }
 
-       /**
-        * @return string
-        */
        public function getWikiID() {
                if ( $this->mTablePrefix ) {
                        return "{$this->mDBname}-{$this->mTablePrefix}";
@@ -808,13 +688,6 @@ abstract class DatabaseBase implements IDatabase {
                );
        }
 
-       /**
-        * Closes a database connection.
-        * if it is open : commits any open transactions
-        *
-        * @throws MWException
-        * @return bool Operation success. true if already closed.
-        */
        public function close() {
                if ( count( $this->mTrxIdleCallbacks ) ) { // sanity
                        throw new MWException( "Transaction idle callbacks still pending." );
@@ -857,10 +730,6 @@ abstract class DatabaseBase implements IDatabase {
         */
        abstract protected function closeConnection();
 
-       /**
-        * @param string $error Fallback error message, used if none is given by DB
-        * @throws DBConnectionError
-        */
        function reportConnectionError( $error = 'Unknown error' ) {
                $myError = $this->lastError();
                if ( $myError ) {
@@ -905,28 +774,6 @@ abstract class DatabaseBase implements IDatabase {
                return !in_array( $verb, array( 'BEGIN', 'COMMIT', 'ROLLBACK', 'SHOW', 'SET' ) );
        }
 
-       /**
-        * Run an SQL query and return the result. Normally throws a DBQueryError
-        * on failure. If errors are ignored, returns false instead.
-        *
-        * In new code, the query wrappers select(), insert(), update(), delete(),
-        * etc. should be used where possible, since they give much better DBMS
-        * independence and automatically quote or validate user input in a variety
-        * of contexts. This function is generally only useful for queries which are
-        * explicitly DBMS-dependent and are unsupported by the query wrappers, such
-        * as CREATE TABLE.
-        *
-        * However, the query wrappers themselves should call this function.
-        *
-        * @param string $sql SQL query
-        * @param string $fname Name of the calling function, for profiling/SHOW PROCESSLIST
-        *     comment (you can use __METHOD__ or add some extra info)
-        * @param bool $tempIgnore Whether to avoid throwing an exception on errors...
-        *     maybe best to catch the exception instead?
-        * @throws MWException
-        * @return bool|ResultWrapper True for a successful write query, ResultWrapper object
-        *     for a successful read query, or false on failure if $tempIgnore set
-        */
        public function query( $sql, $fname = __METHOD__, $tempIgnore = false ) {
                global $wgUser;
 
@@ -1055,22 +902,12 @@ abstract class DatabaseBase implements IDatabase {
 
                if ( $isWriteQuery && $this->mTrxLevel ) {
                        $this->mTrxWriteDuration += $queryRuntime;
+                       $this->mTrxWriteCallers[] = $fname;
                }
 
                return $res;
        }
 
-       /**
-        * Report a query error. Log the error, and if neither the object ignore
-        * flag nor the $tempIgnore flag is set, throw a DBQueryError.
-        *
-        * @param string $error
-        * @param int $errno
-        * @param string $sql
-        * @param string $fname
-        * @param bool $tempIgnore
-        * @throws DBQueryError
-        */
        public function reportQueryError( $error, $errno, $sql, $fname, $tempIgnore = false ) {
                if ( $this->ignoreErrors() || $tempIgnore ) {
                        wfDebug( "SQL ERROR (ignored): $error\n" );
@@ -1195,34 +1032,9 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Free a result object returned by query() or select(). It's usually not
-        * necessary to call this, just use unset() or let the variable holding
-        * the result object go out of scope.
-        *
-        * @param mixed $res A SQL result
-        */
        public function freeResult( $res ) {
        }
 
-       /**
-        * A SELECT wrapper which returns a single field from a single result row.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly
-        * ignored, returns false on failure.
-        *
-        * If no result rows are returned from the query, false is returned.
-        *
-        * @param string|array $table Table name. See DatabaseBase::select() for details.
-        * @param string $var The field name to select. This must be a valid SQL
-        *   fragment: do not use unvalidated user input.
-        * @param string|array $cond The condition array. See DatabaseBase::select() for details.
-        * @param string $fname The function name of the caller.
-        * @param string|array $options The query options. See DatabaseBase::select() for details.
-        *
-        * @return bool|mixed The value from the field, or false on failure.
-        * @throws DBUnexpectedError
-        */
        public function selectField(
                $table, $var, $cond = '', $fname = __METHOD__, $options = array()
        ) {
@@ -1250,27 +1062,8 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * A SELECT wrapper which returns a list of single field values from result rows.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly
-        * ignored, returns false on failure.
-        *
-        * If no result rows are returned from the query, false is returned.
-        *
-        * @param string|array $table Table name. See DatabaseBase::select() for details.
-        * @param string $var The field name to select. This must be a valid SQL
-        *   fragment: do not use unvalidated user input.
-        * @param string|array $cond The condition array. See DatabaseBase::select() for details.
-        * @param string $fname The function name of the caller.
-        * @param string|array $options The query options. See DatabaseBase::select() for details.
-        *
-        * @return bool|array The values from the field, or false on failure
-        * @throws DBUnexpectedError
-        * @since 1.25
-        */
        public function selectFieldValues(
-               $table, $var, $cond = '', $fname = __METHOD__, $options = array()
+               $table, $var, $cond = '', $fname = __METHOD__, $options = array(), $join_conds = array()
        ) {
                if ( $var === '*' ) { // sanity
                        throw new DBUnexpectedError( $this, "Cannot use a * field: got '$var'" );
@@ -1280,7 +1073,7 @@ abstract class DatabaseBase implements IDatabase {
                        $options = array( $options );
                }
 
-               $res = $this->select( $table, $var, $cond, $fname, $options );
+               $res = $this->select( $table, $var, $cond, $fname, $options, $join_conds );
                if ( $res === false ) {
                        return false;
                }
@@ -1424,147 +1217,6 @@ abstract class DatabaseBase implements IDatabase {
                return '';
        }
 
-       /**
-        * Execute a SELECT query constructed using the various parameters provided.
-        * See below for full details of the parameters.
-        *
-        * @param string|array $table Table name
-        * @param string|array $vars Field names
-        * @param string|array $conds Conditions
-        * @param string $fname Caller function name
-        * @param array $options Query options
-        * @param array $join_conds Join conditions
-        *
-        *
-        * @param string|array $table
-        *
-        * 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' )
-        *
-        * This includes the user table in the query, with the alias "a" available
-        * for use in field names (e.g. a.user_name).
-        *
-        * All of the table names given here are automatically run through
-        * DatabaseBase::tableName(), which causes the table prefix (if any) to be
-        * added, and various other table name mappings to be performed.
-        *
-        *
-        * @param string|array $vars
-        *
-        * May be either a field name or an array of field names. The field names
-        * 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)' )
-        *
-        * This includes an expression with the alias "maxrev" in the query.
-        *
-        * If an expression is given, care must be taken to ensure that it is
-        * DBMS-independent.
-        *
-        *
-        * @param string|array $conds
-        *
-        * May be either a string containing a single condition, or an array of
-        * conditions. If an array is given, the conditions constructed from each
-        * element are combined with AND.
-        *
-        * Array elements may take one of two forms:
-        *
-        *   - Elements with a numeric key are interpreted as raw SQL fragments.
-        *   - Elements with a string key are interpreted as equality conditions,
-        *     where the key is the field name.
-        *     - If the value of such an array element is a scalar (such as a
-        *       string), it will be treated as data and thus quoted appropriately.
-        *       If it is null, an IS NULL clause will be added.
-        *     - If the value is an array, an IN (...) clause will be constructed
-        *       from its non-null elements, and an IS NULL clause will be added
-        *       if null is present, such that the field may match any of the
-        *       elements in the array. The non-null elements will be quoted.
-        *
-        * Note that expressions are often DBMS-dependent in their syntax.
-        * DBMS-independent wrappers are provided for constructing several types of
-        * expression commonly used in condition queries. See:
-        *    - DatabaseBase::buildLike()
-        *    - DatabaseBase::conditional()
-        *
-        *
-        * @param string|array $options
-        *
-        * Optional: Array of query options. Boolean options are specified by
-        * including them in the array as a string value with a numeric key, for
-        * example:
-        *
-        *    array( 'FOR UPDATE' )
-        *
-        * The supported options are:
-        *
-        *   - OFFSET: Skip this many rows at the start of the result set. OFFSET
-        *     with LIMIT can theoretically be used for paging through a result set,
-        *     but this is discouraged in MediaWiki for performance reasons.
-        *
-        *   - LIMIT: Integer: return at most this many rows. The rows are sorted
-        *     and then the first rows are taken until the limit is reached. LIMIT
-        *     is applied to a result set after OFFSET.
-        *
-        *   - FOR UPDATE: Boolean: lock the returned rows so that they can't be
-        *     changed until the next COMMIT.
-        *
-        *   - DISTINCT: Boolean: return only unique result rows.
-        *
-        *   - GROUP BY: May be either an SQL fragment string naming a field or
-        *     expression to group by, or an array of such SQL fragments.
-        *
-        *   - HAVING: May be either an string containing a HAVING clause or an array of
-        *     conditions building the HAVING clause. If an array is given, the conditions
-        *     constructed from each element are combined with AND.
-        *
-        *   - ORDER BY: May be either an SQL fragment giving a field name or
-        *     expression to order by, or an array of such SQL fragments.
-        *
-        *   - USE INDEX: This may be either a string giving the index name to use
-        *     for the query, or an array. If it is an associative array, each key
-        *     gives the table name (or alias), each value gives the index name to
-        *     use for that table. All strings are SQL fragments and so should be
-        *     validated by the caller.
-        *
-        *   - EXPLAIN: In MySQL, this causes an EXPLAIN SELECT query to be run,
-        *     instead of SELECT.
-        *
-        * And also the following boolean MySQL extensions, see the MySQL manual
-        * for documentation:
-        *
-        *    - LOCK IN SHARE MODE
-        *    - STRAIGHT_JOIN
-        *    - HIGH_PRIORITY
-        *    - SQL_BIG_RESULT
-        *    - SQL_BUFFER_RESULT
-        *    - SQL_SMALL_RESULT
-        *    - SQL_CALC_FOUND_ROWS
-        *    - SQL_CACHE
-        *    - SQL_NO_CACHE
-        *
-        *
-        * @param string|array $join_conds
-        *
-        * Optional associative array of table-specific join conditions. In the
-        * most common case, this is unnecessary, since the join condition can be
-        * in $conds. However, it is useful for doing a LEFT JOIN.
-        *
-        * The key of the array contains the table name or alias. The value is an
-        * array with two elements, numbered 0 and 1. The first gives the type of
-        * join, the second is an SQL fragment giving the join condition for that
-        * table. For example:
-        *
-        *    array( 'page' => array( '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
-        *   DBQueryError exception will be thrown, except if the "ignore errors"
-        *   option was set, in which case false will be returned.
-        */
        public function select( $table, $vars, $conds = '', $fname = __METHOD__,
                $options = array(), $join_conds = array() ) {
                $sql = $this->selectSQLText( $table, $vars, $conds, $fname, $options, $join_conds );
@@ -1572,22 +1224,6 @@ abstract class DatabaseBase implements IDatabase {
                return $this->query( $sql, $fname );
        }
 
-       /**
-        * The equivalent of DatabaseBase::select() except that the constructed SQL
-        * is returned, instead of being immediately executed. This can be useful for
-        * doing UNION queries, where the SQL text of each query is needed. In general,
-        * however, callers outside of Database classes should just use select().
-        *
-        * @param string|array $table Table name
-        * @param string|array $vars Field names
-        * @param string|array $conds Conditions
-        * @param string $fname Caller function name
-        * @param string|array $options Query options
-        * @param string|array $join_conds Join conditions
-        *
-        * @return string SQL query string.
-        * @see DatabaseBase::select()
-        */
        public function selectSQLText( $table, $vars, $conds = '', $fname = __METHOD__,
                $options = array(), $join_conds = array()
        ) {
@@ -1639,20 +1275,6 @@ abstract class DatabaseBase implements IDatabase {
                return $sql;
        }
 
-       /**
-        * Single row SELECT wrapper. Equivalent to DatabaseBase::select(), except
-        * that a single row object is returned. If the query returns no rows,
-        * false is returned.
-        *
-        * @param string|array $table Table name
-        * @param string|array $vars Field names
-        * @param array $conds Conditions
-        * @param string $fname Caller function name
-        * @param string|array $options Query options
-        * @param array|string $join_conds Join conditions
-        *
-        * @return stdClass|bool
-        */
        public function selectRow( $table, $vars, $conds, $fname = __METHOD__,
                $options = array(), $join_conds = array()
        ) {
@@ -1673,26 +1295,6 @@ abstract class DatabaseBase implements IDatabase {
                return $obj;
        }
 
-       /**
-        * Estimate the number of rows in dataset
-        *
-        * MySQL allows you to estimate the number of rows that would be returned
-        * by a SELECT query, using EXPLAIN SELECT. The estimate is provided using
-        * index cardinality statistics, and is notoriously inaccurate, especially
-        * when large numbers of rows have recently been added or deleted.
-        *
-        * For DBMSs that don't support fast result size estimation, this function
-        * will actually perform the SELECT COUNT(*).
-        *
-        * Takes the same arguments as DatabaseBase::select().
-        *
-        * @param string $table Table name
-        * @param string $vars Unused
-        * @param array|string $conds Filters on the table
-        * @param string $fname Function name for profiling
-        * @param array $options Options for select
-        * @return int Row count
-        */
        public function estimateRowCount(
                $table, $vars = '*', $conds = '', $fname = __METHOD__, $options = array()
        ) {
@@ -1707,23 +1309,6 @@ abstract class DatabaseBase implements IDatabase {
                return $rows;
        }
 
-       /**
-        * Get the number of rows in dataset
-        *
-        * This is useful when trying to do COUNT(*) but with a LIMIT for performance.
-        *
-        * Takes the same arguments as DatabaseBase::select().
-        *
-        * @since 1.27 Added $join_conds parameter
-        *
-        * @param array|string $tables Table names
-        * @param string $vars Unused
-        * @param array|string $conds Filters on the table
-        * @param string $fname Function name for profiling
-        * @param array $options Options for select
-        * @param array $join_conds Join conditions (since 1.27)
-        * @return int Row count
-        */
        public function selectRowCount(
                $tables, $vars = '*', $conds = '', $fname = __METHOD__, $options = array(), $join_conds = array()
        ) {
@@ -1769,30 +1354,12 @@ abstract class DatabaseBase implements IDatabase {
                return $sql;
        }
 
-       /**
-        * Determines whether a field exists in a table
-        *
-        * @param string $table Table name
-        * @param string $field Filed to check on that table
-        * @param string $fname Calling function name (optional)
-        * @return bool Whether $table has filed $field
-        */
        public function fieldExists( $table, $field, $fname = __METHOD__ ) {
                $info = $this->fieldInfo( $table, $field );
 
                return (bool)$info;
        }
 
-       /**
-        * Determines whether an index exists
-        * Usually throws a DBQueryError on failure
-        * If errors are explicitly ignored, returns NULL on failure
-        *
-        * @param string $table
-        * @param string $index
-        * @param string $fname
-        * @return bool|null
-        */
        public function indexExists( $table, $index, $fname = __METHOD__ ) {
                if ( !$this->tableExists( $table ) ) {
                        return null;
@@ -1806,13 +1373,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Query whether a given table exists
-        *
-        * @param string $table
-        * @param string $fname
-        * @return bool
-        */
        public function tableExists( $table, $fname = __METHOD__ ) {
                $table = $this->tableName( $table );
                $old = $this->ignoreErrors( true );
@@ -1822,14 +1382,6 @@ abstract class DatabaseBase implements IDatabase {
                return (bool)$res;
        }
 
-       /**
-        * Determines if a given index is unique
-        *
-        * @param string $table
-        * @param string $index
-        *
-        * @return bool
-        */
        public function indexUnique( $table, $index ) {
                $indexInfo = $this->indexInfo( $table, $index );
 
@@ -1850,39 +1402,6 @@ abstract class DatabaseBase implements IDatabase {
                return implode( ' ', $options );
        }
 
-       /**
-        * INSERT wrapper, inserts an array into a table.
-        *
-        * $a may be either:
-        *
-        *   - A single associative array. The array keys are the field names, and
-        *     the values are the values to insert. The values are treated as data
-        *     and will be quoted appropriately. If NULL is inserted, this will be
-        *     converted to a database NULL.
-        *   - An array with numeric keys, holding a list of associative arrays.
-        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
-        *     each subarray must be identical to each other, and in the same order.
-        *
-        * $options is an array of options, with boolean options encoded as values
-        * with numeric keys, in the same style as $options in
-        * DatabaseBase::select(). Supported options are:
-        *
-        *   - IGNORE: Boolean: if present, duplicate key errors are ignored, and
-        *     any rows which cause duplicate key errors are not inserted. It's
-        *     possible to determine how many rows were successfully inserted using
-        *     DatabaseBase::affectedRows().
-        *
-        * @param string $table Table name. This will be passed through
-        *   DatabaseBase::tableName().
-        * @param array $a Array of rows to insert
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @param array $options Array of options
-        *
-        * @throws DBQueryError Usually throws a DBQueryError on failure. If errors are explicitly ignored,
-        * returns success.
-        *
-        * @return bool
-        */
        public function insert( $table, $a, $fname = __METHOD__, $options = array() ) {
                # No rows to insert, easy just return now
                if ( !count( $a ) ) {
@@ -1971,24 +1490,6 @@ abstract class DatabaseBase implements IDatabase {
                return implode( ' ', $opts );
        }
 
-       /**
-        * UPDATE wrapper. Takes a condition array and a SET array.
-        *
-        * @param string $table Name of the table to UPDATE. This will be passed through
-        *   DatabaseBase::tableName().
-        * @param array $values An array of values to SET. For each array element,
-        *   the key gives the field name, and the value gives the data to set
-        *   that field to. The data will be quoted by DatabaseBase::addQuotes().
-        * @param array $conds An array of conditions (WHERE). See
-        *   DatabaseBase::select() for the details of the format of condition
-        *   arrays. Use '*' to update all rows.
-        * @param string $fname The function name of the caller (from __METHOD__),
-        *   for logging and profiling.
-        * @param array $options An array of UPDATE options, can be:
-        *   - IGNORE: Ignore unique key conflicts
-        *   - LOW_PRIORITY: MySQL-specific, see MySQL manual.
-        * @return bool
-        */
        function update( $table, $values, $conds, $fname = __METHOD__, $options = array() ) {
                $table = $this->tableName( $table );
                $opts = $this->makeUpdateOptions( $options );
@@ -2001,20 +1502,6 @@ abstract class DatabaseBase implements IDatabase {
                return $this->query( $sql, $fname );
        }
 
-       /**
-        * Makes an encoded list of strings from an array
-        *
-        * @param array $a Containing the data
-        * @param int $mode Constant
-        *    - LIST_COMMA: Comma separated, no field names
-        *    - LIST_AND:   ANDed WHERE clause (without the WHERE). See the
-        *      documentation for $conds in DatabaseBase::select().
-        *    - LIST_OR:    ORed WHERE clause (without the WHERE)
-        *    - LIST_SET:   Comma separated with field names, like a SET clause
-        *    - LIST_NAMES: Comma separated field names
-        * @throws MWException|DBUnexpectedError
-        * @return string
-        */
        public function makeList( $a, $mode = LIST_COMMA ) {
                if ( !is_array( $a ) ) {
                        throw new DBUnexpectedError( $this, 'DatabaseBase::makeList called with incorrect parameters' );
@@ -2090,16 +1577,6 @@ abstract class DatabaseBase implements IDatabase {
                return $list;
        }
 
-       /**
-        * Build a partial where clause from a 2-d array such as used for LinkBatch.
-        * The keys on each level may be either integers or strings.
-        *
-        * @param array $data Organized as 2-d
-        *    array(baseKeyVal => array(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
-        */
        public function makeWhereFrom2d( $data, $baseKey, $subKey ) {
                $conds = array();
 
@@ -2129,60 +1606,24 @@ abstract class DatabaseBase implements IDatabase {
         */
        public function aggregateValue( $valuedata, $valuename = 'value' ) {
                return $valuename;
-       }
-
-       /**
-        * @param string $field
-        * @return string
-        */
+       }
+
        public function bitNot( $field ) {
                return "(~$field)";
        }
 
-       /**
-        * @param string $fieldLeft
-        * @param string $fieldRight
-        * @return string
-        */
        public function bitAnd( $fieldLeft, $fieldRight ) {
                return "($fieldLeft & $fieldRight)";
        }
 
-       /**
-        * @param string $fieldLeft
-        * @param string $fieldRight
-        * @return string
-        */
        public function bitOr( $fieldLeft, $fieldRight ) {
                return "($fieldLeft | $fieldRight)";
        }
 
-       /**
-        * Build a concatenation list to feed into a SQL query
-        * @param array $stringList List of raw SQL expressions; caller is
-        *   responsible for any quoting
-        * @return string
-        */
        public function buildConcat( $stringList ) {
                return 'CONCAT(' . implode( ',', $stringList ) . ')';
        }
 
-       /**
-        * Build a GROUP_CONCAT or equivalent statement for a query.
-        *
-        * This is useful for combining a field for several rows into a single string.
-        * NULL values will not appear in the output, duplicated values will appear,
-        * and the resulting delimiter-separated values have no defined sort order.
-        * Code using the results may need to use the PHP unique() or sort() methods.
-        *
-        * @param string $delim Glue to bind the results together
-        * @param string|array $table Table name
-        * @param string $field Field name
-        * @param string|array $conds Conditions
-        * @param string|array $join_conds Join conditions
-        * @return string SQL text
-        * @since 1.23
-        */
        public function buildGroupConcatField(
                $delim, $table, $field, $conds = '', $join_conds = array()
        ) {
@@ -2191,15 +1632,6 @@ abstract class DatabaseBase implements IDatabase {
                return '(' . $this->selectSQLText( $table, $fld, $conds, null, array(), $join_conds ) . ')';
        }
 
-       /**
-        * Change the current database
-        *
-        * @todo Explain what exactly will fail if this is not overridden.
-        *
-        * @param string $db
-        *
-        * @return bool Success or failure
-        */
        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
@@ -2209,18 +1641,10 @@ abstract class DatabaseBase implements IDatabase {
                return true;
        }
 
-       /**
-        * Get the current DB name
-        * @return string
-        */
        public function getDBname() {
                return $this->mDBname;
        }
 
-       /**
-        * Get the server hostname or IP address
-        * @return string
-        */
        public function getServer() {
                return $this->mServer;
        }
@@ -2520,12 +1944,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Adds quotes and backslashes.
-        *
-        * @param string|Blob $s
-        * @return string
-        */
        public function addQuotes( $s ) {
                if ( $s instanceof Blob ) {
                        $s = $s->fetch();
@@ -2573,22 +1991,6 @@ abstract class DatabaseBase implements IDatabase {
                return addcslashes( $s, '\%_' );
        }
 
-       /**
-        * LIKE statement wrapper, receives a variable-length argument list with
-        * parts of pattern to match containing either string literals that will be
-        * escaped or tokens returned by anyChar() or anyString(). Alternatively,
-        * the function could be provided with an array of aforementioned
-        * parameters.
-        *
-        * 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() );
-        *   $query .= $dbr->buildLike( $pattern );
-        *
-        * @since 1.16
-        * @return string Fully built LIKE statement
-        */
        public function buildLike() {
                $params = func_get_args();
 
@@ -2609,35 +2011,14 @@ abstract class DatabaseBase implements IDatabase {
                return " LIKE {$this->addQuotes( $s )} ";
        }
 
-       /**
-        * Returns a token for buildLike() that denotes a '_' to be used in a LIKE query
-        *
-        * @return LikeMatch
-        */
        public function anyChar() {
                return new LikeMatch( '_' );
        }
 
-       /**
-        * Returns a token for buildLike() that denotes a '%' to be used in a LIKE query
-        *
-        * @return LikeMatch
-        */
        public function anyString() {
                return new LikeMatch( '%' );
        }
 
-       /**
-        * Returns an appropriately quoted sequence value for inserting a new row.
-        * MySQL has autoincrement fields, so this is just NULL. But the PostgreSQL
-        * subclass will return an integer, and save the value for insertId()
-        *
-        * Any implementation of this function should *not* involve reusing
-        * sequence numbers created for rolled-back transactions.
-        * See http://bugs.mysql.com/bug.php?id=30767 for details.
-        * @param string $seqName
-        * @return null|int
-        */
        public function nextSequenceValue( $seqName ) {
                return null;
        }
@@ -2656,28 +2037,6 @@ abstract class DatabaseBase implements IDatabase {
                return '';
        }
 
-       /**
-        * REPLACE query wrapper.
-        *
-        * REPLACE is a very handy MySQL extension, which functions like an INSERT
-        * except that when there is a duplicate key error, the old row is deleted
-        * and the new row is inserted in its place.
-        *
-        * We simulate this with standard SQL with a DELETE followed by INSERT. To
-        * perform the delete, we need to know what the unique indexes are so that
-        * we know how to find the conflicting rows.
-        *
-        * It may be more efficient to leave off unique indexes which are unlikely
-        * to collide. However if you do this, you run the risk of encountering
-        * errors which wouldn't have occurred in MySQL.
-        *
-        * @param string $table The table to replace the row(s) in.
-        * @param array $uniqueIndexes Is an array of indexes. Each element may be either
-        *    a field name or an array of field names
-        * @param array $rows Can be either a single row to insert, or multiple rows,
-        *    in the same format as for DatabaseBase::insert()
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        */
        public function replace( $table, $uniqueIndexes, $rows, $fname = __METHOD__ ) {
                $quotedTable = $this->tableName( $table );
 
@@ -2760,40 +2119,6 @@ abstract class DatabaseBase implements IDatabase {
                return $this->query( $sql, $fname );
        }
 
-       /**
-        * INSERT ON DUPLICATE KEY UPDATE wrapper, upserts an array into a table.
-        *
-        * This updates any conflicting rows (according to the unique indexes) using
-        * the provided SET clause and inserts any remaining (non-conflicted) rows.
-        *
-        * $rows may be either:
-        *   - A single associative array. The array keys are the field names, and
-        *     the values are the values to insert. The values are treated as data
-        *     and will be quoted appropriately. If NULL is inserted, this will be
-        *     converted to a database NULL.
-        *   - An array with numeric keys, holding a list of associative arrays.
-        *     This causes a multi-row INSERT on DBMSs that support it. The keys in
-        *     each subarray must be identical to each other, and in the same order.
-        *
-        * It may be more efficient to leave off unique indexes which are unlikely
-        * to collide. However if you do this, you run the risk of encountering
-        * errors which wouldn't have occurred in MySQL.
-        *
-        * Usually throws a DBQueryError on failure. If errors are explicitly ignored,
-        * returns success.
-        *
-        * @since 1.22
-        *
-        * @param string $table Table name. This will be passed through DatabaseBase::tableName().
-        * @param array $rows A single row or list of rows to insert
-        * @param array $uniqueIndexes List of single field names or field name tuples
-        * @param array $set An array of values to SET. For each array element, the
-        *   key gives the field name, and the value gives the data to set that
-        *   field to. The data will be quoted by DatabaseBase::addQuotes().
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @throws Exception
-        * @return bool
-        */
        public function upsert( $table, array $rows, array $uniqueIndexes, array $set,
                $fname = __METHOD__
        ) {
@@ -2848,26 +2173,6 @@ abstract class DatabaseBase implements IDatabase {
                return $ok;
        }
 
-       /**
-        * DELETE where the condition is a join.
-        *
-        * MySQL overrides this to use a multi-table DELETE syntax, in other databases
-        * we use sub-selects
-        *
-        * For safety, an empty $conds will not delete everything. If you want to
-        * delete all rows where the join condition matches, set $conds='*'.
-        *
-        * DO NOT put the join condition in $conds.
-        *
-        * @param string $delTable The table to delete from.
-        * @param string $joinTable The other table.
-        * @param string $delVar The variable to join on, in the first table.
-        * @param string $joinVar The variable to join on, in the second table.
-        * @param array $conds Condition array of field names mapped to variables,
-        *   ANDed together in the WHERE clause
-        * @param string $fname Calling function name (use __METHOD__) for logs/profiling
-        * @throws DBUnexpectedError
-        */
        public function deleteJoin( $delTable, $joinTable, $delVar, $joinVar, $conds,
                $fname = __METHOD__
        ) {
@@ -2923,16 +2228,6 @@ abstract class DatabaseBase implements IDatabase {
                return '';
        }
 
-       /**
-        * DELETE query wrapper.
-        *
-        * @param array $table Table name
-        * @param string|array $conds Array of conditions. See $conds in DatabaseBase::select()
-        *   for the format. Use $conds == "*" to delete all rows
-        * @param string $fname Name of the calling function
-        * @throws DBUnexpectedError
-        * @return bool|ResultWrapper
-        */
        public function delete( $table, $conds, $fname = __METHOD__ ) {
                if ( !$conds ) {
                        throw new DBUnexpectedError( $this, 'DatabaseBase::delete() called with no conditions' );
@@ -2951,32 +2246,6 @@ abstract class DatabaseBase implements IDatabase {
                return $this->query( $sql, $fname );
        }
 
-       /**
-        * INSERT SELECT wrapper. Takes data from a SELECT query and inserts it
-        * into another table.
-        *
-        * @param string $destTable The table name to insert into
-        * @param string|array $srcTable May be either a table name, or an array of table names
-        *    to include in a join.
-        *
-        * @param array $varMap Must be an associative array of the form
-        *    array( 'dest1' => 'source1', ...). Source items may be literals
-        *    rather than field names, but strings should be quoted with
-        *    DatabaseBase::addQuotes()
-        *
-        * @param array $conds Condition array. See $conds in DatabaseBase::select() for
-        *    the details of the format of condition arrays. May be "*" to copy the
-        *    whole table.
-        *
-        * @param string $fname The function name of the caller, from __METHOD__
-        *
-        * @param array $insertOptions Options for the INSERT part of the query, see
-        *    DatabaseBase::insert() for details.
-        * @param array $selectOptions Options for the SELECT part of the query, see
-        *    DatabaseBase::select() for details.
-        *
-        * @return ResultWrapper
-        */
        public function insertSelect( $destTable, $srcTable, $varMap, $conds,
                $fname = __METHOD__,
                $insertOptions = array(), $selectOptions = array()
@@ -3046,38 +2315,16 @@ abstract class DatabaseBase implements IDatabase {
                        . "{$limit} ";
        }
 
-       /**
-        * Returns true if current database backend supports ORDER BY or LIMIT for separate subqueries
-        * within the UNION construct.
-        * @return bool
-        */
        public function unionSupportsOrderAndLimit() {
                return true; // True for almost every DB supported
        }
 
-       /**
-        * Construct a UNION query
-        * This is used for providing overload point for other DB abstractions
-        * not compatible with the MySQL syntax.
-        * @param array $sqls SQL statements to combine
-        * @param bool $all Use UNION ALL
-        * @return string SQL fragment
-        */
        public function unionQueries( $sqls, $all ) {
                $glue = $all ? ') UNION ALL (' : ') UNION (';
 
                return '(' . implode( $glue, $sqls ) . ')';
        }
 
-       /**
-        * Returns an SQL expression for a simple conditional. This doesn't need
-        * to be overridden unless CASE isn't supported in your DBMS.
-        *
-        * @param string|array $cond SQL expression which will result in a boolean value
-        * @param string $trueVal SQL expression to return if true
-        * @param string $falseVal SQL expression to return if false
-        * @return string SQL fragment
-        */
        public function conditional( $cond, $trueVal, $falseVal ) {
                if ( is_array( $cond ) ) {
                        $cond = $this->makeList( $cond, LIST_AND );
@@ -3086,67 +2333,26 @@ abstract class DatabaseBase implements IDatabase {
                return " (CASE WHEN $cond THEN $trueVal ELSE $falseVal END) ";
        }
 
-       /**
-        * Returns a comand for str_replace function in SQL query.
-        * Uses REPLACE() in MySQL
-        *
-        * @param string $orig Column to modify
-        * @param string $old Column to seek
-        * @param string $new Column to replace with
-        *
-        * @return string
-        */
        public function strreplace( $orig, $old, $new ) {
                return "REPLACE({$orig}, {$old}, {$new})";
        }
 
-       /**
-        * Determines how long the server has been up
-        * STUB
-        *
-        * @return int
-        */
        public function getServerUptime() {
                return 0;
        }
 
-       /**
-        * Determines if the last failure was due to a deadlock
-        * STUB
-        *
-        * @return bool
-        */
        public function wasDeadlock() {
                return false;
        }
 
-       /**
-        * Determines if the last failure was due to a lock timeout
-        * STUB
-        *
-        * @return bool
-        */
        public function wasLockTimeout() {
                return false;
        }
 
-       /**
-        * Determines if the last query error was something that should be dealt
-        * with by pinging the connection and reissuing the query.
-        * STUB
-        *
-        * @return bool
-        */
        public function wasErrorReissuable() {
                return false;
        }
 
-       /**
-        * Determines if the last failure was due to the database being read-only.
-        * STUB
-        *
-        * @return bool
-        */
        public function wasReadOnlyError() {
                return false;
        }
@@ -3177,9 +2383,9 @@ abstract class DatabaseBase implements IDatabase {
         * 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 DBQueryError
+        * @throws DBUnexpectedError
+        * @throws Exception
         */
        public function deadlockLoop() {
                $args = func_get_args();
@@ -3189,6 +2395,7 @@ abstract class DatabaseBase implements IDatabase {
                $this->begin( __METHOD__ );
 
                $retVal = null;
+               /** @var Exception $e */
                $e = null;
                do {
                        try {
@@ -3216,55 +2423,21 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Wait for the slave 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 Zero if the slave was past that position already,
-        *   greater than zero if we waited for some period of time, less than
-        *   zero if we timed out.
-        */
        public function masterPosWait( DBMasterPos $pos, $timeout ) {
                # Real waits are implemented in the subclass.
                return 0;
        }
 
-       /**
-        * Get the replication position of this slave
-        *
-        * @return DBMasterPos|bool False if this is not a slave.
-        */
        public function getSlavePos() {
                # Stub
                return false;
        }
 
-       /**
-        * Get the position of this master
-        *
-        * @return DBMasterPos|bool False if this is not a master
-        */
        public function getMasterPos() {
                # Stub
                return false;
        }
 
-       /**
-        * Run an anonymous function 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.
-        *
-        * This is useful for updates to different systems or when separate transactions are needed.
-        * For example, one might want to enqueue jobs into a system outside the database, but only
-        * after the database is updated so that the jobs will see the data when they actually run.
-        * It can also be used for updates that easily cause deadlocks if locks are held too long.
-        *
-        * @param callable $callback
-        * @since 1.20
-        */
        final public function onTransactionIdle( $callback ) {
                $this->mTrxIdleCallbacks[] = array( $callback, wfGetCaller() );
                if ( !$this->mTrxLevel ) {
@@ -3272,17 +2445,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Run an anonymous function 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.
-        *
-        * 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.
-        *
-        * @param callable $callback
-        * @since 1.22
-        */
        final public function onTransactionPreCommitOrIdle( $callback ) {
                if ( $this->mTrxLevel ) {
                        $this->mTrxPreCommitCallbacks[] = array( $callback, wfGetCaller() );
@@ -3360,30 +2522,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Begin an atomic section of statements
-        *
-        * If a transaction has been started already, just keep track of the given
-        * section name to make sure the transaction is not committed pre-maturely.
-        * This function can be used in layers (with sub-sections), so use a stack
-        * to keep track of the different atomic sections. If there is no transaction,
-        * start one implicitly.
-        *
-        * The goal of this function is to create an atomic section of SQL queries
-        * without having to start a new transaction if it already exists.
-        *
-        * Atomic sections are more strict than transactions. With transactions,
-        * attempting to begin a new transaction when one is already running results
-        * in MediaWiki issuing a brief warning and doing an implicit commit. All
-        * atomic levels *must* be explicitly closed using DatabaseBase::endAtomic(),
-        * and any database transactions cannot be began or committed until all atomic
-        * levels are closed. There is no such thing as implicitly opening or closing
-        * an atomic section.
-        *
-        * @since 1.23
-        * @param string $fname
-        * @throws DBError
-        */
        final public function startAtomic( $fname = __METHOD__ ) {
                if ( !$this->mTrxLevel ) {
                        $this->begin( $fname );
@@ -3398,17 +2536,6 @@ abstract class DatabaseBase implements IDatabase {
                $this->mTrxAtomicLevels[] = $fname;
        }
 
-       /**
-        * Ends an atomic section of SQL statements
-        *
-        * Ends the next section of atomic SQL statements and commits the transaction
-        * if necessary.
-        *
-        * @since 1.23
-        * @see DatabaseBase::startAtomic
-        * @param string $fname
-        * @throws DBError
-        */
        final public function endAtomic( $fname = __METHOD__ ) {
                if ( !$this->mTrxLevel ) {
                        throw new DBUnexpectedError( $this, 'No atomic transaction is open.' );
@@ -3424,21 +2551,21 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Begin a transaction. If a transaction is already in progress,
-        * that transaction will be committed before the new transaction is started.
-        *
-        * 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.
-        *
-        * Nesting of transactions is not supported. Attempts to nest transactions
-        * will cause a warning, unless the current transaction was started
-        * automatically because of the DBO_TRX flag.
-        *
-        * @param string $fname
-        * @throws DBError
-        */
+       final public function doAtomicSection( $fname, $callback ) {
+               if ( !is_callable( $callback ) ) {
+                       throw new UnexpectedValueException( "Invalid callback." );
+               };
+
+               $this->startAtomic( $fname );
+               try {
+                       call_user_func_array( $callback, array( $this, $fname ) );
+               } catch ( Exception $e ) {
+                       $this->rollback( $fname );
+                       throw $e;
+               }
+               $this->endAtomic( $fname );
+       }
+
        final public function begin( $fname = __METHOD__ ) {
                if ( $this->mTrxLevel ) { // implicit commit
                        if ( $this->mTrxAtomicLevels ) {
@@ -3495,6 +2622,7 @@ abstract class DatabaseBase implements IDatabase {
                $this->mTrxPreCommitCallbacks = array();
                $this->mTrxShortId = wfRandomString( 12 );
                $this->mTrxWriteDuration = 0.0;
+               $this->mTrxWriteCallers = array();
                // First SELECT after BEGIN will establish the snapshot in REPEATABLE-READ.
                // Get an estimate of the slave lag before then, treating estimate staleness
                // as lag itself just to be safe
@@ -3513,20 +2641,6 @@ abstract class DatabaseBase implements IDatabase {
                $this->mTrxLevel = 1;
        }
 
-       /**
-        * Commits a transaction previously started using begin().
-        * If no transaction is in progress, a warning is issued.
-        *
-        * 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. 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
-        */
        final public function commit( $fname = __METHOD__, $flush = '' ) {
                if ( $this->mTrxLevel && $this->mTrxAtomicLevels ) {
                        // There are still atomic sections open. This cannot be ignored
@@ -3579,33 +2693,15 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Rollback a transaction previously started using begin().
-        * If no transaction is in progress, a warning is issued.
-        *
-        * No-op on non-transactional databases.
-        *
-        * @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.
-        * @throws DBUnexpectedError
-        * @since 1.23 Added $flush parameter
-        */
        final public function rollback( $fname = __METHOD__, $flush = '' ) {
                if ( $flush !== 'flush' ) {
                        if ( !$this->mTrxLevel ) {
                                wfWarn( "$fname: No transaction to rollback, something got out of sync!" );
                                return; // nothing to do
-                       } elseif ( $this->mTrxAutomatic ) {
-                               wfWarn( "$fname: Explicit rollback of implicit transaction. Something may be out of sync!" );
                        }
                } else {
                        if ( !$this->mTrxLevel ) {
                                return; // nothing to do
-                       } elseif ( !$this->mTrxAutomatic ) {
-                               wfWarn( "$fname: Flushing an explicit transaction, getting out of sync!" );
                        }
                }
 
@@ -3657,14 +2753,6 @@ abstract class DatabaseBase implements IDatabase {
                        'DatabaseBase::duplicateTableStructure is not implemented in descendant class' );
        }
 
-       /**
-        * List all tables on the database
-        *
-        * @param string $prefix Only show tables with this prefix, e.g. mw_
-        * @param string $fname Calling function name
-        * @throws MWException
-        * @return array
-        */
        function listTables( $prefix = null, $fname = __METHOD__ ) {
                throw new MWException( 'DatabaseBase::listTables is not implemented in descendant class' );
        }
@@ -3705,34 +2793,10 @@ abstract class DatabaseBase implements IDatabase {
                throw new MWException( 'DatabaseBase::isView is not implemented in descendant class' );
        }
 
-       /**
-        * Convert a timestamp in one of the formats accepted by wfTimestamp()
-        * to the format used for inserting into timestamp fields in this DBMS.
-        *
-        * The result is unquoted, and needs to be passed through addQuotes()
-        * before it can be included in raw SQL.
-        *
-        * @param string|int $ts
-        *
-        * @return string
-        */
        public function timestamp( $ts = 0 ) {
                return wfTimestamp( TS_MW, $ts );
        }
 
-       /**
-        * Convert a timestamp in one of the formats accepted by wfTimestamp()
-        * to the format used for inserting into timestamp fields in this DBMS. If
-        * NULL is input, it is passed through, allowing NULL values to be inserted
-        * into timestamp fields.
-        *
-        * The result is unquoted, and needs to be passed through addQuotes()
-        * before it can be included in raw SQL.
-        *
-        * @param string|int $ts
-        *
-        * @return string
-        */
        public function timestampOrNull( $ts = null ) {
                if ( is_null( $ts ) ) {
                        return null;
@@ -3767,11 +2831,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Ping the server and try to reconnect if it there is no connection
-        *
-        * @return bool Success or failure
-        */
        public function ping() {
                # Stub. Not essential to override.
                return true;
@@ -3850,36 +2909,14 @@ abstract class DatabaseBase implements IDatabase {
                return 0;
        }
 
-       /**
-        * Return the maximum number of items allowed in a list, or 0 for unlimited.
-        *
-        * @return int
-        */
        function maxListLen() {
                return 0;
        }
 
-       /**
-        * Some DBMSs have a special format for inserting into blob fields, they
-        * don't allow simple quoted strings to be inserted. To insert into such
-        * a field, pass the data through this function before passing it to
-        * DatabaseBase::insert().
-        *
-        * @param string $b
-        * @return string
-        */
        public function encodeBlob( $b ) {
                return $b;
        }
 
-       /**
-        * Some DBMSs return a special placeholder object representing blob fields
-        * in result objects. Pass the object through this function to return the
-        * original string.
-        *
-        * @param string|Blob $b
-        * @return string
-        */
        public function decodeBlob( $b ) {
                if ( $b instanceof Blob ) {
                        $b = $b->fetch();
@@ -3887,16 +2924,6 @@ abstract class DatabaseBase implements IDatabase {
                return $b;
        }
 
-       /**
-        * Override database's default behavior. $options include:
-        *     'connTimeout' : Set the connection timeout value in seconds.
-        *                     May be useful for very long batch queries such as
-        *                     full-wiki dumps, where a single query reads out over
-        *                     hours or days.
-        *
-        * @param array $options
-        * @return void
-        */
        public function setSessionOptions( array $options ) {
        }
 
@@ -3962,13 +2989,6 @@ abstract class DatabaseBase implements IDatabase {
                }
        }
 
-       /**
-        * Set variables to be used in sourceFile/sourceStream, in preference to the
-        * ones in $GLOBALS. If an array is set here, $GLOBALS will not be used at
-        * all. If it's set to false, $GLOBALS will be used.
-        *
-        * @param bool|array $vars Mapping variable name to value.
-        */
        public function setSchemaVars( $vars ) {
                $this->mSchemaVars = $vars;
        }
@@ -4135,54 +3155,18 @@ abstract class DatabaseBase implements IDatabase {
                return array();
        }
 
-       /**
-        * Check to see if a named lock is available (non-blocking)
-        *
-        * @param string $lockName Name of lock to poll
-        * @param string $method Name of method calling us
-        * @return bool
-        * @since 1.20
-        */
        public function lockIsFree( $lockName, $method ) {
                return true;
        }
 
-       /**
-        * Acquire a named lock
-        *
-        * Named locks are not related to transactions
-        *
-        * @param string $lockName Name of lock to aquire
-        * @param string $method Name of method calling us
-        * @param int $timeout
-        * @return bool
-        */
        public function lock( $lockName, $method, $timeout = 5 ) {
                return true;
        }
 
-       /**
-        * Release a lock
-        *
-        * Named locks are not related to transactions
-        *
-        * @param string $lockName Name of lock to release
-        * @param string $method Name of method calling us
-        *
-        * @return int Returns 1 if the lock was released, 0 if the lock was not established
-        * by this thread (in which case the lock is not released), and NULL if the named
-        * lock did not exist
-        */
        public function unlock( $lockName, $method ) {
                return true;
        }
 
-       /**
-        * Check to see if a named lock used by lock() use blocking queues
-        *
-        * @return bool
-        * @since 1.26
-        */
        public function namedLocksEnqueue() {
                return false;
        }
@@ -4239,51 +3223,22 @@ abstract class DatabaseBase implements IDatabase {
                return 'SearchEngineDummy';
        }
 
-       /**
-        * Find out when 'infinity' is. Most DBMSes support this. This is a special
-        * keyword for timestamps in PostgreSQL, and works with CHAR(14) as well
-        * because "i" sorts after all numbers.
-        *
-        * @return string
-        */
        public function getInfinity() {
                return 'infinity';
        }
 
-       /**
-        * Encode an expiry time into the DBMS dependent format
-        *
-        * @param string $expiry Timestamp for expiry, or the 'infinity' string
-        * @return string
-        */
        public function encodeExpiry( $expiry ) {
                return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
                        ? $this->getInfinity()
                        : $this->timestamp( $expiry );
        }
 
-       /**
-        * Decode an expiry time into a DBMS independent format
-        *
-        * @param string $expiry DB timestamp field value for expiry
-        * @param int $format TS_* constant, defaults to TS_MW
-        * @return string
-        */
        public function decodeExpiry( $expiry, $format = TS_MW ) {
                return ( $expiry == '' || $expiry == 'infinity' || $expiry == $this->getInfinity() )
                        ? 'infinity'
                        : wfTimestamp( $format, $expiry );
        }
 
-       /**
-        * Allow or deny "big selects" for this session only. This is done by setting
-        * the sql_big_selects session variable.
-        *
-        * This is a MySQL-specific feature.
-        *
-        * @param bool|string $value True for allow, false for deny, or "default" to
-        *   restore the initial value
-        */
        public function setBigSelects( $value = true ) {
                // no-op
        }
index b36de98..3a8f737 100644 (file)
@@ -621,13 +621,20 @@ abstract class DatabaseMysqlBase extends Database {
        abstract protected function mysqlPing();
 
        function getLag() {
-               if ( $this->lagDetectionMethod === 'pt-heartbeat' ) {
+               if ( $this->getLagDetectionMethod() === 'pt-heartbeat' ) {
                        return $this->getLagFromPtHeartbeat();
                } else {
                        return $this->getLagFromSlaveStatus();
                }
        }
 
+       /**
+        * @return string
+        */
+       protected function getLagDetectionMethod() {
+               return $this->lagDetectionMethod;
+       }
+
        /**
         * @return bool|int
         */
@@ -645,35 +652,96 @@ abstract class DatabaseMysqlBase extends Database {
         * @return bool|float
         */
        protected function getLagFromPtHeartbeat() {
-               $key = wfMemcKey( 'mysql', 'master-server-id', $this->getServer() );
-               $masterId = intval( $this->srvCache->get( $key ) );
-               if ( !$masterId ) {
-                       $res = $this->query( 'SHOW SLAVE STATUS', __METHOD__ );
-                       $row = $res ? $res->fetchObject() : false;
-                       if ( $row && strval( $row->Master_Server_Id ) !== '' ) {
-                               $masterId = intval( $row->Master_Server_Id );
-                               $this->srvCache->set( $key, $masterId, 30 );
-                       }
+               $masterInfo = $this->getMasterServerInfo();
+               if ( !$masterInfo ) {
+                       wfLogDBError(
+                               "Unable to query master of {db_server} for server ID",
+                               $this->getLogContext( array(
+                                       'method' => __METHOD__
+                               ) )
+                       );
+
+                       return false; // could not get master server ID
                }
 
-               if ( !$masterId ) {
-                       return false;
+               list( $time, $nowUnix ) = $this->getHeartbeatData( $masterInfo['serverId'] );
+               if ( $time !== null ) {
+                       // @time is in ISO format like "2015-09-25T16:48:10.000510"
+                       $dateTime = new DateTime( $time, new DateTimeZone( 'UTC' ) );
+                       $timeUnix = (int)$dateTime->format( 'U' ) + $dateTime->format( 'u' ) / 1e6;
+
+                       return max( $nowUnix - $timeUnix, 0.0 );
                }
 
+               wfLogDBError(
+                       "Unable to find pt-heartbeat row for {db_server}",
+                       $this->getLogContext( array(
+                               'method' => __METHOD__
+                       ) )
+               );
+
+               return false;
+       }
+
+       protected function getMasterServerInfo() {
+               $cache = $this->srvCache;
+               $key = $cache->makeGlobalKey(
+                       'mysql',
+                       'master-info',
+                       // Using one key for all cluster slaves is preferable
+                       $this->getLBInfo( 'clusterMasterHost' ) ?: $this->getServer()
+               );
+
+               $that = $this;
+               return $cache->getWithSetCallback(
+                       $key,
+                       $cache::TTL_INDEFINITE,
+                       function () use ( $that, $cache, $key ) {
+                               // Get and leave a lock key in place for a short period
+                               if ( !$cache->lock( $key, 0, 10 ) ) {
+                                       return false; // avoid master connection spike slams
+                               }
+
+                               $conn = $that->getLazyMasterHandle();
+                               if ( !$conn ) {
+                                       return false; // something is misconfigured
+                               }
+
+                               // Connect to and query the master; catch errors to avoid outages
+                               try {
+                                       $res = $conn->query( 'SELECT @@server_id AS id', __METHOD__ );
+                                       $row = $res ? $res->fetchObject() : false;
+                                       $id = $row ? (int)$row->id : 0;
+                               } catch ( DBError $e ) {
+                                       $id = 0;
+                               }
+
+                               // Cache the ID if it was retrieved
+                               return $id ? array( 'serverId' => $id, 'asOf' => time() ) : false;
+                       }
+               );
+       }
+
+       /**
+        * @param string $masterId Server ID
+        * @return array (heartbeat `ts` column value or null, UNIX timestamp)
+        * @see https://www.percona.com/doc/percona-toolkit/2.1/pt-heartbeat.html
+        */
+       protected function getHeartbeatData( $masterId ) {
+               // Get the status row for this master; use the oldest for sanity in case the master
+               // has entries listed under different server IDs (which should really not happen).
+               // Note: this would use "MAX(TIMESTAMPDIFF(MICROSECOND,ts,UTC_TIMESTAMP(6)))" but the
+               // percision field is not supported in MySQL <= 5.5.
                $res = $this->query(
-                       "SELECT TIMESTAMPDIFF(MICROSECOND,ts,UTC_TIMESTAMP(6)) AS Lag " .
-                       "FROM heartbeat.heartbeat WHERE server_id = $masterId"
+                       "SELECT ts FROM heartbeat.heartbeat WHERE server_id=" . intval( $masterId )
                );
                $row = $res ? $res->fetchObject() : false;
-               if ( $row ) {
-                       return max( floatval( $row->Lag ) / 1e6, 0.0 );
-               }
 
-               return false;
+               return array( $row ? $row->ts : null, microtime( true ) );
        }
 
        public function getApproximateLagStatus() {
-               if ( $this->lagDetectionMethod === 'pt-heartbeat' ) {
+               if ( $this->getLagDetectionMethod() === 'pt-heartbeat' ) {
                        // Disable caching since this is fast enough and we don't wan't
                        // to be *too* pessimistic by having both the cache TTL and the
                        // pt-heartbeat interval count as lag in getSessionLagStatus()
index 4674c17..c72218a 100644 (file)
@@ -174,7 +174,7 @@ interface IDatabase {
        public function writesOrCallbacksPending();
 
        /**
-        * Get the time spend running write queries for this
+        * Get the time spend running write queries for this transaction
         *
         * High times could be due to scanning, updates, locking, and such
         *
@@ -183,6 +183,14 @@ interface IDatabase {
         */
        public function pendingWriteQueryDuration();
 
+       /**
+        * Get the list of method names that did write queries for this transaction
+        *
+        * @return array
+        * @since 1.27
+        */
+       public function pendingWriteCallers();
+
        /**
         * Is a connection to the database open?
         * @return bool
@@ -1268,6 +1276,34 @@ interface IDatabase {
         */
        public function endAtomic( $fname = __METHOD__ );
 
+       /**
+        * Run a callback to do an atomic set of updates for this database
+        *
+        * The $callback takes the following arguments:
+        *   - This database object
+        *   - The value of $fname
+        *
+        * If any exception occurs in the callback, then rollback() will be called and the error will
+        * be re-thrown. It may also be that the rollback itself fails with an exception before then.
+        * In any case, such errors are expected to terminate the request, without any outside caller
+        * attempting to catch errors and commit anyway. Note that any rollback undoes all prior
+        * atomic section and uncommitted updates, which trashes the current request, requiring an
+        * error to be displayed.
+        *
+        * This can be an alternative to explicit startAtomic()/endAtomic() calls.
+        *
+        * @see DatabaseBase::startAtomic
+        * @see DatabaseBase::endAtomic
+        *
+        * @param string $fname Caller name (usually __METHOD__)
+        * @param callable $callback Callback that issues DB updates
+        * @throws DBError
+        * @throws RuntimeException
+        * @throws UnexpectedValueException
+        * @since 1.27
+        */
+       public function doAtomicSection( $fname, $callback );
+
        /**
         * Begin a transaction. If a transaction is already in progress,
         * that transaction will be committed before the new transaction is started.
index eeeca62..25fdea9 100644 (file)
@@ -21,6 +21,9 @@
  * @ingroup Database
  */
 
+use Psr\Log\LoggerInterface;
+use MediaWiki\Logger\LoggerFactory;
+
 /**
  * An interface for generating database load balancers
  * @ingroup Database
@@ -29,6 +32,12 @@ abstract class LBFactory {
        /** @var ChronologyProtector */
        protected $chronProt;
 
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
+
+       /** @var LoggerInterface */
+       protected $logger;
+
        /** @var LBFactory */
        private static $instance;
 
@@ -47,6 +56,8 @@ abstract class LBFactory {
                }
 
                $this->chronProt = $this->newChronologyProtector();
+               $this->trxProfiler = Profiler::instance()->getTransactionProfiler();
+               $this->logger = LoggerFactory::getInstance( 'DBTransaction' );
        }
 
        /**
@@ -190,36 +201,74 @@ abstract class LBFactory {
         * @param array $args
         */
        private function forEachLBCallMethod( $methodName, array $args = array() ) {
-               $this->forEachLB( function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
-                       call_user_func_array( array( $loadBalancer, $methodName ), $args );
-               }, array( $methodName, $args ) );
+               $this->forEachLB(
+                       function ( LoadBalancer $loadBalancer, $methodName, array $args ) {
+                               call_user_func_array( array( $loadBalancer, $methodName ), $args );
+                       },
+                       array( $methodName, $args )
+               );
        }
 
        /**
         * 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.
+        * @param string $fname Caller name
         */
-       public function commitAll() {
-               $this->forEachLBCallMethod( 'commitAll' );
+       public function commitAll( $fname = __METHOD__ ) {
+               $this->logMultiDbTransaction();
+
+               $start = microtime( true );
+               $this->forEachLBCallMethod( 'commitAll', array( $fname ) );
+               $timeMs = 1000 * ( microtime( true ) - $start );
+
+               RequestContext::getMain()->getStats()->timing( "db.commit-all", $timeMs );
        }
 
        /**
         * Commit changes on all master connections
+        * @param string $fname Caller name
         */
-       public function commitMasterChanges() {
+       public function commitMasterChanges( $fname = __METHOD__ ) {
+               $this->logMultiDbTransaction();
+
                $start = microtime( true );
-               $this->forEachLBCallMethod( 'commitMasterChanges' );
+               $this->forEachLBCallMethod( 'commitMasterChanges', array( $fname ) );
                $timeMs = 1000 * ( microtime( true ) - $start );
+
                RequestContext::getMain()->getStats()->timing( "db.commit-masters", $timeMs );
        }
 
        /**
         * Rollback changes on all master connections
+        * @param string $fname Caller name
         * @since 1.23
         */
-       public function rollbackMasterChanges() {
-               $this->forEachLBCallMethod( 'rollbackMasterChanges' );
+       public function rollbackMasterChanges( $fname = __METHOD__ ) {
+               $this->forEachLBCallMethod( 'rollbackMasterChanges', array( $fname ) );
+       }
+
+       /**
+        * Log query info if multi DB transactions are going to be committed now
+        */
+       private function logMultiDbTransaction() {
+               $callersByDB = array();
+               $this->forEachLB( function ( LoadBalancer $lb ) use ( &$callersByDB ) {
+                       $masterName = $lb->getServerName( $lb->getWriterIndex() );
+                       $callers = $lb->pendingMasterChangeCallers();
+                       if ( $callers ) {
+                               $callersByDB[$masterName] = $callers;
+                       }
+               } );
+
+               if ( count( $callersByDB ) >= 2 ) {
+                       $dbs = implode( ', ', array_keys( $callersByDB ) );
+                       $msg = "Multi-DB transaction [{$dbs}]:\n";
+                       foreach ( $callersByDB as $db => $callers ) {
+                               $msg .= "$db: " . implode( '; ', $callers ) . "\n";
+                       }
+                       $this->logger->info( $msg );
+               }
        }
 
        /**
@@ -263,6 +312,90 @@ abstract class LBFactory {
                return $ret;
        }
 
+       /**
+        * Waits for the slave 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.
+        *
+        * 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.
+        * In that case, setting "ifWritesSince" can avoid the overhead of waiting for clusters
+        * that were not changed since the last wait check. To forcefully wait on a specific cluster
+        * for a given wiki, use the 'wiki' parameter. To forcefully wait on an "external" cluster,
+        * use the "cluster" parameter.
+        *
+        * Never call this function after a large DB write that is *still* in a transaction.
+        * It only makes sense to call this after the possible lag inducing changes were committed.
+        *
+        * @param array $opts Optional fields that include:
+        *   - wiki : wait on the load balancer DBs that handles the given wiki
+        *   - cluster : wait on the given external load balancer DBs
+        *   - timeout : Max wait time. Default: ~60 seconds
+        *   - ifWritesSince: Only wait if writes were done since this UNIX timestamp
+        * @throws DBReplicationWaitError If a timeout or error occured waiting on a DB cluster
+        * @since 1.27
+        */
+       public function waitForReplication( array $opts = array() ) {
+               $opts += array(
+                       'wiki' => false,
+                       'cluster' => false,
+                       'timeout' => 60,
+                       'ifWritesSince' => null
+               );
+
+               // Figure out which clusters need to be checked
+               /** @var LoadBalancer[] $lbs */
+               $lbs = array();
+               if ( $opts['cluster'] !== false ) {
+                       $lbs[] = $this->getExternalLB( $opts['cluster'] );
+               } elseif ( $opts['wiki'] !== false ) {
+                       $lbs[] = $this->getMainLB( $opts['wiki'] );
+               } else {
+                       $this->forEachLB( function ( LoadBalancer $lb ) use ( &$lbs ) {
+                               $lbs[] = $lb;
+                       } );
+                       if ( !$lbs ) {
+                               return; // nothing actually used
+                       }
+               }
+
+               // Get all the master positions of applicable DBs right now.
+               // This can be faster since waiting on one cluster reduces the
+               // time needed to wait on the next clusters.
+               $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
+                               // Prevents permission error when getting master position
+                               continue;
+                       } elseif ( $opts['ifWritesSince']
+                               && $lb->lastMasterChangeTimestamp() < $opts['ifWritesSince']
+                       ) {
+                               continue; // no writes since the last wait
+                       }
+                       $masterPositions[$i] = $lb->getMasterPos();
+               }
+
+               $failed = array();
+               foreach ( $lbs as $i => $lb ) {
+                       if ( $masterPositions[$i] ) {
+                               // The DBMS may not support getMasterPos() or the whole
+                               // load balancer might be fake (e.g. $wgAllDBsAreLocalhost).
+                               if ( !$lb->waitForAll( $masterPositions[$i], $opts['timeout'] ) ) {
+                                       $failed[] = $lb->getServerName( $lb->getWriterIndex() );
+                               }
+                       }
+               }
+
+               if ( $failed ) {
+                       throw new DBReplicationWaitError(
+                               "Could not wait for slaves to catch up to " .
+                               implode( ', ', $failed )
+                       );
+               }
+       }
+
        /**
         * Disable the ChronologyProtector for all load balancers
         *
@@ -329,3 +462,9 @@ class DBAccessError extends MWException {
                        "This is not allowed." );
        }
 }
+
+/**
+ * Exception class for replica DB wait timeouts
+ */
+class DBReplicationWaitError extends Exception {
+}
index e58aead..39be996 100644 (file)
@@ -300,7 +300,8 @@ class LBFactoryMulti extends LBFactory {
                return new LoadBalancer( array(
                        'servers' => $this->makeServerArray( $template, $loads, $groupLoads ),
                        'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $readOnlyReason
+                       'readOnlyReason' => $readOnlyReason,
+                       'trxProfiler' => $this->trxProfiler
                ) );
        }
 
@@ -402,6 +403,6 @@ class LBFactoryMulti extends LBFactory {
                if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
                        $this->shutdownChronologyProtector( $this->chronProt );
                }
-               $this->commitMasterChanges(); // sanity
+               $this->commitMasterChanges( __METHOD__ ); // sanity
        }
 }
index 1c9e094..53b0310 100644 (file)
@@ -87,7 +87,8 @@ class LBFactorySimple extends LBFactory {
                return new LoadBalancer( array(
                        'servers' => $servers,
                        'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $this->readOnlyReason
+                       'readOnlyReason' => $this->readOnlyReason,
+                       'trxProfiler' => $this->trxProfiler
                ) );
        }
 
@@ -120,7 +121,8 @@ class LBFactorySimple extends LBFactory {
                return new LoadBalancer( array(
                        'servers' => $wgExternalServers[$cluster],
                        'loadMonitor' => $this->loadMonitorClass,
-                       'readOnlyReason' => $this->readOnlyReason
+                       'readOnlyReason' => $this->readOnlyReason,
+                       'trxProfiler' => $this->trxProfiler
                ) );
        }
 
@@ -160,6 +162,6 @@ class LBFactorySimple extends LBFactory {
                if ( !( $flags & self::SHUTDOWN_NO_CHRONPROT ) ) {
                        $this->shutdownChronologyProtector( $this->chronProt );
                }
-               $this->commitMasterChanges(); // sanity
+               $this->commitMasterChanges( __METHOD__ ); // sanity
        }
 }
index 5a6cfa7..cbe9517 100644 (file)
@@ -35,8 +35,10 @@ class LBFactorySingle extends LBFactory {
        public function __construct( array $conf ) {
                parent::__construct( $conf );
 
-               $conf['readOnlyReason'] = $this->readOnlyReason;
-               $this->lb = new LoadBalancerSingle( $conf );
+               $this->lb = new LoadBalancerSingle( array(
+                       'readOnlyReason' => $this->readOnlyReason,
+                       'trxProfiler' => $this->trxProfiler
+               ) + $conf );
        }
 
        /**
@@ -103,7 +105,8 @@ class LoadBalancerSingle extends LoadBalancer {
                                        'dbname' => $this->db->getDBname(),
                                        'load' => 1,
                                )
-                       )
+                       ),
+                       'trxProfiler' => $this->trxProfiler
                ) );
 
                if ( isset( $params['readOnlyReason'] ) ) {
index 19b2d1c..b5a79a9 100644 (file)
@@ -40,9 +40,9 @@ class LoadBalancer {
        private $mAllowLagged;
        /** @var integer Seconds to spend waiting on slave lag to resolve */
        private $mWaitTimeout;
-
        /** @var array LBFactory information */
        private $mParentInfo;
+
        /** @var string The LoadMonitor subclass name */
        private $mLoadMonitorClass;
        /** @var LoadMonitor */
@@ -67,6 +67,9 @@ class LoadBalancer {
        /** @var integer Total connections opened */
        private $connsOpened = 0;
 
+       /** @var TransactionProfiler */
+       protected $trxProfiler;
+
        /** @var integer Warn when this many connection are held */
        const CONN_HELD_WARN_THRESHOLD = 10;
        /** @var integer Default 'max lag' when unspecified */
@@ -127,6 +130,12 @@ class LoadBalancer {
                }
 
                $this->srvCache = ObjectCache::getLocalServerInstance();
+
+               if ( isset( $params['trxProfiler'] ) ) {
+                       $this->trxProfiler = $params['trxProfiler'];
+               } else {
+                       $this->trxProfiler = new TransactionProfiler();
+               }
        }
 
        /**
@@ -181,11 +190,13 @@ class LoadBalancer {
                                if ( isset( $this->mServers[$i]['max lag'] ) ) {
                                        $maxServerLag = min( $maxServerLag, $this->mServers[$i]['max lag'] );
                                }
+
+                               $host = $this->getServerName( $i );
                                if ( $lag === false ) {
-                                       wfDebugLog( 'replication', "Server #$i is not replicating" );
+                                       wfDebugLog( 'replication', "Server $host (#$i) is not replicating?" );
                                        unset( $loads[$i] );
                                } elseif ( $lag > $maxServerLag ) {
-                                       wfDebugLog( 'replication', "Server #$i is excessively lagged ($lag seconds)" );
+                                       wfDebugLog( 'replication', "Server $host (#$i) has >= $lag seconds of lag" );
                                        unset( $loads[$i] );
                                }
                        }
@@ -670,9 +681,7 @@ class LoadBalancer {
         *
         * @param int $i Server index
         * @param string|bool $wiki Wiki ID, or false for the current wiki
-        * @return DatabaseBase
-        *
-        * @access private
+        * @return DatabaseBase|bool Returns false on errors
         */
        public function openConnection( $i, $wiki = false ) {
                if ( $wiki !== false ) {
@@ -816,11 +825,14 @@ class LoadBalancer {
                        $server['dbname'] = $dbNameOverride;
                }
 
+               // Let the handle know what the cluster master is (e.g. "db1052")
+               $masterName = $this->getServerName( 0 );
+               $server['clusterMasterHost'] = $masterName;
+
                // Log when many connection are made on requests
                if ( ++$this->connsOpened >= self::CONN_HELD_WARN_THRESHOLD ) {
-                       $masterAddr = $this->getServerName( 0 );
                        wfDebugLog( 'DBPerformance', __METHOD__ . ": " .
-                               "{$this->connsOpened}+ connections made (master=$masterAddr)\n" .
+                               "{$this->connsOpened}+ connections made (master=$masterName)\n" .
                                wfBacktrace( true ) );
                }
 
@@ -834,6 +846,10 @@ class LoadBalancer {
                }
 
                $db->setLBInfo( $server );
+               $db->setLazyMasterHandle(
+                       $this->getLazyConnectionRef( DB_MASTER, array(), $db->getWikiID() )
+               );
+               $db->setTransactionProfiler( $this->trxProfiler );
 
                return $db;
        }
@@ -1021,14 +1037,15 @@ class LoadBalancer {
 
        /**
         * Commit transactions on all open connections
+        * @param string $fname Caller name
         */
-       public function commitAll() {
+       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( __METHOD__, 'flush' );
+                                               $conn->commit( $fname, 'flush' );
                                        }
                                }
                        }
@@ -1036,9 +1053,10 @@ class LoadBalancer {
        }
 
        /**
-        *  Issue COMMIT only on master, only if queries were done on connection
+        * Issue COMMIT only on master, only if queries were done on connection
+        * @param string $fname Caller name
         */
-       public function commitMasterChanges() {
+       public function commitMasterChanges( $fname = __METHOD__ ) {
                $masterIndex = $this->getWriterIndex();
                foreach ( $this->mConns as $conns2 ) {
                        if ( empty( $conns2[$masterIndex] ) ) {
@@ -1047,7 +1065,7 @@ class LoadBalancer {
                        /** @var DatabaseBase $conn */
                        foreach ( $conns2[$masterIndex] as $conn ) {
                                if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
-                                       $conn->commit( __METHOD__, 'flush' );
+                                       $conn->commit( $fname, 'flush' );
                                }
                        }
                }
@@ -1055,9 +1073,11 @@ class LoadBalancer {
 
        /**
         * Issue ROLLBACK only on master, only if queries were done on connection
+        * @param string $fname Caller name
+        * @throws DBExpectedError
         * @since 1.23
         */
-       public function rollbackMasterChanges() {
+       public function rollbackMasterChanges( $fname = __METHOD__ ) {
                $failedServers = array();
 
                $masterIndex = $this->getWriterIndex();
@@ -1069,7 +1089,7 @@ class LoadBalancer {
                        foreach ( $conns2[$masterIndex] as $conn ) {
                                if ( $conn->trxLevel() && $conn->writesOrCallbacksPending() ) {
                                        try {
-                                               $conn->rollback( __METHOD__, 'flush' );
+                                               $conn->rollback( $fname, 'flush' );
                                        } catch ( DBError $e ) {
                                                MWExceptionHandler::logException( $e );
                                                $failedServers[] = $conn->getServer();
@@ -1148,6 +1168,29 @@ class LoadBalancer {
                        || $this->lastMasterChangeTimestamp() > microtime( true ) - $age );
        }
 
+       /**
+        * Get the list of callers that have pending master changes
+        *
+        * @return array
+        * @since 1.27
+        */
+       public function pendingMasterChangeCallers() {
+               $fnames = array();
+
+               $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() );
+                       }
+               }
+
+               return $fnames;
+       }
+
        /**
         * @param mixed $value
         * @return mixed
index 31f6163..e251501 100644 (file)
@@ -88,18 +88,33 @@ class LoadMonitorMySQL implements LoadMonitor {
 
                $lagTimes = array();
                foreach ( $serverIndexes as $i ) {
-                       if ( $i == 0 ) { # Master
-                               $lagTimes[$i] = 0;
+                       if ( $i == $this->parent->getWriterIndex() ) {
+                               $lagTimes[$i] = 0; // master always has no lag
                                continue;
                        }
+
                        $conn = $this->parent->getAnyOpenConnection( $i );
-                       if ( $conn !== false ) {
-                               $lagTimes[$i] = $conn->getLag();
+                       if ( $conn ) {
+                               $close = false; // already open
+                       } else {
+                               $conn = $this->parent->openConnection( $i, $wiki );
+                               $close = true; // new connection
+                       }
+
+                       if ( !$conn ) {
+                               $lagTimes[$i] = false;
+                               $host = $this->parent->getServerName( $i );
+                               wfDebugLog( 'replication', __METHOD__ . ": host $host (#$i) is unreachable" );
                                continue;
                        }
-                       $conn = $this->parent->openConnection( $i, $wiki );
-                       if ( $conn !== false ) {
-                               $lagTimes[$i] = $conn->getLag();
+
+                       $lagTimes[$i] = $conn->getLag();
+                       if ( $lagTimes[$i] === false ) {
+                               $host = $this->parent->getServerName( $i );
+                               wfDebugLog( 'replication', __METHOD__ . ": host $host (#$i) is not replicating?" );
+                       }
+
+                       if ( $close ) {
                                # Close the connection to avoid sleeper connections piling up.
                                # Note that the caller will pick one of these DBs and reconnect,
                                # which is slightly inefficient, but this only matters for the lag
@@ -124,7 +139,10 @@ class LoadMonitorMySQL implements LoadMonitor {
        }
 
        private function getLagTimeCacheKey() {
+               $writerIndex = $this->parent->getWriterIndex();
                // Lag is per-server, not per-DB, so key on the master DB name
-               return $this->srvCache->makeGlobalKey( 'lag-times', $this->parent->getServerName( 0 ) );
+               return $this->srvCache->makeGlobalKey(
+                       'lag-times', $this->parent->getServerName( $writerIndex )
+               );
        }
 }
index 841636c..8f943bf 100644 (file)
@@ -300,7 +300,7 @@ class MWDebug {
                        trigger_error( $msg, $level );
                }
 
-               wfDebugLog( $group, $msg, 'log' );
+               wfDebugLog( $group, $msg, 'private' );
        }
 
        /**
index 65719fa..34ea641 100644 (file)
@@ -70,7 +70,6 @@ class LegacyLogger extends AbstractLogger {
                LogLevel::EMERGENCY => 600,
        );
 
-
        /**
         * @param string $channel
         */
@@ -91,11 +90,12 @@ class LegacyLogger extends AbstractLogger {
                        $destination = self::destination( $this->channel, $message, $context );
                        self::emit( $text, $destination );
                }
-               // Add to debug toolbar
-               MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context );
+               if ( !isset( $context['private'] ) || !$context['private'] ) {
+                       // Add to debug toolbar if not marked as "private"
+                       MWDebug::debugMsg( $message, array( 'channel' => $this->channel ) + $context );
+               }
        }
 
-
        /**
         * Determine if the given message should be emitted or not.
         *
@@ -118,6 +118,13 @@ class LegacyLogger extends AbstractLogger {
                        // All messages on the wfErrorLog channel should be emitted.
                        $shouldEmit = true;
 
+               } elseif ( $channel === 'wfDebug' ) {
+                       // wfDebug messages are emitted if a catch all logging file has
+                       // been specified. Checked explicitly so that 'private' flagged
+                       // messages are not discarded by unset $wgDebugLogGroups channel
+                       // handling below.
+                       $shouldEmit = $wgDebugLogFile != '';
+
                } elseif ( isset( $wgDebugLogGroups[$channel] ) ) {
                        $logConfig = $wgDebugLogGroups[$channel];
 
@@ -154,7 +161,6 @@ class LegacyLogger extends AbstractLogger {
                return $shouldEmit;
        }
 
-
        /**
         * Format a message.
         *
@@ -239,7 +245,6 @@ class LegacyLogger extends AbstractLogger {
                return self::interpolate( $text, $context );
        }
 
-
        /**
         * Format a message as `wfDebug()` would have formatted it.
         *
@@ -261,7 +266,6 @@ class LegacyLogger extends AbstractLogger {
                return "{$text}\n";
        }
 
-
        /**
         * Format a message as `wfLogDBError()` would have formatted it.
         *
@@ -294,7 +298,6 @@ class LegacyLogger extends AbstractLogger {
                return $text;
        }
 
-
        /**
         * Format a message as `wfDebugLog() would have formatted it.
         *
@@ -310,7 +313,6 @@ class LegacyLogger extends AbstractLogger {
                return $text;
        }
 
-
        /**
         * Interpolate placeholders in logging message.
         *
@@ -329,7 +331,6 @@ class LegacyLogger extends AbstractLogger {
                return $message;
        }
 
-
        /**
         * Convert a logging context element to a string suitable for
         * interpolation.
@@ -389,7 +390,6 @@ class LegacyLogger extends AbstractLogger {
                return '[Unknown ' . gettype( $item ) . ']';
        }
 
-
        /**
         * Select the appropriate log output destination for the given log event.
         *
@@ -430,7 +430,6 @@ class LegacyLogger extends AbstractLogger {
                return $destination;
        }
 
-
        /**
        * Log to a file without getting "file size exceeded" signals.
        *
index 1bf39e4..1bb779d 100644 (file)
@@ -42,7 +42,6 @@ class LegacySpi implements Spi {
         */
        protected $singletons = array();
 
-
        /**
         * Get a logger instance.
         *
index e0c4989..ce92dbd 100644 (file)
@@ -51,7 +51,6 @@ class LoggerFactory {
         */
        private static $spi;
 
-
        /**
         * Register a service provider to create new \Psr\Log\LoggerInterface
         * instances.
@@ -62,7 +61,6 @@ class LoggerFactory {
                self::$spi = $provider;
        }
 
-
        /**
         * Get the registered service provider.
         *
@@ -86,7 +84,6 @@ class LoggerFactory {
                return self::$spi;
        }
 
-
        /**
         * Get a named logger instance from the currently configured logger factory.
         *
@@ -97,7 +94,6 @@ class LoggerFactory {
                return self::getProvider()->getLogger( $channel );
        }
 
-
        /**
         * Construction of utility class is not allowed.
         */
index eea9adc..4b9c3f5 100644 (file)
@@ -126,7 +126,6 @@ class MonologSpi implements Spi {
         */
        protected $config;
 
-
        /**
         * @param array $config Configuration data.
         */
@@ -135,7 +134,6 @@ class MonologSpi implements Spi {
                $this->mergeConfig( $config );
        }
 
-
        /**
         * Merge additional configuration data into the configuration.
         *
@@ -153,7 +151,6 @@ class MonologSpi implements Spi {
                $this->reset();
        }
 
-
        /**
         * Reset internal caches.
         *
@@ -169,7 +166,6 @@ class MonologSpi implements Spi {
                );
        }
 
-
        /**
         * Get a logger instance.
         *
@@ -195,7 +191,6 @@ class MonologSpi implements Spi {
                return $this->singletons['loggers'][$channel];
        }
 
-
        /**
         * Create a logger.
         * @param string $channel Logger channel
@@ -225,7 +220,6 @@ class MonologSpi implements Spi {
                return $obj;
        }
 
-
        /**
         * Create or return cached processor.
         * @param string $name Processor name
@@ -240,7 +234,6 @@ class MonologSpi implements Spi {
                return $this->singletons['processors'][$name];
        }
 
-
        /**
         * Create or return cached handler.
         * @param string $name Processor name
@@ -263,7 +256,6 @@ class MonologSpi implements Spi {
                return $this->singletons['handlers'][$name];
        }
 
-
        /**
         * Create or return cached formatter.
         * @param string $name Formatter name
index 8ae34e8..f92ff7d 100644 (file)
@@ -44,12 +44,10 @@ class NullSpi implements Spi {
         */
        protected $singleton;
 
-
        public function __construct() {
                $this->singleton = new NullLogger();
        }
 
-
        /**
         * Get a logger instance.
         *
index 510d42a..fc24e82 100644 (file)
@@ -171,7 +171,6 @@ class AvroFormatter implements FormatterInterface {
                return null;
        }
 
-
        /**
         * convert an integer to a 64bits big endian long (Java compatible)
         * NOTE: certainly only compatible with PHP 64bits
index a4bb172..7c75a7d 100644 (file)
@@ -87,7 +87,6 @@ class LegacyHandler extends AbstractProcessingHandler {
         */
        protected $prefix;
 
-
        /**
         * @param string $stream Stream URI
         * @param bool $useLegacyFilter Filter log events using legacy rules
@@ -160,7 +159,6 @@ class LegacyHandler extends AbstractProcessingHandler {
                }
        }
 
-
        /**
         * Custom error handler.
         * @param int $code Error number
@@ -170,7 +168,6 @@ class LegacyHandler extends AbstractProcessingHandler {
                $this->error = $msg;
        }
 
-
        /**
         * Should we use UDP to send messages to the sink?
         * @return bool
@@ -179,7 +176,6 @@ class LegacyHandler extends AbstractProcessingHandler {
                return $this->host !== null;
        }
 
-
        protected function write( array $record ) {
                if ( $this->useLegacyFilter &&
                        !LegacyLogger::shouldEmit(
@@ -228,7 +224,6 @@ class LegacyHandler extends AbstractProcessingHandler {
                }
        }
 
-
        public function close() {
                if ( is_resource( $this->sink ) ) {
                        if ( $this->useUdp() ) {
index 2ba7a53..acc2b30 100644 (file)
@@ -60,7 +60,6 @@ class LineFormatter extends MonologLineFormatter {
                $this->includeStacktraces( $includeStacktraces );
        }
 
-
        /**
         * {@inheritdoc}
         */
@@ -94,7 +93,6 @@ class LineFormatter extends MonologLineFormatter {
                return $output;
        }
 
-
        /**
         * Convert an Exception to a string.
         *
@@ -105,7 +103,6 @@ class LineFormatter extends MonologLineFormatter {
                return $this->normalizeExceptionArray( $this->exceptionAsArray( $e ) );
        }
 
-
        /**
         * Convert an exception to an array of structured data.
         *
@@ -130,7 +127,6 @@ class LineFormatter extends MonologLineFormatter {
                return $out;
        }
 
-
        /**
         * Convert an array of Exception data to a string.
         *
index 2614b16..104ee58 100644 (file)
@@ -58,7 +58,6 @@ class SyslogHandler extends SyslogUdpHandler {
         */
        private $hostname;
 
-
        /**
         * @param string $appname Application name to report to syslog
         * @param string $host Syslog host
diff --git a/includes/deferred/AtomicSectionUpdate.php b/includes/deferred/AtomicSectionUpdate.php
new file mode 100644 (file)
index 0000000..a9921b3
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * Deferrable Update for closure/callback updates via IDatabase::doAtomicSection()
+ * @since 1.27
+ */
+class AtomicSectionUpdate implements DeferrableUpdate {
+       /** @var IDatabase */
+       private $dbw;
+       /** @var string */
+       private $fname;
+       /** @var Closure|callable */
+       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 ) {
+               $this->dbw = $dbw;
+               $this->fname = $fname;
+               if ( !is_callable( $callback ) ) {
+                       throw new InvalidArgumentException( 'Not a valid callback/closure!' );
+               }
+               $this->callback = $callback;
+       }
+
+       public function doUpdate() {
+               $this->dbw->doAtomicSection( $this->fname, $this->callback );
+       }
+}
index 808626d..4b19c20 100644 (file)
@@ -4,25 +4,20 @@
  * Deferrable Update for closure/callback
  */
 class MWCallableUpdate implements DeferrableUpdate {
-       /**
-        * @var Closure|callable
-        */
+       /** @var Closure|callable */
        private $callback;
 
        /**
         * @param callable $callback
-        * @throws MWException
+        * @throws InvalidArgumentException
         */
        public function __construct( $callback ) {
                if ( !is_callable( $callback ) ) {
-                       throw new MWException( 'Not a valid callback/closure!' );
+                       throw new InvalidArgumentException( 'Not a valid callback/closure!' );
                }
                $this->callback = $callback;
        }
 
-       /**
-        * Run the update
-        */
        public function doUpdate() {
                call_user_func( $this->callback );
        }
index fb6ef13..5583588 100644 (file)
@@ -149,7 +149,7 @@ class DeferredUpdates {
                        foreach ( $otherUpdates as $update ) {
                                try {
                                        $update->doUpdate();
-                                       wfGetLBFactory()->commitMasterChanges();
+                                       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
@@ -158,7 +158,7 @@ class DeferredUpdates {
                                        }
                                        // 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();
+                                       wfGetLBFactory()->rollbackMasterChanges( __METHOD__ );
                                }
                        }
 
index 7401992..ad6e81e 100644 (file)
  */
 
 /**
- * @todo document
+ * The base class for all other DiffOp classes.
+ *
+ * The classes that extend DiffOp are: DiffOpCopy, DiffOpDelete, DiffOpAdd and
+ * DiffOpChange. FakeDiffOp also extends DiffOp, but it is not located in this file.
+ *
  * @private
  * @ingroup DifferenceEngine
  */
@@ -93,7 +97,9 @@ abstract class DiffOp {
 }
 
 /**
- * @todo document
+ * Extends DiffOp. Used to mark strings that have been
+ * copied from one string array to the other.
+ *
  * @private
  * @ingroup DifferenceEngine
  */
@@ -117,7 +123,9 @@ class DiffOpCopy extends DiffOp {
 }
 
 /**
- * @todo document
+ * Extends DiffOp. Used to mark strings that have been
+ * deleted from the first string array.
+ *
  * @private
  * @ingroup DifferenceEngine
  */
@@ -138,7 +146,9 @@ class DiffOpDelete extends DiffOp {
 }
 
 /**
- * @todo document
+ * Extends DiffOp. Used to mark strings that have been
+ * added from the first string array.
+ *
  * @private
  * @ingroup DifferenceEngine
  */
@@ -159,7 +169,9 @@ class DiffOpAdd extends DiffOp {
 }
 
 /**
- * @todo document
+ * Extends DiffOp. Used to mark strings that have been
+ * changed from the first string array (both added and subtracted).
+ *
  * @private
  * @ingroup DifferenceEngine
  */
index 781b6a6..d588d51 100644 (file)
@@ -433,7 +433,7 @@ class DifferenceEngine extends ContextSource {
                                        array( $msg ) );
                        } else {
                                # Give explanation and add a link to view the diff...
-                               $query = $this->getRequest()->appendQueryValue( 'unhide', '1', true );
+                               $query = $this->getRequest()->appendQueryValue( 'unhide', '1' );
                                $link = $this->getTitle()->getFullURL( $query );
                                $msg = $suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff';
                                $out->wrapWikiMsg(
@@ -459,74 +459,93 @@ class DifferenceEngine extends ContextSource {
        }
 
        /**
-        * Get a link to mark the change as patrolled, or '' if there's either no
-        * revision to patrol or the user is not allowed to to it.
+        * Build a link to mark a change as patrolled.
+        *
+        * Returns empty string if there's either no revision to patrol or the user is not allowed to.
         * Side effect: When the patrol link is build, this method will call
         * OutputPage::preventClickjacking() and load mediawiki.page.patrol.ajax.
         *
-        * @return string
+        * @return string HTML or empty string
         */
        protected function markPatrolledLink() {
+               if ( $this->mMarkPatrolledLink === null ) {
+                       $linkInfo = $this->getMarkPatrolledLinkInfo();
+                       // If false, there is no patrol link needed/allowed
+                       if ( !$linkInfo ) {
+                               $this->mMarkPatrolledLink = '';
+                       } else {
+                               $this->mMarkPatrolledLink = ' <span class="patrollink">[' . Linker::linkKnown(
+                                       $this->mNewPage,
+                                       $this->msg( 'markaspatrolleddiff' )->escaped(),
+                                       array(),
+                                       array(
+                                               'action' => 'markpatrolled',
+                                               'rcid' => $linkInfo['rcid'],
+                                               'token' => $linkInfo['token'],
+                                       )
+                               ) . ']</span>';
+                       }
+               }
+               return $this->mMarkPatrolledLink;
+       }
+
+       /**
+        * Returns an array of meta data needed to build a "mark as patrolled" link and
+        * adds the mediawiki.page.patrol.ajax to the output.
+        *
+        * @return array|false An array of meta data for a patrol link (rcid & token)
+        *  or false if no link is needed
+        */
+       protected function getMarkPatrolledLinkInfo() {
                global $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI;
+
                $user = $this->getUser();
 
-               if ( $this->mMarkPatrolledLink === null ) {
-                       // Prepare a change patrol link, if applicable
-                       if (
-                               // Is patrolling enabled and the user allowed to?
-                               $wgUseRCPatrol && $this->mNewPage->quickUserCan( 'patrol', $user ) &&
-                               // Only do this if the revision isn't more than 6 hours older
-                               // than the Max RC age (6h because the RC might not be cleaned out regularly)
-                               RecentChange::isInRCLifespan( $this->mNewRev->getTimestamp(), 21600 )
-                       ) {
-                               // Look for an unpatrolled change corresponding to this diff
-
-                               $db = wfGetDB( DB_SLAVE );
-                               $change = RecentChange::newFromConds(
-                                       array(
-                                               'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
-                                               'rc_this_oldid' => $this->mNewid,
-                                               'rc_patrolled' => 0
-                                       ),
-                                       __METHOD__
-                               );
+               // Prepare a change patrol link, if applicable
+               if (
+                       // Is patrolling enabled and the user allowed to?
+                       $wgUseRCPatrol && $this->mNewPage->quickUserCan( 'patrol', $user ) &&
+                       // Only do this if the revision isn't more than 6 hours older
+                       // than the Max RC age (6h because the RC might not be cleaned out regularly)
+                       RecentChange::isInRCLifespan( $this->mNewRev->getTimestamp(), 21600 )
+               ) {
+                       // Look for an unpatrolled change corresponding to this diff
+                       $db = wfGetDB( DB_SLAVE );
+                       $change = RecentChange::newFromConds(
+                               array(
+                                       'rc_timestamp' => $db->timestamp( $this->mNewRev->getTimestamp() ),
+                                       'rc_this_oldid' => $this->mNewid,
+                                       'rc_patrolled' => 0
+                               ),
+                               __METHOD__
+                       );
 
-                               if ( $change && !$change->getPerformer()->equals( $user ) ) {
-                                       $rcid = $change->getAttribute( 'rc_id' );
-                               } else {
-                                       // None found or the page has been created by the current user.
-                                       // If the user could patrol this it already would be patrolled
-                                       $rcid = 0;
+                       if ( $change && !$change->getPerformer()->equals( $user ) ) {
+                               $rcid = $change->getAttribute( 'rc_id' );
+                       } else {
+                               // None found or the page has been created by the current user.
+                               // If the user could patrol this it already would be patrolled
+                               $rcid = 0;
+                       }
+                       // Build the link
+                       if ( $rcid ) {
+                               $this->getOutput()->preventClickjacking();
+                               if ( $wgEnableAPI && $wgEnableWriteAPI
+                                       && $user->isAllowed( 'writeapi' )
+                               ) {
+                                       $this->getOutput()->addModules( 'mediawiki.page.patrol.ajax' );
                                }
-                               // Build the link
-                               if ( $rcid ) {
-                                       $this->getOutput()->preventClickjacking();
-                                       if ( $wgEnableAPI && $wgEnableWriteAPI
-                                               && $user->isAllowed( 'writeapi' )
-                                       ) {
-                                               $this->getOutput()->addModules( 'mediawiki.page.patrol.ajax' );
-                                       }
 
-                                       $token = $user->getEditToken( $rcid );
-                                       $this->mMarkPatrolledLink = ' <span class="patrollink">[' . Linker::linkKnown(
-                                               $this->mNewPage,
-                                               $this->msg( 'markaspatrolleddiff' )->escaped(),
-                                               array(),
-                                               array(
-                                                       'action' => 'markpatrolled',
-                                                       'rcid' => $rcid,
-                                                       'token' => $token,
-                                               )
-                                       ) . ']</span>';
-                               } else {
-                                       $this->mMarkPatrolledLink = '';
-                               }
-                       } else {
-                               $this->mMarkPatrolledLink = '';
+                               $token = $user->getEditToken( $rcid );
+                               return array(
+                                       'rcid' => $rcid,
+                                       'token' => $token,
+                               );
                        }
                }
 
-               return $this->mMarkPatrolledLink;
+               // No mark as patrolled link applicable
+               return false;
        }
 
        /**
index 26960ff..d653dd0 100644 (file)
@@ -143,7 +143,7 @@ class MWExceptionHandler {
                                self::getLogMessage( $e ),
                                self::getLogContext( $e )
                        );
-                       $factory->rollbackMasterChanges();
+                       $factory->rollbackMasterChanges( __METHOD__ );
                }
        }
 
@@ -246,7 +246,6 @@ class MWExceptionHandler {
                return false;
        }
 
-
        /**
         * Dual purpose callback used as both a set_error_handler() callback and
         * a registered shutdown function. Receive a callback from the interpreter
diff --git a/includes/export/Dump7ZipOutput.php b/includes/export/Dump7ZipOutput.php
new file mode 100644 (file)
index 0000000..31c945c
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+/**
+ * Sends dump output via the p7zip compressor.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class Dump7ZipOutput extends DumpPipeOutput {
+       /**
+        * @var int
+        */
+       protected $compressionLevel;
+
+       /**
+        * @param string $file
+        * @param int $cmpLevel Compression level passed to 7za command's -mx
+        */
+       function __construct( $file, $cmpLevel = 4 ) {
+               $this->compressionLevel = $cmpLevel;
+               $command = $this->setup7zCommand( $file );
+               parent::__construct( $command );
+               $this->filename = $file;
+       }
+
+       /**
+        * @param string $file
+        * @return string
+        */
+       function setup7zCommand( $file ) {
+               $command = "7za a -bd -si -mx=";
+               $command .= wfEscapeShellArg( $this->compressionLevel ) . ' ';
+               $command .= wfEscapeShellArg( $file );
+               // Suppress annoying useless crap from p7zip
+               // Unfortunately this could suppress real error messages too
+               $command .= ' >' . wfGetNull() . ' 2>&1';
+               return $command;
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $newname = $this->checkRenameArgCount( $newname );
+               if ( $newname ) {
+                       fclose( $this->handle );
+                       proc_close( $this->procOpenResource );
+                       $this->renameOrException( $newname );
+                       if ( $open ) {
+                               $command = $this->setup7zCommand( $this->filename );
+                               $this->startCommand( $command );
+                       }
+               }
+       }
+}
diff --git a/includes/export/DumpBZip2Output.php b/includes/export/DumpBZip2Output.php
new file mode 100644 (file)
index 0000000..bbc1c11
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Sends dump output via the bgzip2 compressor.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpBZip2Output extends DumpPipeOutput {
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               parent::__construct( "bzip2", $file );
+       }
+}
diff --git a/includes/export/DumpDBZip2Output.php b/includes/export/DumpDBZip2Output.php
new file mode 100644 (file)
index 0000000..5edde8f
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Sends dump output via the bgzip2 compressor.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpDBZip2Output extends DumpPipeOutput {
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               parent::__construct( "dbzip2", $file );
+       }
+}
diff --git a/includes/export/DumpFileOutput.php b/includes/export/DumpFileOutput.php
new file mode 100644 (file)
index 0000000..4bec7d4
--- /dev/null
@@ -0,0 +1,115 @@
+<?php
+/**
+ * Stream outputter to send data to a file.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpFileOutput extends DumpOutput {
+       protected $handle = false, $filename;
+
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               $this->handle = fopen( $file, "wt" );
+               $this->filename = $file;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               parent::writeCloseStream( $string );
+               if ( $this->handle ) {
+                       fclose( $this->handle );
+                       $this->handle = false;
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function write( $string ) {
+               fputs( $this->handle, $string );
+       }
+
+       /**
+        * @param string $newname
+        */
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       /**
+        * @param string $newname
+        * @throws MWException
+        */
+       function renameOrException( $newname ) {
+                       if ( !rename( $this->filename, $newname ) ) {
+                               throw new MWException( __METHOD__ . ": rename of file {$this->filename} to $newname failed\n" );
+                       }
+       }
+
+       /**
+        * @param array $newname
+        * @return string
+        * @throws MWException
+        */
+       function checkRenameArgCount( $newname ) {
+               if ( is_array( $newname ) ) {
+                       if ( count( $newname ) > 1 ) {
+                               throw new MWException( __METHOD__ . ": passed multiple arguments for rename of single file\n" );
+                       } else {
+                               $newname = $newname[0];
+                       }
+               }
+               return $newname;
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $newname = $this->checkRenameArgCount( $newname );
+               if ( $newname ) {
+                       if ( $this->handle ) {
+                               fclose( $this->handle );
+                               $this->handle = false;
+                       }
+                       $this->renameOrException( $newname );
+                       if ( $open ) {
+                               $this->handle = fopen( $this->filename, "wt" );
+                       }
+               }
+       }
+
+       /**
+        * @return string|null
+        */
+       function getFilenames() {
+               return $this->filename;
+       }
+}
diff --git a/includes/export/DumpFilter.php b/includes/export/DumpFilter.php
new file mode 100644 (file)
index 0000000..5c27658
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Dump output filter class.
+ * This just does output filtering and streaming; XML formatting is done
+ * higher up, so be careful in what you do.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpFilter {
+       /**
+        * @var DumpOutput
+        * FIXME will need to be made protected whenever legacy code
+        * is updated.
+        */
+       public $sink;
+
+       /**
+        * @var bool
+        */
+       protected $sendingThisPage;
+
+       /**
+        * @param DumpOutput $sink
+        */
+       function __construct( &$sink ) {
+               $this->sink =& $sink;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeOpenStream( $string ) {
+               $this->sink->writeOpenStream( $string );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               $this->sink->writeCloseStream( $string );
+       }
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               $this->sendingThisPage = $this->pass( $page, $string );
+               if ( $this->sendingThisPage ) {
+                       $this->sink->writeOpenPage( $page, $string );
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               if ( $this->sendingThisPage ) {
+                       $this->sink->writeClosePage( $string );
+                       $this->sendingThisPage = false;
+               }
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               if ( $this->sendingThisPage ) {
+                       $this->sink->writeRevision( $rev, $string );
+               }
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeLogItem( $rev, $string ) {
+               $this->sink->writeRevision( $rev, $string );
+       }
+
+       /**
+        * @param string $newname
+        */
+       function closeRenameAndReopen( $newname ) {
+               $this->sink->closeRenameAndReopen( $newname );
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $this->sink->closeAndRename( $newname, $open );
+       }
+
+       /**
+        * @return array
+        */
+       function getFilenames() {
+               return $this->sink->getFilenames();
+       }
+
+       /**
+        * Override for page-based filter types.
+        * @param object $page
+        * @return bool
+        */
+       function pass( $page ) {
+               return true;
+       }
+}
diff --git a/includes/export/DumpGZipOutput.php b/includes/export/DumpGZipOutput.php
new file mode 100644 (file)
index 0000000..d9e74a7
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Sends dump output via the gzip compressor.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpGZipOutput extends DumpPipeOutput {
+       /**
+        * @param string $file
+        */
+       function __construct( $file ) {
+               parent::__construct( "gzip", $file );
+       }
+}
diff --git a/includes/export/DumpLatestFilter.php b/includes/export/DumpLatestFilter.php
new file mode 100644 (file)
index 0000000..d3742b7
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Dump output filter to include only the last revision in each page sequence.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpLatestFilter extends DumpFilter {
+       public $page;
+
+       public $pageString;
+
+       public $rev;
+
+       public $revString;
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               $this->page = $page;
+               $this->pageString = $string;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               if ( $this->rev ) {
+                       $this->sink->writeOpenPage( $this->page, $this->pageString );
+                       $this->sink->writeRevision( $this->rev, $this->revString );
+                       $this->sink->writeClosePage( $string );
+               }
+               $this->rev = null;
+               $this->revString = null;
+               $this->page = null;
+               $this->pageString = null;
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               if ( $rev->rev_id == $this->page->page_latest ) {
+                       $this->rev = $rev;
+                       $this->revString = $string;
+               }
+       }
+}
diff --git a/includes/export/DumpMultiWriter.php b/includes/export/DumpMultiWriter.php
new file mode 100644 (file)
index 0000000..6fe11a3
--- /dev/null
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Base class for output stream; prints to stdout or buffer or wherever.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpMultiWriter {
+
+       /**
+        * @param array $sinks
+        */
+       function __construct( $sinks ) {
+               $this->sinks = $sinks;
+               $this->count = count( $sinks );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeOpenStream( $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeOpenStream( $string );
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeCloseStream( $string );
+               }
+       }
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeOpenPage( $page, $string );
+               }
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeClosePage( $string );
+               }
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->writeRevision( $rev, $string );
+               }
+       }
+
+       /**
+        * @param array $newnames
+        */
+       function closeRenameAndReopen( $newnames ) {
+               $this->closeAndRename( $newnames, true );
+       }
+
+       /**
+        * @param array $newnames
+        * @param bool $open
+        */
+       function closeAndRename( $newnames, $open = false ) {
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $this->sinks[$i]->closeAndRename( $newnames[$i], $open );
+               }
+       }
+
+       /**
+        * @return array
+        */
+       function getFilenames() {
+               $filenames = array();
+               for ( $i = 0; $i < $this->count; $i++ ) {
+                       $filenames[] = $this->sinks[$i]->getFilenames();
+               }
+               return $filenames;
+       }
+}
diff --git a/includes/export/DumpNamespaceFilter.php b/includes/export/DumpNamespaceFilter.php
new file mode 100644 (file)
index 0000000..e8d4428
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Dump output filter to include or exclude pages in a given set of namespaces.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpNamespaceFilter extends DumpFilter {
+       /** @var bool */
+       public $invert = false;
+
+       /** @var array */
+       public $namespaces = array();
+
+       /**
+        * @param DumpOutput $sink
+        * @param array $param
+        * @throws MWException
+        */
+       function __construct( &$sink, $param ) {
+               parent::__construct( $sink );
+
+               $constants = array(
+                       "NS_MAIN"           => NS_MAIN,
+                       "NS_TALK"           => NS_TALK,
+                       "NS_USER"           => NS_USER,
+                       "NS_USER_TALK"      => NS_USER_TALK,
+                       "NS_PROJECT"        => NS_PROJECT,
+                       "NS_PROJECT_TALK"   => NS_PROJECT_TALK,
+                       "NS_FILE"           => NS_FILE,
+                       "NS_FILE_TALK"      => NS_FILE_TALK,
+                       "NS_IMAGE"          => NS_IMAGE, // NS_IMAGE is an alias for NS_FILE
+                       "NS_IMAGE_TALK"     => NS_IMAGE_TALK,
+                       "NS_MEDIAWIKI"      => NS_MEDIAWIKI,
+                       "NS_MEDIAWIKI_TALK" => NS_MEDIAWIKI_TALK,
+                       "NS_TEMPLATE"       => NS_TEMPLATE,
+                       "NS_TEMPLATE_TALK"  => NS_TEMPLATE_TALK,
+                       "NS_HELP"           => NS_HELP,
+                       "NS_HELP_TALK"      => NS_HELP_TALK,
+                       "NS_CATEGORY"       => NS_CATEGORY,
+                       "NS_CATEGORY_TALK"  => NS_CATEGORY_TALK );
+
+               if ( $param { 0 } == '!' ) {
+                       $this->invert = true;
+                       $param = substr( $param, 1 );
+               }
+
+               foreach ( explode( ',', $param ) as $key ) {
+                       $key = trim( $key );
+                       if ( isset( $constants[$key] ) ) {
+                               $ns = $constants[$key];
+                               $this->namespaces[$ns] = true;
+                       } elseif ( is_numeric( $key ) ) {
+                               $ns = intval( $key );
+                               $this->namespaces[$ns] = true;
+                       } else {
+                               throw new MWException( "Unrecognized namespace key '$key'\n" );
+                       }
+               }
+       }
+
+       /**
+        * @param object $page
+        * @return bool
+        */
+       function pass( $page ) {
+               $match = isset( $this->namespaces[$page->page_namespace] );
+               return $this->invert xor $match;
+       }
+}
diff --git a/includes/export/DumpNotalkFilter.php b/includes/export/DumpNotalkFilter.php
new file mode 100644 (file)
index 0000000..d99b1b1
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+/**
+ * Simple dump output filter to exclude all talk pages.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpNotalkFilter extends DumpFilter {
+       /**
+        * @param object $page
+        * @return bool
+        */
+       function pass( $page ) {
+               return !MWNamespace::isTalk( $page->page_namespace );
+       }
+}
diff --git a/includes/export/DumpOutput.php b/includes/export/DumpOutput.php
new file mode 100644 (file)
index 0000000..edd73fc
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Base class for output stream; prints to stdout or buffer or wherever.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpOutput {
+
+       /**
+        * @param string $string
+        */
+       function writeOpenStream( $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param object $page
+        * @param string $string
+        */
+       function writeOpenPage( $page, $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeClosePage( $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeRevision( $rev, $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * @param object $rev
+        * @param string $string
+        */
+       function writeLogItem( $rev, $string ) {
+               $this->write( $string );
+       }
+
+       /**
+        * Override to write to a different stream type.
+        * @param string $string
+        * @return bool
+        */
+       function write( $string ) {
+               print $string;
+       }
+
+       /**
+        * Close the old file, move it to a specified name,
+        * and reopen new file with the old name. Use this
+        * for writing out a file in multiple pieces
+        * at specified checkpoints (e.g. every n hours).
+        * @param string|array $newname File name. May be a string or an array with one element
+        */
+       function closeRenameAndReopen( $newname ) {
+       }
+
+       /**
+        * Close the old file, and move it to a specified name.
+        * Use this for the last piece of a file written out
+        * at specified checkpoints (e.g. every n hours).
+        * @param string|array $newname File name. May be a string or an array with one element
+        * @param bool $open If true, a new file with the old filename will be opened
+        *   again for writing (default: false)
+        */
+       function closeAndRename( $newname, $open = false ) {
+       }
+
+       /**
+        * Returns the name of the file or files which are
+        * being written to, if there are any.
+        * @return null
+        */
+       function getFilenames() {
+               return null;
+       }
+}
diff --git a/includes/export/DumpPipeOutput.php b/includes/export/DumpPipeOutput.php
new file mode 100644 (file)
index 0000000..61177ab
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Stream outputter to send data to a file via some filter program.
+ * Even if compression is available in a library, using a separate
+ * program can allow us to make use of a multi-processor system.
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class DumpPipeOutput extends DumpFileOutput {
+       protected $command, $filename;
+       protected $procOpenResource = false;
+
+       /**
+        * @param string $command
+        * @param string $file
+        */
+       function __construct( $command, $file = null ) {
+               if ( !is_null( $file ) ) {
+                       $command .= " > " . wfEscapeShellArg( $file );
+               }
+
+               $this->startCommand( $command );
+               $this->command = $command;
+               $this->filename = $file;
+       }
+
+       /**
+        * @param string $string
+        */
+       function writeCloseStream( $string ) {
+               parent::writeCloseStream( $string );
+               if ( $this->procOpenResource ) {
+                       proc_close( $this->procOpenResource );
+                       $this->procOpenResource = false;
+               }
+       }
+
+       /**
+        * @param string $command
+        */
+       function startCommand( $command ) {
+               $spec = array(
+                       0 => array( "pipe", "r" ),
+               );
+               $pipes = array();
+               $this->procOpenResource = proc_open( $command, $spec, $pipes );
+               $this->handle = $pipes[0];
+       }
+
+       /**
+        * @param string $newname
+        */
+       function closeRenameAndReopen( $newname ) {
+               $this->closeAndRename( $newname, true );
+       }
+
+       /**
+        * @param string $newname
+        * @param bool $open
+        */
+       function closeAndRename( $newname, $open = false ) {
+               $newname = $this->checkRenameArgCount( $newname );
+               if ( $newname ) {
+                       if ( $this->handle ) {
+                               fclose( $this->handle );
+                               $this->handle = false;
+                       }
+                       if ( $this->procOpenResource ) {
+                               proc_close( $this->procOpenResource );
+                               $this->procOpenResource = false;
+                       }
+                       $this->renameOrException( $newname );
+                       if ( $open ) {
+                               $command = $this->command;
+                               $command .= " > " . wfEscapeShellArg( $this->filename );
+                               $this->startCommand( $command );
+                       }
+               }
+       }
+}
diff --git a/includes/export/WikiExporter.php b/includes/export/WikiExporter.php
new file mode 100644 (file)
index 0000000..ab2632d
--- /dev/null
@@ -0,0 +1,469 @@
+<?php
+/**
+ * Base class for exporting
+ *
+ * Copyright © 2003, 2005, 2006 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
+ */
+
+/**
+ * @defgroup Dump Dump
+ */
+
+/**
+ * @ingroup SpecialPage Dump
+ */
+class WikiExporter {
+       /** @var bool Return distinct author list (when not returning full history) */
+       public $list_authors = false;
+
+       /** @var bool */
+       public $dumpUploads = false;
+
+       /** @var bool */
+       public $dumpUploadFileContents = false;
+
+       /** @var string */
+       public $author_list = "";
+
+       const FULL = 1;
+       const CURRENT = 2;
+       const STABLE = 4; // extension defined
+       const LOGS = 8;
+       const RANGE = 16;
+
+       const BUFFER = 0;
+       const STREAM = 1;
+
+       const TEXT = 0;
+       const STUB = 1;
+
+       /** @var int */
+       public $buffer;
+
+       /** @var int */
+       public $text;
+
+       /** @var DumpOutput */
+       public $sink;
+
+       /**
+        * Returns the export schema version.
+        * @return string
+        */
+       public static function schemaVersion() {
+               return "0.10";
+       }
+
+       /**
+        * If using WikiExporter::STREAM to stream a large amount of data,
+        * provide a database connection which is not managed by
+        * LoadBalancer to read from: some history blob types will
+        * make additional queries to pull source data while the
+        * main query is still running.
+        *
+        * @param IDatabase $db
+        * @param int|array $history One of WikiExporter::FULL, WikiExporter::CURRENT,
+        *   WikiExporter::RANGE or WikiExporter::STABLE, or an associative array:
+        *   - offset: non-inclusive offset at which to start the query
+        *   - limit: maximum number of rows to return
+        *   - dir: "asc" or "desc" timestamp order
+        * @param int $buffer One of WikiExporter::BUFFER or WikiExporter::STREAM
+        * @param int $text One of WikiExporter::TEXT or WikiExporter::STUB
+        */
+       function __construct( $db, $history = WikiExporter::CURRENT,
+                       $buffer = WikiExporter::BUFFER, $text = WikiExporter::TEXT ) {
+               $this->db = $db;
+               $this->history = $history;
+               $this->buffer = $buffer;
+               $this->writer = new XmlDumpWriter();
+               $this->sink = new DumpOutput();
+               $this->text = $text;
+       }
+
+       /**
+        * Set the DumpOutput or DumpFilter object which will receive
+        * various row objects and XML output for filtering. Filters
+        * can be chained or used as callbacks.
+        *
+        * @param DumpOutput $sink
+        */
+       public function setOutputSink( &$sink ) {
+               $this->sink =& $sink;
+       }
+
+       public function openStream() {
+               $output = $this->writer->openStream();
+               $this->sink->writeOpenStream( $output );
+       }
+
+       public function closeStream() {
+               $output = $this->writer->closeStream();
+               $this->sink->writeCloseStream( $output );
+       }
+
+       /**
+        * Dumps a series of page and revision records for all pages
+        * in the database, either including complete history or only
+        * the most recent version.
+        */
+       public function allPages() {
+               $this->dumpFrom( '' );
+       }
+
+       /**
+        * Dumps a series of page and revision records for those pages
+        * in the database falling within the page_id range given.
+        * @param int $start Inclusive lower limit (this id is included)
+        * @param int $end Exclusive upper limit (this id is not included)
+        *   If 0, no upper limit.
+        */
+       public function pagesByRange( $start, $end ) {
+               $condition = 'page_id >= ' . intval( $start );
+               if ( $end ) {
+                       $condition .= ' AND page_id < ' . intval( $end );
+               }
+               $this->dumpFrom( $condition );
+       }
+
+       /**
+        * Dumps a series of page and revision records for those pages
+        * in the database with revisions falling within the rev_id range given.
+        * @param int $start Inclusive lower limit (this id is included)
+        * @param int $end Exclusive upper limit (this id is not included)
+        *   If 0, no upper limit.
+        */
+       public function revsByRange( $start, $end ) {
+               $condition = 'rev_id >= ' . intval( $start );
+               if ( $end ) {
+                       $condition .= ' AND rev_id < ' . intval( $end );
+               }
+               $this->dumpFrom( $condition );
+       }
+
+       /**
+        * @param Title $title
+        */
+       public function pageByTitle( $title ) {
+               $this->dumpFrom(
+                       'page_namespace=' . $title->getNamespace() .
+                       ' AND page_title=' . $this->db->addQuotes( $title->getDBkey() ) );
+       }
+
+       /**
+        * @param string $name
+        * @throws MWException
+        */
+       public function pageByName( $name ) {
+               $title = Title::newFromText( $name );
+               if ( is_null( $title ) ) {
+                       throw new MWException( "Can't export invalid title" );
+               } else {
+                       $this->pageByTitle( $title );
+               }
+       }
+
+       /**
+        * @param array $names
+        */
+       public function pagesByName( $names ) {
+               foreach ( $names as $name ) {
+                       $this->pageByName( $name );
+               }
+       }
+
+       public function allLogs() {
+               $this->dumpFrom( '' );
+       }
+
+       /**
+        * @param int $start
+        * @param int $end
+        */
+       public function logsByRange( $start, $end ) {
+               $condition = 'log_id >= ' . intval( $start );
+               if ( $end ) {
+                       $condition .= ' AND log_id < ' . intval( $end );
+               }
+               $this->dumpFrom( $condition );
+       }
+
+       /**
+        * Generates the distinct list of authors of an article
+        * Not called by default (depends on $this->list_authors)
+        * Can be set by Special:Export when not exporting whole history
+        *
+        * @param array $cond
+        */
+       protected function do_list_authors( $cond ) {
+               $this->author_list = "<contributors>";
+               // rev_deleted
+
+               $res = $this->db->select(
+                       array( 'page', 'revision' ),
+                       array( 'DISTINCT rev_user_text', 'rev_user' ),
+                       array(
+                               $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
+                               $cond,
+                               'page_id = rev_id',
+                       ),
+                       __METHOD__
+               );
+
+               foreach ( $res as $row ) {
+                       $this->author_list .= "<contributor>" .
+                               "<username>" .
+                               htmlentities( $row->rev_user_text ) .
+                               "</username>" .
+                               "<id>" .
+                               $row->rev_user .
+                               "</id>" .
+                               "</contributor>";
+               }
+               $this->author_list .= "</contributors>";
+       }
+
+       /**
+        * @param string $cond
+        * @throws MWException
+        * @throws Exception
+        */
+       protected function dumpFrom( $cond = '' ) {
+               # For logging dumps...
+               if ( $this->history & self::LOGS ) {
+                       $where = array( 'user_id = log_user' );
+                       # Hide private logs
+                       $hideLogs = LogEventsList::getExcludeClause( $this->db );
+                       if ( $hideLogs ) {
+                               $where[] = $hideLogs;
+                       }
+                       # Add on any caller specified conditions
+                       if ( $cond ) {
+                               $where[] = $cond;
+                       }
+                       # Get logging table name for logging.* clause
+                       $logging = $this->db->tableName( 'logging' );
+
+                       if ( $this->buffer == WikiExporter::STREAM ) {
+                               $prev = $this->db->bufferResults( false );
+                       }
+                       $result = null; // Assuring $result is not undefined, if exception occurs early
+                       try {
+                               $result = $this->db->select( array( 'logging', 'user' ),
+                                       array( "{$logging}.*", 'user_name' ), // grab the user name
+                                       $where,
+                                       __METHOD__,
+                                       array( 'ORDER BY' => 'log_id', 'USE INDEX' => array( 'logging' => 'PRIMARY' ) )
+                               );
+                               $this->outputLogStream( $result );
+                               if ( $this->buffer == WikiExporter::STREAM ) {
+                                       $this->db->bufferResults( $prev );
+                               }
+                       } catch ( Exception $e ) {
+                               // Throwing the exception does not reliably free the resultset, and
+                               // would also leave the connection in unbuffered mode.
+
+                               // Freeing result
+                               try {
+                                       if ( $result ) {
+                                               $result->free();
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Putting database back in previous buffer mode
+                               try {
+                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                               $this->db->bufferResults( $prev );
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Inform caller about problem
+                               throw $e;
+                       }
+               # For page dumps...
+               } else {
+                       $tables = array( 'page', 'revision' );
+                       $opts = array( 'ORDER BY' => 'page_id ASC' );
+                       $opts['USE INDEX'] = array();
+                       $join = array();
+                       if ( is_array( $this->history ) ) {
+                               # Time offset/limit for all pages/history...
+                               $revJoin = 'page_id=rev_page';
+                               # Set time order
+                               if ( $this->history['dir'] == 'asc' ) {
+                                       $op = '>';
+                                       $opts['ORDER BY'] = 'rev_timestamp ASC';
+                               } else {
+                                       $op = '<';
+                                       $opts['ORDER BY'] = 'rev_timestamp DESC';
+                               }
+                               # Set offset
+                               if ( !empty( $this->history['offset'] ) ) {
+                                       $revJoin .= " AND rev_timestamp $op " .
+                                               $this->db->addQuotes( $this->db->timestamp( $this->history['offset'] ) );
+                               }
+                               $join['revision'] = array( 'INNER JOIN', $revJoin );
+                               # Set query limit
+                               if ( !empty( $this->history['limit'] ) ) {
+                                       $opts['LIMIT'] = intval( $this->history['limit'] );
+                               }
+                       } elseif ( $this->history & WikiExporter::FULL ) {
+                               # Full history dumps...
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
+                       } elseif ( $this->history & WikiExporter::CURRENT ) {
+                               # Latest revision dumps...
+                               if ( $this->list_authors && $cond != '' ) { // List authors, if so desired
+                                       $this->do_list_authors( $cond );
+                               }
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
+                       } elseif ( $this->history & WikiExporter::STABLE ) {
+                               # "Stable" revision dumps...
+                               # Default JOIN, to be overridden...
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page AND page_latest=rev_id' );
+                               # One, and only one hook should set this, and return false
+                               if ( Hooks::run( 'WikiExporter::dumpStableQuery', array( &$tables, &$opts, &$join ) ) ) {
+                                       throw new MWException( __METHOD__ . " given invalid history dump type." );
+                               }
+                       } elseif ( $this->history & WikiExporter::RANGE ) {
+                               # Dump of revisions within a specified range
+                               $join['revision'] = array( 'INNER JOIN', 'page_id=rev_page' );
+                               $opts['ORDER BY'] = array( 'rev_page ASC', 'rev_id ASC' );
+                       } else {
+                               # Unknown history specification parameter?
+                               throw new MWException( __METHOD__ . " given invalid history dump type." );
+                       }
+                       # Query optimization hacks
+                       if ( $cond == '' ) {
+                               $opts[] = 'STRAIGHT_JOIN';
+                               $opts['USE INDEX']['page'] = 'PRIMARY';
+                       }
+                       # Build text join options
+                       if ( $this->text != WikiExporter::STUB ) { // 1-pass
+                               $tables[] = 'text';
+                               $join['text'] = array( 'INNER JOIN', 'rev_text_id=old_id' );
+                       }
+
+                       if ( $this->buffer == WikiExporter::STREAM ) {
+                               $prev = $this->db->bufferResults( false );
+                       }
+
+                       $result = null; // Assuring $result is not undefined, if exception occurs early
+                       try {
+                               Hooks::run( 'ModifyExportQuery',
+                                               array( $this->db, &$tables, &$cond, &$opts, &$join ) );
+
+                               # Do the query!
+                               $result = $this->db->select( $tables, '*', $cond, __METHOD__, $opts, $join );
+                               # Output dump results
+                               $this->outputPageStream( $result );
+
+                               if ( $this->buffer == WikiExporter::STREAM ) {
+                                       $this->db->bufferResults( $prev );
+                               }
+                       } catch ( Exception $e ) {
+                               // Throwing the exception does not reliably free the resultset, and
+                               // would also leave the connection in unbuffered mode.
+
+                               // Freeing result
+                               try {
+                                       if ( $result ) {
+                                               $result->free();
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Putting database back in previous buffer mode
+                               try {
+                                       if ( $this->buffer == WikiExporter::STREAM ) {
+                                               $this->db->bufferResults( $prev );
+                                       }
+                               } catch ( Exception $e2 ) {
+                                       // Already in panic mode -> ignoring $e2 as $e has
+                                       // higher priority
+                               }
+
+                               // Inform caller about problem
+                               throw $e;
+                       }
+               }
+       }
+
+       /**
+        * Runs through a query result set dumping page and revision records.
+        * The result set should be sorted/grouped by page to avoid duplicate
+        * page records in the output.
+        *
+        * Should be safe for
+        * streaming (non-buffered) queries, as long as it was made on a
+        * separate database connection not managed by LoadBalancer; some
+        * blob storage types will make queries to pull source data.
+        *
+        * @param ResultWrapper $resultset
+        */
+       protected function outputPageStream( $resultset ) {
+               $last = null;
+               foreach ( $resultset as $row ) {
+                       if ( $last === null ||
+                               $last->page_namespace != $row->page_namespace ||
+                               $last->page_title != $row->page_title ) {
+                               if ( $last !== null ) {
+                                       $output = '';
+                                       if ( $this->dumpUploads ) {
+                                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
+                                       }
+                                       $output .= $this->writer->closePage();
+                                       $this->sink->writeClosePage( $output );
+                               }
+                               $output = $this->writer->openPage( $row );
+                               $this->sink->writeOpenPage( $row, $output );
+                               $last = $row;
+                       }
+                       $output = $this->writer->writeRevision( $row );
+                       $this->sink->writeRevision( $row, $output );
+               }
+               if ( $last !== null ) {
+                       $output = '';
+                       if ( $this->dumpUploads ) {
+                               $output .= $this->writer->writeUploads( $last, $this->dumpUploadFileContents );
+                       }
+                       $output .= $this->author_list;
+                       $output .= $this->writer->closePage();
+                       $this->sink->writeClosePage( $output );
+               }
+       }
+
+       /**
+        * @param ResultWrapper $resultset
+        */
+       protected function outputLogStream( $resultset ) {
+               foreach ( $resultset as $row ) {
+                       $output = $this->writer->writeLogItem( $row );
+                       $this->sink->writeLogItem( $row, $output );
+               }
+       }
+}
diff --git a/includes/export/XmlDumpWriter.php b/includes/export/XmlDumpWriter.php
new file mode 100644 (file)
index 0000000..3bd4c96
--- /dev/null
@@ -0,0 +1,440 @@
+<?php
+/**
+ * XmlDumpWriter
+ *
+ * Copyright © 2003, 2005, 2006 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 Dump
+ */
+class XmlDumpWriter {
+       /**
+        * Opens the XML output stream's root "<mediawiki>" element.
+        * This does not include an xml directive, so is safe to include
+        * as a subelement in a larger XML stream. Namespace and XML Schema
+        * references are included.
+        *
+        * Output will be encoded in UTF-8.
+        *
+        * @return string
+        */
+       function openStream() {
+               global $wgLanguageCode;
+               $ver = WikiExporter::schemaVersion();
+               return Xml::element( 'mediawiki', array(
+                       'xmlns'              => "http://www.mediawiki.org/xml/export-$ver/",
+                       'xmlns:xsi'          => "http://www.w3.org/2001/XMLSchema-instance",
+                       /*
+                        * When a new version of the schema is created, it needs staging on mediawiki.org.
+                        * This requires a change in the operations/mediawiki-config git repo.
+                        *
+                        * Create a changeset like https://gerrit.wikimedia.org/r/#/c/149643/ in which
+                        * you copy in the new xsd file.
+                        *
+                        * After it is reviewed, merged and deployed (sync-docroot), the index.html needs purging.
+                        * echo "http://www.mediawiki.org/xml/index.html" | mwscript purgeList.php --wiki=aawiki
+                        */
+                       'xsi:schemaLocation' => "http://www.mediawiki.org/xml/export-$ver/ " .
+                               "http://www.mediawiki.org/xml/export-$ver.xsd",
+                       'version'            => $ver,
+                       'xml:lang'           => $wgLanguageCode ),
+                       null ) .
+                       "\n" .
+                       $this->siteInfo();
+       }
+
+       /**
+        * @return string
+        */
+       function siteInfo() {
+               $info = array(
+                       $this->sitename(),
+                       $this->dbname(),
+                       $this->homelink(),
+                       $this->generator(),
+                       $this->caseSetting(),
+                       $this->namespaces() );
+               return "  <siteinfo>\n    " .
+                       implode( "\n    ", $info ) .
+                       "\n  </siteinfo>\n";
+       }
+
+       /**
+        * @return string
+        */
+       function sitename() {
+               global $wgSitename;
+               return Xml::element( 'sitename', array(), $wgSitename );
+       }
+
+       /**
+        * @return string
+        */
+       function dbname() {
+               global $wgDBname;
+               return Xml::element( 'dbname', array(), $wgDBname );
+       }
+
+       /**
+        * @return string
+        */
+       function generator() {
+               global $wgVersion;
+               return Xml::element( 'generator', array(), "MediaWiki $wgVersion" );
+       }
+
+       /**
+        * @return string
+        */
+       function homelink() {
+               return Xml::element( 'base', array(), Title::newMainPage()->getCanonicalURL() );
+       }
+
+       /**
+        * @return string
+        */
+       function caseSetting() {
+               global $wgCapitalLinks;
+               // "case-insensitive" option is reserved for future
+               $sensitivity = $wgCapitalLinks ? 'first-letter' : 'case-sensitive';
+               return Xml::element( 'case', array(), $sensitivity );
+       }
+
+       /**
+        * @return string
+        */
+       function namespaces() {
+               global $wgContLang;
+               $spaces = "<namespaces>\n";
+               foreach ( $wgContLang->getFormattedNamespaces() as $ns => $title ) {
+                       $spaces .= '      ' .
+                               Xml::element( 'namespace',
+                                       array(
+                                               'key' => $ns,
+                                               'case' => MWNamespace::isCapitalized( $ns ) ? 'first-letter' : 'case-sensitive',
+                                       ), $title ) . "\n";
+               }
+               $spaces .= "    </namespaces>";
+               return $spaces;
+       }
+
+       /**
+        * Closes the output stream with the closing root element.
+        * Call when finished dumping things.
+        *
+        * @return string
+        */
+       function closeStream() {
+               return "</mediawiki>\n";
+       }
+
+       /**
+        * Opens a "<page>" section on the output stream, with data
+        * from the given database row.
+        *
+        * @param object $row
+        * @return string
+        */
+       public function openPage( $row ) {
+               $out = "  <page>\n";
+               $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+               $out .= '    ' . Xml::elementClean( 'title', array(), self::canonicalTitle( $title ) ) . "\n";
+               $out .= '    ' . Xml::element( 'ns', array(), strval( $row->page_namespace ) ) . "\n";
+               $out .= '    ' . Xml::element( 'id', array(), strval( $row->page_id ) ) . "\n";
+               if ( $row->page_is_redirect ) {
+                       $page = WikiPage::factory( $title );
+                       $redirect = $page->getRedirectTarget();
+                       if ( $redirect instanceof Title && $redirect->isValidRedirectTarget() ) {
+                               $out .= '    ';
+                               $out .= Xml::element( 'redirect', array( 'title' => self::canonicalTitle( $redirect ) ) );
+                               $out .= "\n";
+                       }
+               }
+
+               if ( $row->page_restrictions != '' ) {
+                       $out .= '    ' . Xml::element( 'restrictions', array(),
+                               strval( $row->page_restrictions ) ) . "\n";
+               }
+
+               Hooks::run( 'XmlDumpWriterOpenPage', array( $this, &$out, $row, $title ) );
+
+               return $out;
+       }
+
+       /**
+        * Closes a "<page>" section on the output stream.
+        *
+        * @access private
+        * @return string
+        */
+       function closePage() {
+               return "  </page>\n";
+       }
+
+       /**
+        * Dumps a "<revision>" section on the output stream, with
+        * data filled in from the given database row.
+        *
+        * @param object $row
+        * @return string
+        * @access private
+        */
+       function writeRevision( $row ) {
+
+               $out = "    <revision>\n";
+               $out .= "      " . Xml::element( 'id', null, strval( $row->rev_id ) ) . "\n";
+               if ( isset( $row->rev_parent_id ) && $row->rev_parent_id ) {
+                       $out .= "      " . Xml::element( 'parentid', null, strval( $row->rev_parent_id ) ) . "\n";
+               }
+
+               $out .= $this->writeTimestamp( $row->rev_timestamp );
+
+               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_USER ) ) {
+                       $out .= "      " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+               } else {
+                       $out .= $this->writeContributor( $row->rev_user, $row->rev_user_text );
+               }
+
+               if ( isset( $row->rev_minor_edit ) && $row->rev_minor_edit ) {
+                       $out .= "      <minor/>\n";
+               }
+               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_COMMENT ) ) {
+                       $out .= "      " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+               } elseif ( $row->rev_comment != '' ) {
+                       $out .= "      " . Xml::elementClean( 'comment', array(), strval( $row->rev_comment ) ) . "\n";
+               }
+
+               if ( isset( $row->rev_content_model ) && !is_null( $row->rev_content_model ) ) {
+                       $content_model = strval( $row->rev_content_model );
+               } else {
+                       // probably using $wgContentHandlerUseDB = false;
+                       $title = Title::makeTitle( $row->page_namespace, $row->page_title );
+                       $content_model = ContentHandler::getDefaultModelFor( $title );
+               }
+
+               $content_handler = ContentHandler::getForModelID( $content_model );
+
+               if ( isset( $row->rev_content_format ) && !is_null( $row->rev_content_format ) ) {
+                       $content_format = strval( $row->rev_content_format );
+               } else {
+                       // probably using $wgContentHandlerUseDB = false;
+                       $content_format = $content_handler->getDefaultFormat();
+               }
+
+               $out .= "      " . Xml::element( 'model', null, strval( $content_model ) ) . "\n";
+               $out .= "      " . Xml::element( 'format', null, strval( $content_format ) ) . "\n";
+
+               $text = '';
+               if ( isset( $row->rev_deleted ) && ( $row->rev_deleted & Revision::DELETED_TEXT ) ) {
+                       $out .= "      " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+               } elseif ( isset( $row->old_text ) ) {
+                       // Raw text from the database may have invalid chars
+                       $text = strval( Revision::getRevisionText( $row ) );
+                       $text = $content_handler->exportTransform( $text, $content_format );
+                       $out .= "      " . Xml::elementClean( 'text',
+                               array( 'xml:space' => 'preserve', 'bytes' => intval( $row->rev_len ) ),
+                               strval( $text ) ) . "\n";
+               } else {
+                       // Stub output
+                       $out .= "      " . Xml::element( 'text',
+                               array( 'id' => $row->rev_text_id, 'bytes' => intval( $row->rev_len ) ),
+                               "" ) . "\n";
+               }
+
+               if ( isset( $row->rev_sha1 )
+                       && $row->rev_sha1
+                       && !( $row->rev_deleted & Revision::DELETED_TEXT )
+               ) {
+                       $out .= "      " . Xml::element( 'sha1', null, strval( $row->rev_sha1 ) ) . "\n";
+               } else {
+                       $out .= "      <sha1/>\n";
+               }
+
+               Hooks::run( 'XmlDumpWriterWriteRevision', array( &$this, &$out, $row, $text ) );
+
+               $out .= "    </revision>\n";
+
+               return $out;
+       }
+
+       /**
+        * Dumps a "<logitem>" section on the output stream, with
+        * data filled in from the given database row.
+        *
+        * @param object $row
+        * @return string
+        * @access private
+        */
+       function writeLogItem( $row ) {
+
+               $out = "  <logitem>\n";
+               $out .= "    " . Xml::element( 'id', null, strval( $row->log_id ) ) . "\n";
+
+               $out .= $this->writeTimestamp( $row->log_timestamp, "    " );
+
+               if ( $row->log_deleted & LogPage::DELETED_USER ) {
+                       $out .= "    " . Xml::element( 'contributor', array( 'deleted' => 'deleted' ) ) . "\n";
+               } else {
+                       $out .= $this->writeContributor( $row->log_user, $row->user_name, "    " );
+               }
+
+               if ( $row->log_deleted & LogPage::DELETED_COMMENT ) {
+                       $out .= "    " . Xml::element( 'comment', array( 'deleted' => 'deleted' ) ) . "\n";
+               } elseif ( $row->log_comment != '' ) {
+                       $out .= "    " . Xml::elementClean( 'comment', null, strval( $row->log_comment ) ) . "\n";
+               }
+
+               $out .= "    " . Xml::element( 'type', null, strval( $row->log_type ) ) . "\n";
+               $out .= "    " . Xml::element( 'action', null, strval( $row->log_action ) ) . "\n";
+
+               if ( $row->log_deleted & LogPage::DELETED_ACTION ) {
+                       $out .= "    " . Xml::element( 'text', array( 'deleted' => 'deleted' ) ) . "\n";
+               } else {
+                       $title = Title::makeTitle( $row->log_namespace, $row->log_title );
+                       $out .= "    " . Xml::elementClean( 'logtitle', null, self::canonicalTitle( $title ) ) . "\n";
+                       $out .= "    " . Xml::elementClean( 'params',
+                               array( 'xml:space' => 'preserve' ),
+                               strval( $row->log_params ) ) . "\n";
+               }
+
+               $out .= "  </logitem>\n";
+
+               return $out;
+       }
+
+       /**
+        * @param string $timestamp
+        * @param string $indent Default to six spaces
+        * @return string
+        */
+       function writeTimestamp( $timestamp, $indent = "      " ) {
+               $ts = wfTimestamp( TS_ISO_8601, $timestamp );
+               return $indent . Xml::element( 'timestamp', null, $ts ) . "\n";
+       }
+
+       /**
+        * @param int $id
+        * @param string $text
+        * @param string $indent Default to six spaces
+        * @return string
+        */
+       function writeContributor( $id, $text, $indent = "      " ) {
+               $out = $indent . "<contributor>\n";
+               if ( $id || !IP::isValid( $text ) ) {
+                       $out .= $indent . "  " . Xml::elementClean( 'username', null, strval( $text ) ) . "\n";
+                       $out .= $indent . "  " . Xml::element( 'id', null, strval( $id ) ) . "\n";
+               } else {
+                       $out .= $indent . "  " . Xml::elementClean( 'ip', null, strval( $text ) ) . "\n";
+               }
+               $out .= $indent . "</contributor>\n";
+               return $out;
+       }
+
+       /**
+        * Warning! This data is potentially inconsistent. :(
+        * @param object $row
+        * @param bool $dumpContents
+        * @return string
+        */
+       function writeUploads( $row, $dumpContents = false ) {
+               if ( $row->page_namespace == NS_FILE ) {
+                       $img = wfLocalFile( $row->page_title );
+                       if ( $img && $img->exists() ) {
+                               $out = '';
+                               foreach ( array_reverse( $img->getHistory() ) as $ver ) {
+                                       $out .= $this->writeUpload( $ver, $dumpContents );
+                               }
+                               $out .= $this->writeUpload( $img, $dumpContents );
+                               return $out;
+                       }
+               }
+               return '';
+       }
+
+       /**
+        * @param File $file
+        * @param bool $dumpContents
+        * @return string
+        */
+       function writeUpload( $file, $dumpContents = false ) {
+               if ( $file->isOld() ) {
+                       $archiveName = "      " .
+                               Xml::element( 'archivename', null, $file->getArchiveName() ) . "\n";
+               } else {
+                       $archiveName = '';
+               }
+               if ( $dumpContents ) {
+                       $be = $file->getRepo()->getBackend();
+                       # Dump file as base64
+                       # Uses only XML-safe characters, so does not need escaping
+                       # @todo Too bad this loads the contents into memory (script might swap)
+                       $contents = '      <contents encoding="base64">' .
+                               chunk_split( base64_encode(
+                                       $be->getFileContents( array( 'src' => $file->getPath() ) ) ) ) .
+                               "      </contents>\n";
+               } else {
+                       $contents = '';
+               }
+               if ( $file->isDeleted( File::DELETED_COMMENT ) ) {
+                       $comment = Xml::element( 'comment', array( 'deleted' => 'deleted' ) );
+               } else {
+                       $comment = Xml::elementClean( 'comment', null, $file->getDescription() );
+               }
+               return "    <upload>\n" .
+                       $this->writeTimestamp( $file->getTimestamp() ) .
+                       $this->writeContributor( $file->getUser( 'id' ), $file->getUser( 'text' ) ) .
+                       "      " . $comment . "\n" .
+                       "      " . Xml::element( 'filename', null, $file->getName() ) . "\n" .
+                       $archiveName .
+                       "      " . Xml::element( 'src', null, $file->getCanonicalURL() ) . "\n" .
+                       "      " . Xml::element( 'size', null, $file->getSize() ) . "\n" .
+                       "      " . Xml::element( 'sha1base36', null, $file->getSha1() ) . "\n" .
+                       "      " . Xml::element( 'rel', null, $file->getRel() ) . "\n" .
+                       $contents .
+                       "    </upload>\n";
+       }
+
+       /**
+        * Return prefixed text form of title, but using the content language's
+        * canonical namespace. This skips any special-casing such as gendered
+        * user namespaces -- which while useful, are not yet listed in the
+        * XML "<siteinfo>" data so are unsafe in export.
+        *
+        * @param Title $title
+        * @return string
+        * @since 1.18
+        */
+       public static function canonicalTitle( Title $title ) {
+               if ( $title->isExternal() ) {
+                       return $title->getPrefixedText();
+               }
+
+               global $wgContLang;
+               $prefix = $wgContLang->getFormattedNsText( $title->getNamespace() );
+
+               if ( $prefix !== '' ) {
+                       $prefix .= ':';
+               }
+
+               return $prefix . $title->getText();
+       }
+}
index 07370ad..f11c218 100644 (file)
@@ -64,6 +64,7 @@ class FSFileBackend extends FileBackendStore {
         *   - containerPaths : Map of container names to custom file system directories.
         *                      This should only be used for backwards-compatibility.
         *   - fileMode       : Octal UNIX file permissions to use on files stored.
+        * @param array $config
         */
        public function __construct( array $config ) {
                parent::__construct( $config );
@@ -561,9 +562,6 @@ class FSFileBackend extends FileBackendStore {
                }
        }
 
-       /**
-        * @see FileBackendStore::doClearCache()
-        */
        protected function doClearCache( array $paths = null ) {
                clearstatcache(); // clear the PHP file stat cache
        }
@@ -682,7 +680,7 @@ class FSFileBackend extends FileBackendStore {
        }
 
        /**
-        * @param FileBackendStoreOpHandle[] $fileOpHandles
+        * @param FSFileOpHandle[] $fileOpHandles
         *
         * @return Status[]
         */
index faa1314..95a7897 100644 (file)
@@ -49,7 +49,7 @@ class FileOpBatch {
         *   - a) unexpected operation errors occurred (network partitions, disk full...)
         *   - b) significant operation errors occurred and 'force' was not set
         *
-        * @param array $performOps List of FileOp operations
+        * @param FileOp[] $performOps List of FileOp operations
         * @param array $opts Batch operation options
         * @param FileJournal $journal Journal to log operations to
         * @return Status
@@ -147,6 +147,7 @@ class FileOpBatch {
        protected static function runParallelBatches( array $pPerformOps, Status $status ) {
                $aborted = false; // set to true on unexpected errors
                foreach ( $pPerformOps as $performOpsBatch ) {
+                       /** @var FileOp[] $performOpsBatch */
                        if ( $aborted ) { // check batch op abort flag...
                                // We can't continue (even with $ignoreErrors) as $predicates is wrong.
                                // Log the remaining ops as failed for recovery...
@@ -157,6 +158,7 @@ class FileOpBatch {
                                }
                                continue;
                        }
+                       /** @var Status[] $statuses */
                        $statuses = array();
                        $opHandles = array();
                        // Get the backend; all sub-batch ops belong to a single backend
index 15007af..8ba2745 100644 (file)
@@ -603,16 +603,6 @@ class FileRepo {
                return array();
        }
 
-       /**
-        * Get the public root URL of the repository
-        *
-        * @deprecated since 1.20
-        * @return string
-        */
-       public function getRootUrl() {
-               return $this->getZoneUrl( 'public' );
-       }
-
        /**
         * Get the URL of thumb.php
         *
index f898bb6..a8d37a1 100644 (file)
@@ -218,7 +218,11 @@ class ForeignAPIRepo extends FileRepo {
                if ( $data && isset( $data['query']['pages'] ) ) {
                        foreach ( $data['query']['pages'] as $info ) {
                                if ( isset( $info['imageinfo'][0] ) ) {
-                                       return $info['imageinfo'][0];
+                                       $return = $info['imageinfo'][0];
+                                       if ( isset( $info['pageid'] ) ) {
+                                               $return['pageid'] = $info['pageid'];
+                                       }
+                                       return $return;
                                }
                        }
                }
@@ -499,7 +503,7 @@ class ForeignAPIRepo extends FileRepo {
 
        /**
         * Like a Http:get request, but with custom User-Agent.
-        * @see Http:get
+        * @see Http::get
         * @param string $url
         * @param string $timeout
         * @param array $options
index 72f12d1..8a3900e 100644 (file)
@@ -354,6 +354,16 @@ abstract class File implements IDBAccessObject {
                return $this->url;
        }
 
+       /*
+        * Get short description URL for a files based on the page ID
+        *
+        * @return string|null
+        * @since 1.27
+        */
+       public function getDescriptionShortUrl() {
+               return null;
+       }
+
        /**
         * Return a fully-qualified URL to the file.
         * Upload URL paths _may or may not_ be fully qualified, so
index 26ea38b..43cb5a5 100644 (file)
@@ -218,6 +218,25 @@ class ForeignAPIFile extends File {
                return isset( $this->mInfo['url'] ) ? strval( $this->mInfo['url'] ) : null;
        }
 
+       /**
+        * Get short description URL for a file based on the foreign API response,
+        * or if unavailable, the short URL is constructed from the foreign page ID.
+        *
+        * @return null|string
+        * @since 1.27
+        */
+       public function getDescriptionShortUrl() {
+               if ( isset( $this->mInfo['descriptionshorturl'] ) ) {
+                       return $this->mInfo['descriptionshorturl'];
+               } elseif ( isset( $this->mInfo['pageid'] ) ) {
+                       $url = $this->repo->makeUrl( array( 'curid' => $this->mInfo['pageid'] ) );
+                       if ( $url !== false ) {
+                               return $url;
+                       }
+               }
+               return null;
+       }
+
        /**
         * @param string $type
         * @return int|null|string
index 561ead7..611ae10 100644 (file)
@@ -127,4 +127,28 @@ class ForeignDBFile extends LocalFile {
                // Restore remote behavior
                return File::getDescriptionText( $lang );
        }
+
+       /**
+        * Get short description URL for a file based on the page ID.
+        *
+        * @return string
+        * @throws DBUnexpectedError
+        * @since 1.27
+        */
+       public function getDescriptionShortUrl() {
+               $dbr = $this->repo->getSlaveDB();
+               $pageId = $dbr->selectField( 'page', 'page_id', array(
+                       'page_namespace' => NS_FILE,
+                       'page_title' => $this->title->getDBkey()
+               ) );
+
+               if ( $pageId !== false ) {
+                       $url = $this->repo->makeUrl( array( 'curid' => $pageId ) );
+                       if ( $url !== false ) {
+                               return $url;
+                       }
+               }
+               return null;
+       }
+
 }
index 0da1ae8..9e214f6 100644 (file)
@@ -757,6 +757,25 @@ class LocalFile extends File {
                }
        }
 
+       /**
+        * Get short description URL for a file based on the page ID.
+        *
+        * @return string|null
+        * @throws MWException
+        * @since 1.27
+        */
+       public function getDescriptionShortUrl() {
+               $pageId = $this->title->getArticleID();
+
+               if ( $pageId !== null ) {
+                       $url = $this->repo->makeUrl( array( 'curid' => $pageId ) );
+                       if ( $url !== false ) {
+                               return $url;
+                       }
+               }
+               return null;
+       }
+
        /**
         * Get handler-specific metadata
         * @return string
@@ -1330,6 +1349,7 @@ class LocalFile extends File {
                }
 
                $descTitle = $this->getTitle();
+               $descId = $descTitle->getArticleID();
                $wikiPage = new WikiFilePage( $descTitle );
                $wikiPage->setFile( $this );
 
@@ -1362,7 +1382,7 @@ class LocalFile extends File {
 
                        $nullRevision = Revision::newNullRevision(
                                $dbw,
-                               $descTitle->getArticleID(),
+                               $descId,
                                $editSummary,
                                false,
                                $user
@@ -1374,6 +1394,8 @@ class LocalFile extends File {
                                        array( $wikiPage, $nullRevision, $nullRevision->getParentId(), $user )
                                );
                                $wikiPage->updateRevisionOn( $dbw, $nullRevision );
+                               // Associate null revision id
+                               $logEntry->setAssociatedRevId( $nullRevision->getId() );
                        }
 
                        $newPageContent = null;
@@ -1391,11 +1413,12 @@ class LocalFile extends File {
                # 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
+                       $that, $reupload, $wikiPage, $newPageContent, $comment, $user, $logEntry, $logId, $descId
                ) {
                        # 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
@@ -1408,25 +1431,51 @@ class LocalFile extends File {
                                        $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'];
-                                       $that->getRepo()->getMasterDB()->update(
-                                               'logging',
-                                               array( 'log_page' => $rev->getPage() ),
-                                               array( 'log_id' => $logId ),
-                                               __METHOD__
-                                       );
+                                       $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 = array( '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,
+                               array( 'log_id' => $logId ),
+                               __METHOD__
+                       );
+                       $that->getRepo()->getMasterDB()->insert(
+                               'log_search',
+                               array(
+                                       'ls_field' => 'associated_rev_id',
+                                       'ls_value' => $logEntry->getAssociatedRevId(),
+                                       'ls_log_id' => $logId,
+                               ),
+                               __METHOD__
+                       );
 
-                       # Now that the page exists, make an RC entry.
+                       # 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', array( $that, $reupload, !$newPageContent ) );
index 1d45e5f..b4849e5 100644 (file)
@@ -5,10 +5,24 @@
  * 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 = array();
@@ -18,6 +32,30 @@ class HTMLButtonField extends HTMLFormField {
                if ( isset( $info['flags'] ) ) {
                        $this->mFlags = $info['flags'];
                }
+
+               # Generate the label from a message, if possible
+               if ( isset( $info['buttonlabel-message'] ) ) {
+                       $msgInfo = $info['buttonlabel-message'];
+
+                       if ( is_array( $msgInfo ) ) {
+                               $msg = array_shift( $msgInfo );
+                       } else {
+                               $msg = $msgInfo;
+                               $msgInfo = array();
+                       }
+
+                       $this->buttonLabel = $this->msg( $msg, $msgInfo )->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'];
+               }
+
                parent::__construct( $info );
        }
 
@@ -37,9 +75,16 @@ class HTMLButtonField extends HTMLFormField {
                $attr = array(
                        'class' => 'mw-htmlform-submit ' . $this->mClass . $flags,
                        'id' => $this->mID,
+                       'type' => $this->buttonType,
+                       'name' => $this->mName,
+                       'value' => $value,
                ) + $this->getAttributes( array( 'disabled', 'tabindex' ) );
 
-               return Html::input( $this->mName, $value, $this->buttonType, $attr );
+               if ( $this->isBadIE() ) {
+                       return Html::element( 'input', $attr );
+               } else {
+                       return Html::rawElement( 'button', $attr, $this->buttonLabel ?: htmlspecialchars( $value ) );
+               }
        }
 
        /**
@@ -51,11 +96,14 @@ class HTMLButtonField extends HTMLFormField {
                return new OOUI\ButtonInputWidget( array(
                        'name' => $this->mName,
                        'value' => $value,
-                       'label' => $value,
+                       'label' => !$this->isBadIE() && $this->buttonLabel
+                               ? new OOUI\HtmlSnippet( $this->buttonLabel )
+                               : $value,
                        'type' => $this->buttonType,
                        'classes' => array( 'mw-htmlform-submit', $this->mClass ),
                        'id' => $this->mID,
                        'flags' => $this->mFlags,
+                       'useInputTag' => $this->isBadIE(),
                ) + $this->getAttributes( array( 'disabled', 'tabindex' ), array( 'tabindex' => 'tabIndex' ) ) );
        }
 
@@ -74,4 +122,15 @@ class HTMLButtonField extends HTMLFormField {
        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' ) );
+       }
 }
index b0d90af..2282dc2 100644 (file)
@@ -526,6 +526,21 @@ class HTMLForm extends ContextSource {
                return false;
        }
 
+       /**
+        * Same as self::show with the difference, that the form will be
+        * added to the output, no matter, if the validation was good or not.
+        * @return bool|Status Whether submission was successful.
+        */
+       function showAlways() {
+               $this->prepareForm();
+
+               $result = $this->tryAuthorizedSubmit();
+
+               $this->displayForm( $result );
+
+               return $result;
+       }
+
        /**
         * Validate all the fields, and call the submission callback
         * function if everything is kosher.
@@ -656,10 +671,10 @@ class HTMLForm extends ContextSource {
        }
 
        /**
-        * Set the introductory message, overwriting any existing message.
+        * Set the introductory message HTML, overwriting any existing message.
         * @since 1.19
         *
-        * @param string $msg Complete text of message to display
+        * @param string $msg Complete HTML of message to display
         *
         * @return HTMLForm $this for chaining calls (since 1.20)
         */
@@ -670,9 +685,9 @@ class HTMLForm extends ContextSource {
        }
 
        /**
-        * Add introductory text.
+        * Add HTML to introductory message.
         *
-        * @param string $msg Complete text of message to display
+        * @param string $msg Complete HTML of message to display
         *
         * @return HTMLForm $this for chaining calls (since 1.20)
         */
@@ -683,9 +698,9 @@ class HTMLForm extends ContextSource {
        }
 
        /**
-        * Add header text, inside the form.
+        * Add HTML to the header, inside the form.
         *
-        * @param string $msg Complete text of message to display
+        * @param string $msg Additional HTML to display in header
         * @param string|null $section The section to add the header to
         *
         * @return HTMLForm $this for chaining calls (since 1.20)
@@ -707,7 +722,7 @@ class HTMLForm extends ContextSource {
         * Set header text, inside the form.
         * @since 1.19
         *
-        * @param string $msg Complete text of message to display
+        * @param string $msg Complete HTML of header to display
         * @param string|null $section The section to add the header to
         *
         * @return HTMLForm $this for chaining calls (since 1.20)
@@ -727,7 +742,7 @@ class HTMLForm extends ContextSource {
         *
         * @param string|null $section The section to get the header text for
         * @since 1.26
-        * @return string
+        * @return string HTML
         */
        function getHeaderText( $section = null ) {
                if ( is_null( $section ) ) {
@@ -855,15 +870,53 @@ class HTMLForm extends ContextSource {
        /**
         * Add a button to the form
         *
-        * @param string $name Field name.
-        * @param string $value Field value
-        * @param string $id DOM id for the button (default: null)
-        * @param array $attribs
-        *
+        * @since 1.27 takes an array as shown. Earlier versions accepted
+        *  'name', 'value', 'id', and 'attribs' as separate parameters in that
+        *  order.
+        * @note Custom labels ('label', 'label-message', 'label-raw') are not
+        *  supported for IE6 and IE7 due to bugs in those browsers. If detected,
+        *  they will be served buttons using 'value' as the button label.
+        * @param array $data Data to define the button:
+        *  - name: (string) Button name.
+        *  - value: (string) Button value.
+        *  - label-message: (string, optional) Button label message key to use
+        *    instead of 'value'. Overrides 'label' and 'label-raw'.
+        *  - label: (string, optional) Button label text to use instead of
+        *    'value'. Overrides 'label-raw'.
+        *  - label-raw: (string, optional) Button label HTML to use instead of
+        *    'value'.
+        *  - id: (string, optional) DOM id for the button.
+        *  - attribs: (array, optional) Additional HTML attributes.
+        *  - flags: (string|string[], optional) OOUI flags.
         * @return HTMLForm $this for chaining calls (since 1.20)
         */
-       public function addButton( $name, $value, $id = null, $attribs = null ) {
-               $this->mButtons[] = compact( 'name', 'value', 'id', 'attribs' );
+       public function addButton( $data ) {
+               if ( !is_array( $data ) ) {
+                       $args = func_get_args();
+                       if ( count( $args ) < 2 || count( $args ) > 4 ) {
+                               throw new InvalidArgumentException(
+                                       'Incorrect number of arguments for deprecated calling style'
+                               );
+                       }
+                       $data = array(
+                               'name' => $args[0],
+                               'value' => $args[1],
+                               'id' => isset( $args[2] ) ? $args[2] : null,
+                               'attribs' => isset( $args[3] ) ? $args[3] : null,
+                       );
+               } else {
+                       if ( !isset( $data['name'] ) ) {
+                               throw new InvalidArgumentException( 'A name is required' );
+                       }
+                       if ( !isset( $data['value'] ) ) {
+                               throw new InvalidArgumentException( 'A value is required' );
+                       }
+               }
+               $this->mButtons[] = $data + array(
+                       'id' => null,
+                       'attribs' => null,
+                       'flags' => null,
+               );
 
                return $this;
        }
@@ -904,7 +957,7 @@ class HTMLForm extends ContextSource {
         *
         * @param bool|string|array|Status $submitResult Output from HTMLForm::trySubmit()
         *
-        * @return string
+        * @return string HTML
         */
        function getHTML( $submitResult ) {
                # For good measure (it is the default)
@@ -1041,6 +1094,9 @@ class HTMLForm extends ContextSource {
                        ) . "\n";
                }
 
+               // IE<8 has bugs with <button>, so we'll need to avoid them.
+               $isBadIE = preg_match( '/MSIE [1-7]\./i', $this->getRequest()->getHeader( 'User-Agent' ) );
+
                foreach ( $this->mButtons as $button ) {
                        $attrs = array(
                                'type' => 'submit',
@@ -1048,6 +1104,16 @@ class HTMLForm extends ContextSource {
                                'value' => $button['value']
                        );
 
+                       if ( isset( $button['label-message'] ) ) {
+                               $label = $this->msg( $button['label-message'] )->parse();
+                       } elseif ( isset( $button['label'] ) ) {
+                               $label = htmlspecialchars( $button['label'] );
+                       } elseif ( isset( $button['label-raw'] ) ) {
+                               $label = $button['label-raw'];
+                       } else {
+                               $label = htmlspecialchars( $button['value'] );
+                       }
+
                        if ( $button['attribs'] ) {
                                $attrs += $button['attribs'];
                        }
@@ -1061,7 +1127,11 @@ class HTMLForm extends ContextSource {
                                $attrs['class'][] = 'mw-ui-button';
                        }
 
-                       $buttons .= Html::element( 'input', $attrs ) . "\n";
+                       if ( $isBadIE ) {
+                               $buttons .= Html::element( 'input', $attrs ) . "\n";
+                       } else {
+                               $buttons .= Html::rawElement( 'button', $attrs, $label ) . "\n";
+                       }
                }
 
                $html = Html::rawElement( 'span',
@@ -1396,6 +1466,7 @@ class HTMLForm extends ContextSource {
                $hasLabel = false;
 
                // Conveniently, PHP method names are case-insensitive.
+               // For grep: this can call getDiv, getRaw, getInline, getVForm, getOOUI
                $getFieldHtmlMethod = $displayFormat == 'table' ? 'getTableRow' : ( 'get' . $displayFormat );
 
                foreach ( $fields as $key => $value ) {
index d4a293e..3ba2156 100644 (file)
@@ -587,7 +587,7 @@ abstract class HTMLFormField {
                        // It might look weird, but it'll work OK.
                        return $this->getFieldLayoutOOUI(
                                new OOUI\Widget( array( 'content' => new OOUI\HtmlSnippet( $this->getDiv( $value ) ) ) ),
-                               array( 'infusable' => false )
+                               array( 'infusable' => false, 'align' => 'top' )
                        );
                }
 
@@ -1054,8 +1054,8 @@ abstract class HTMLFormField {
 
                foreach ( $oldoptions as $text => $data ) {
                        $options[] = array(
-                               'data' => $data,
-                               'label' => $text,
+                               'data' => (string)$data,
+                               'label' => (string)$text,
                        );
                }
 
index c11449a..811eaf4 100644 (file)
@@ -50,6 +50,9 @@ class OOUIHTMLForm extends HTMLForm {
        function getButtons() {
                $buttons = '';
 
+               // IE<8 has bugs with <button>, so we'll need to avoid them.
+               $isBadIE = preg_match( '/MSIE [1-7]\./i', $this->getRequest()->getHeader( 'User-Agent' ) );
+
                if ( $this->mShowSubmit ) {
                        $attribs = array( 'infusable' => true );
 
@@ -70,6 +73,7 @@ class OOUIHTMLForm extends HTMLForm {
                        $attribs['label'] = $this->getSubmitText();
                        $attribs['value'] = $this->getSubmitText();
                        $attribs['flags'] = $this->mSubmitFlags;
+                       $attribs['useInputTag'] = $isBadIE;
 
                        $buttons .= new OOUI\ButtonInputWidget( $attribs );
                }
@@ -78,6 +82,7 @@ class OOUIHTMLForm extends HTMLForm {
                        $buttons .= new OOUI\ButtonInputWidget( array(
                                'type' => 'reset',
                                'label' => $this->msg( 'htmlform-reset' )->text(),
+                               'useInputTag' => $isBadIE,
                        ) );
                }
 
@@ -92,13 +97,27 @@ class OOUIHTMLForm extends HTMLForm {
                                $attrs['id'] = $button['id'];
                        }
 
+                       if ( $isBadIE ) {
+                               $label = $button['value'];
+                       } elseif ( isset( $button['label-message'] ) ) {
+                               $label = new OOUI\HtmlSnippet( $this->msg( $button['label-message'] )->parse() );
+                       } elseif ( isset( $button['label'] ) ) {
+                               $label = $button['label'];
+                       } elseif ( isset( $button['label-raw'] ) ) {
+                               $label = new OOUI\HtmlSnippet( $button['label-raw'] );
+                       } else {
+                               $label = $button['value'];
+                       }
+
                        $attrs['classes'] = isset( $attrs['class'] ) ? (array)$attrs['class'] : array();
 
                        $buttons .= new OOUI\ButtonInputWidget( array(
                                'type' => 'submit',
                                'name' => $button['name'],
                                'value' => $button['value'],
-                               'label' => $button['value'],
+                               'label' => $label,
+                               'flags' => $button['flags'],
+                               'useInputTag' => $isBadIE,
                        ) + $attrs );
                }
 
diff --git a/includes/import/ImportSource.php b/includes/import/ImportSource.php
new file mode 100644 (file)
index 0000000..75d20b4
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+/**
+ * Source interface for XML import.
+ *
+ * Copyright © 2003,2005 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 SpecialPage
+ */
+
+/**
+ * Source interface for XML import.
+ *
+ * @ingroup SpecialPage
+ */
+interface ImportSource {
+
+       /**
+        * Indicates whether the end of the input has been reached.
+        * Will return true after a finite number of calls to readChunk.
+        *
+        * @return bool true if there is no more input, false otherwise.
+        */
+       function atEnd();
+
+       /**
+        * Return a chunk of the input, as a (possibly empty) string.
+        * When the end of input is reached, readChunk() returns false.
+        * If atEnd() returns false, readChunk() will return a string.
+        * If atEnd() returns true, readChunk() will return false.
+        *
+        * @return bool|string
+        */
+       function readChunk();
+}
diff --git a/includes/import/ImportStreamSource.php b/includes/import/ImportStreamSource.php
new file mode 100644 (file)
index 0000000..0e03d9f
--- /dev/null
@@ -0,0 +1,172 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 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 SpecialPage
+ */
+
+/**
+ * Imports a XML dump from a file (either from file upload, files on disk, or HTTP)
+ * @ingroup SpecialPage
+ */
+class ImportStreamSource implements ImportSource {
+       function __construct( $handle ) {
+               $this->mHandle = $handle;
+       }
+
+       /**
+        * @return bool
+        */
+       function atEnd() {
+               return feof( $this->mHandle );
+       }
+
+       /**
+        * @return string
+        */
+       function readChunk() {
+               return fread( $this->mHandle, 32768 );
+       }
+
+       /**
+        * @param string $filename
+        * @return Status
+        */
+       static function newFromFile( $filename ) {
+               MediaWiki\suppressWarnings();
+               $file = fopen( $filename, 'rt' );
+               MediaWiki\restoreWarnings();
+               if ( !$file ) {
+                       return Status::newFatal( "importcantopen" );
+               }
+               return Status::newGood( new ImportStreamSource( $file ) );
+       }
+
+       /**
+        * @param string $fieldname
+        * @return Status
+        */
+       static function newFromUpload( $fieldname = "xmlimport" ) {
+               $upload =& $_FILES[$fieldname];
+
+               if ( $upload === null || !$upload['name'] ) {
+                       return Status::newFatal( 'importnofile' );
+               }
+               if ( !empty( $upload['error'] ) ) {
+                       switch ( $upload['error'] ) {
+                               case 1:
+                                       # The uploaded file exceeds the upload_max_filesize directive in php.ini.
+                                       return Status::newFatal( 'importuploaderrorsize' );
+                               case 2:
+                                       # The uploaded file exceeds the MAX_FILE_SIZE directive that
+                                       # was specified in the HTML form.
+                                       return Status::newFatal( 'importuploaderrorsize' );
+                               case 3:
+                                       # The uploaded file was only partially uploaded
+                                       return Status::newFatal( 'importuploaderrorpartial' );
+                               case 6:
+                                       # Missing a temporary folder.
+                                       return Status::newFatal( 'importuploaderrortemp' );
+                               # case else: # Currently impossible
+                       }
+
+               }
+               $fname = $upload['tmp_name'];
+               if ( is_uploaded_file( $fname ) ) {
+                       return ImportStreamSource::newFromFile( $fname );
+               } else {
+                       return Status::newFatal( 'importnofile' );
+               }
+       }
+
+       /**
+        * @param string $url
+        * @param string $method
+        * @return Status
+        */
+       static function newFromURL( $url, $method = 'GET' ) {
+               wfDebug( __METHOD__ . ": opening $url\n" );
+               # Use the standard HTTP fetch function; it times out
+               # quicker and sorts out user-agent problems which might
+               # otherwise prevent importing from large sites, such
+               # as the Wikimedia cluster, etc.
+               $data = Http::request( $method, $url, array( 'followRedirects' => true ), __METHOD__ );
+               if ( $data !== false ) {
+                       $file = tmpfile();
+                       fwrite( $file, $data );
+                       fflush( $file );
+                       fseek( $file, 0 );
+                       return Status::newGood( new ImportStreamSource( $file ) );
+               } else {
+                       return Status::newFatal( 'importcantopen' );
+               }
+       }
+
+       /**
+        * @param string $interwiki
+        * @param string $page
+        * @param bool $history
+        * @param bool $templates
+        * @param int $pageLinkDepth
+        * @return Status
+        */
+       public static function newFromInterwiki( $interwiki, $page, $history = false,
+               $templates = false, $pageLinkDepth = 0
+       ) {
+               if ( $page == '' ) {
+                       return Status::newFatal( 'import-noarticle' );
+               }
+
+               # Look up the first interwiki prefix, and let the foreign site handle
+               # subsequent interwiki prefixes
+               $firstIwPrefix = strtok( $interwiki, ':' );
+               $firstIw = Interwiki::fetch( $firstIwPrefix );
+               if ( !$firstIw ) {
+                       return Status::newFatal( 'importbadinterwiki' );
+               }
+
+               $additionalIwPrefixes = strtok( '' );
+               if ( $additionalIwPrefixes ) {
+                       $additionalIwPrefixes .= ':';
+               }
+               # Have to do a DB-key replacement ourselves; otherwise spaces get
+               # URL-encoded to +, which is wrong in this case. Similar to logic in
+               # Title::getLocalURL
+               $link = $firstIw->getURL( strtr( "${additionalIwPrefixes}Special:Export/$page",
+                       ' ', '_' ) );
+
+               $params = array();
+               if ( $history ) {
+                       $params['history'] = 1;
+               }
+               if ( $templates ) {
+                       $params['templates'] = 1;
+               }
+               if ( $pageLinkDepth ) {
+                       $params['pagelink-depth'] = $pageLinkDepth;
+               }
+
+               $url = wfAppendQuery( $link, $params );
+               # For interwikis, use POST to avoid redirects.
+               return ImportStreamSource::newFromURL( $url, "POST" );
+       }
+}
diff --git a/includes/import/ImportStringSource.php b/includes/import/ImportStringSource.php
new file mode 100644 (file)
index 0000000..85983b1
--- /dev/null
@@ -0,0 +1,57 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 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 SpecialPage
+ */
+
+/**
+ * Used for importing XML dumps where the content of the dump is in a string.
+ * This class is ineffecient, and should only be used for small dumps.
+ * For larger dumps, ImportStreamSource should be used instead.
+ *
+ * @ingroup SpecialPage
+ */
+class ImportStringSource implements ImportSource {
+       function __construct( $string ) {
+               $this->mString = $string;
+               $this->mRead = false;
+       }
+
+       /**
+        * @return bool
+        */
+       function atEnd() {
+               return $this->mRead;
+       }
+
+       /**
+        * @return bool|string
+        */
+       function readChunk() {
+               if ( $this->atEnd() ) {
+                       return false;
+               }
+               $this->mRead = true;
+               return $this->mString;
+       }
+}
diff --git a/includes/import/UploadSourceAdapter.php b/includes/import/UploadSourceAdapter.php
new file mode 100644 (file)
index 0000000..17fbdfb
--- /dev/null
@@ -0,0 +1,149 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 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 SpecialPage
+ */
+
+/**
+ * This is a horrible hack used to keep source compatibility.
+ * @ingroup SpecialPage
+ */
+class UploadSourceAdapter {
+       /** @var array */
+       public static $sourceRegistrations = array();
+
+       /** @var string */
+       private $mSource;
+
+       /** @var string */
+       private $mBuffer;
+
+       /** @var int */
+       private $mPosition;
+
+       /**
+        * @param ImportSource $source
+        * @return string
+        */
+       static function registerSource( ImportSource $source ) {
+               $id = wfRandomString();
+
+               self::$sourceRegistrations[$id] = $source;
+
+               return $id;
+       }
+
+       /**
+        * @param string $path
+        * @param string $mode
+        * @param array $options
+        * @param string $opened_path
+        * @return bool
+        */
+       function stream_open( $path, $mode, $options, &$opened_path ) {
+               $url = parse_url( $path );
+               $id = $url['host'];
+
+               if ( !isset( self::$sourceRegistrations[$id] ) ) {
+                       return false;
+               }
+
+               $this->mSource = self::$sourceRegistrations[$id];
+
+               return true;
+       }
+
+       /**
+        * @param int $count
+        * @return string
+        */
+       function stream_read( $count ) {
+               $return = '';
+               $leave = false;
+
+               while ( !$leave && !$this->mSource->atEnd() &&
+                               strlen( $this->mBuffer ) < $count ) {
+                       $read = $this->mSource->readChunk();
+
+                       if ( !strlen( $read ) ) {
+                               $leave = true;
+                       }
+
+                       $this->mBuffer .= $read;
+               }
+
+               if ( strlen( $this->mBuffer ) ) {
+                       $return = substr( $this->mBuffer, 0, $count );
+                       $this->mBuffer = substr( $this->mBuffer, $count );
+               }
+
+               $this->mPosition += strlen( $return );
+
+               return $return;
+       }
+
+       /**
+        * @param string $data
+        * @return bool
+        */
+       function stream_write( $data ) {
+               return false;
+       }
+
+       /**
+        * @return mixed
+        */
+       function stream_tell() {
+               return $this->mPosition;
+       }
+
+       /**
+        * @return bool
+        */
+       function stream_eof() {
+               return $this->mSource->atEnd();
+       }
+
+       /**
+        * @return array
+        */
+       function url_stat() {
+               $result = array();
+
+               $result['dev'] = $result[0] = 0;
+               $result['ino'] = $result[1] = 0;
+               $result['mode'] = $result[2] = 0;
+               $result['nlink'] = $result[3] = 0;
+               $result['uid'] = $result[4] = 0;
+               $result['gid'] = $result[5] = 0;
+               $result['rdev'] = $result[6] = 0;
+               $result['size'] = $result[7] = 0;
+               $result['atime'] = $result[8] = 0;
+               $result['mtime'] = $result[9] = 0;
+               $result['ctime'] = $result[10] = 0;
+               $result['blksize'] = $result[11] = 0;
+               $result['blocks'] = $result[12] = 0;
+
+               return $result;
+       }
+}
diff --git a/includes/import/WikiImporter.php b/includes/import/WikiImporter.php
new file mode 100644 (file)
index 0000000..9bf9282
--- /dev/null
@@ -0,0 +1,1070 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 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 SpecialPage
+ */
+
+/**
+ * XML file reader for the page data importer.
+ *
+ * implements Special:Import
+ * @ingroup SpecialPage
+ */
+class WikiImporter {
+       private $reader = null;
+       private $foreignNamespaces = null;
+       private $mLogItemCallback, $mUploadCallback, $mRevisionCallback, $mPageCallback;
+       private $mSiteInfoCallback, $mPageOutCallback;
+       private $mNoticeCallback, $mDebug;
+       private $mImportUploads, $mImageBasePath;
+       private $mNoUpdates = false;
+       /** @var Config */
+       private $config;
+       /** @var ImportTitleFactory */
+       private $importTitleFactory;
+       /** @var array */
+       private $countableCache = array();
+
+       /**
+        * Creates an ImportXMLReader drawing from the source provided
+        * @param ImportSource $source
+        * @param Config $config
+        * @throws Exception
+        */
+       function __construct( ImportSource $source, Config $config = null ) {
+               if ( !class_exists( 'XMLReader' ) ) {
+                       throw new Exception( 'Import requires PHP to have been compiled with libxml support' );
+               }
+
+               $this->reader = new XMLReader();
+               if ( !$config ) {
+                       wfDeprecated( __METHOD__ . ' without a Config instance', '1.25' );
+                       $config = ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+               }
+               $this->config = $config;
+
+               if ( !in_array( 'uploadsource', stream_get_wrappers() ) ) {
+                       stream_wrapper_register( 'uploadsource', 'UploadSourceAdapter' );
+               }
+               $id = UploadSourceAdapter::registerSource( $source );
+
+               // Enable the entity loader, as it is needed for loading external URLs via
+               // XMLReader::open (T86036)
+               $oldDisable = libxml_disable_entity_loader( false );
+               if ( defined( 'LIBXML_PARSEHUGE' ) ) {
+                       $status = $this->reader->open( "uploadsource://$id", null, LIBXML_PARSEHUGE );
+               } else {
+                       $status = $this->reader->open( "uploadsource://$id" );
+               }
+               if ( !$status ) {
+                       $error = libxml_get_last_error();
+                       libxml_disable_entity_loader( $oldDisable );
+                       throw new MWException( 'Encountered an internal error while initializing WikiImporter object: ' .
+                               $error->message );
+               }
+               libxml_disable_entity_loader( $oldDisable );
+
+               // Default callbacks
+               $this->setPageCallback( array( $this, 'beforeImportPage' ) );
+               $this->setRevisionCallback( array( $this, "importRevision" ) );
+               $this->setUploadCallback( array( $this, 'importUpload' ) );
+               $this->setLogItemCallback( array( $this, 'importLogItem' ) );
+               $this->setPageOutCallback( array( $this, 'finishImportPage' ) );
+
+               $this->importTitleFactory = new NaiveImportTitleFactory();
+       }
+
+       /**
+        * @return null|XMLReader
+        */
+       public function getReader() {
+               return $this->reader;
+       }
+
+       public function throwXmlError( $err ) {
+               $this->debug( "FAILURE: $err" );
+               wfDebug( "WikiImporter XML error: $err\n" );
+       }
+
+       public function debug( $data ) {
+               if ( $this->mDebug ) {
+                       wfDebug( "IMPORT: $data\n" );
+               }
+       }
+
+       public function warn( $data ) {
+               wfDebug( "IMPORT: $data\n" );
+       }
+
+       public function notice( $msg /*, $param, ...*/ ) {
+               $params = func_get_args();
+               array_shift( $params );
+
+               if ( is_callable( $this->mNoticeCallback ) ) {
+                       call_user_func( $this->mNoticeCallback, $msg, $params );
+               } else { # No ImportReporter -> CLI
+                       echo wfMessage( $msg, $params )->text() . "\n";
+               }
+       }
+
+       /**
+        * Set debug mode...
+        * @param bool $debug
+        */
+       function setDebug( $debug ) {
+               $this->mDebug = $debug;
+       }
+
+       /**
+        * Set 'no updates' mode. In this mode, the link tables will not be updated by the importer
+        * @param bool $noupdates
+        */
+       function setNoUpdates( $noupdates ) {
+               $this->mNoUpdates = $noupdates;
+       }
+
+       /**
+        * Set a callback that displays notice messages
+        *
+        * @param callable $callback
+        * @return callable
+        */
+       public function setNoticeCallback( $callback ) {
+               return wfSetVar( $this->mNoticeCallback, $callback );
+       }
+
+       /**
+        * Sets the action to perform as each new page in the stream is reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setPageCallback( $callback ) {
+               $previous = $this->mPageCallback;
+               $this->mPageCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each page in the stream is completed.
+        * Callback accepts the page title (as a Title object), a second object
+        * with the original title form (in case it's been overridden into a
+        * local namespace), and a count of revisions.
+        *
+        * @param callable $callback
+        * @return callable
+        */
+       public function setPageOutCallback( $callback ) {
+               $previous = $this->mPageOutCallback;
+               $this->mPageOutCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each page revision is reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setRevisionCallback( $callback ) {
+               $previous = $this->mRevisionCallback;
+               $this->mRevisionCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each file upload version is reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setUploadCallback( $callback ) {
+               $previous = $this->mUploadCallback;
+               $this->mUploadCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform as each log item reached.
+        * @param callable $callback
+        * @return callable
+        */
+       public function setLogItemCallback( $callback ) {
+               $previous = $this->mLogItemCallback;
+               $this->mLogItemCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the action to perform when site info is encountered
+        * @param callable $callback
+        * @return callable
+        */
+       public function setSiteInfoCallback( $callback ) {
+               $previous = $this->mSiteInfoCallback;
+               $this->mSiteInfoCallback = $callback;
+               return $previous;
+       }
+
+       /**
+        * Sets the factory object to use to convert ForeignTitle objects into local
+        * Title objects
+        * @param ImportTitleFactory $factory
+        */
+       public function setImportTitleFactory( $factory ) {
+               $this->importTitleFactory = $factory;
+       }
+
+       /**
+        * Set a target namespace to override the defaults
+        * @param null|int $namespace
+        * @return bool
+        */
+       public function setTargetNamespace( $namespace ) {
+               if ( is_null( $namespace ) ) {
+                       // Don't override namespaces
+                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
+                       return true;
+               } elseif (
+                       $namespace >= 0 &&
+                       MWNamespace::exists( intval( $namespace ) )
+               ) {
+                       $namespace = intval( $namespace );
+                       $this->setImportTitleFactory( new NamespaceImportTitleFactory( $namespace ) );
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Set a target root page under which all pages are imported
+        * @param null|string $rootpage
+        * @return Status
+        */
+       public function setTargetRootPage( $rootpage ) {
+               $status = Status::newGood();
+               if ( is_null( $rootpage ) ) {
+                       // No rootpage
+                       $this->setImportTitleFactory( new NaiveImportTitleFactory() );
+               } elseif ( $rootpage !== '' ) {
+                       $rootpage = rtrim( $rootpage, '/' ); // avoid double slashes
+                       $title = Title::newFromText( $rootpage );
+
+                       if ( !$title || $title->isExternal() ) {
+                               $status->fatal( 'import-rootpage-invalid' );
+                       } else {
+                               if ( !MWNamespace::hasSubpages( $title->getNamespace() ) ) {
+                                       global $wgContLang;
+
+                                       $displayNSText = $title->getNamespace() == NS_MAIN
+                                               ? wfMessage( 'blanknamespace' )->text()
+                                               : $wgContLang->getNsText( $title->getNamespace() );
+                                       $status->fatal( 'import-rootpage-nosubpage', $displayNSText );
+                               } else {
+                                       // set namespace to 'all', so the namespace check in processTitle() can pass
+                                       $this->setTargetNamespace( null );
+                                       $this->setImportTitleFactory( new SubpageImportTitleFactory( $title ) );
+                               }
+                       }
+               }
+               return $status;
+       }
+
+       /**
+        * @param string $dir
+        */
+       public function setImageBasePath( $dir ) {
+               $this->mImageBasePath = $dir;
+       }
+
+       /**
+        * @param bool $import
+        */
+       public function setImportUploads( $import ) {
+               $this->mImportUploads = $import;
+       }
+
+       /**
+        * Default per-page callback. Sets up some things related to site statistics
+        * @param array $titleAndForeignTitle Two-element array, with Title object at
+        * index 0 and ForeignTitle object at index 1
+        * @return bool
+        */
+       public function beforeImportPage( $titleAndForeignTitle ) {
+               $title = $titleAndForeignTitle[0];
+               $page = WikiPage::factory( $title );
+               $this->countableCache['title_' . $title->getPrefixedText()] = $page->isCountable();
+               return true;
+       }
+
+       /**
+        * Default per-revision callback, performs the import.
+        * @param WikiRevision $revision
+        * @return bool
+        */
+       public function importRevision( $revision ) {
+               if ( !$revision->getContentHandler()->canBeUsedOn( $revision->getTitle() ) ) {
+                       $this->notice( 'import-error-bad-location',
+                               $revision->getTitle()->getPrefixedText(),
+                               $revision->getID(),
+                               $revision->getModel(),
+                               $revision->getFormat() );
+
+                       return false;
+               }
+
+               try {
+                       $dbw = wfGetDB( DB_MASTER );
+                       return $dbw->deadlockLoop( array( $revision, 'importOldRevision' ) );
+               } catch ( MWContentSerializationException $ex ) {
+                       $this->notice( 'import-error-unserialize',
+                               $revision->getTitle()->getPrefixedText(),
+                               $revision->getID(),
+                               $revision->getModel(),
+                               $revision->getFormat() );
+               }
+
+               return false;
+       }
+
+       /**
+        * Default per-revision callback, performs the import.
+        * @param WikiRevision $revision
+        * @return bool
+        */
+       public function importLogItem( $revision ) {
+               $dbw = wfGetDB( DB_MASTER );
+               return $dbw->deadlockLoop( array( $revision, 'importLogItem' ) );
+       }
+
+       /**
+        * Dummy for now...
+        * @param WikiRevision $revision
+        * @return bool
+        */
+       public function importUpload( $revision ) {
+               $dbw = wfGetDB( DB_MASTER );
+               return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
+       }
+
+       /**
+        * Mostly for hook use
+        * @param Title $title
+        * @param ForeignTitle $foreignTitle
+        * @param int $revCount
+        * @param int $sRevCount
+        * @param array $pageInfo
+        * @return bool
+        */
+       public function finishImportPage( $title, $foreignTitle, $revCount,
+                       $sRevCount, $pageInfo ) {
+
+               // 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
+               // and revision count, and we implement our own custom logic for the
+               // article (content page) count.
+               $page = WikiPage::factory( $title );
+               $page->loadPageData( 'fromdbmaster' );
+               $content = $page->getContent();
+               if ( $content === null ) {
+                       wfDebug( __METHOD__ . ': Skipping article count adjustment for ' . $title .
+                               ' because WikiPage::getContent() returned null' );
+               } else {
+                       $editInfo = $page->prepareContentForEdit( $content );
+                       $countKey = 'title_' . $title->getPrefixedText();
+                       $countable = $page->isCountable( $editInfo );
+                       if ( array_key_exists( $countKey, $this->countableCache ) &&
+                               $countable != $this->countableCache[$countKey] ) {
+                               DeferredUpdates::addUpdate( SiteStatsUpdate::factory( array(
+                                       'articles' => ( (int)$countable - (int)$this->countableCache[$countKey] )
+                               ) ) );
+                       }
+               }
+
+               $args = func_get_args();
+               return Hooks::run( 'AfterImportPage', $args );
+       }
+
+       /**
+        * Alternate per-revision callback, for debugging.
+        * @param WikiRevision $revision
+        */
+       public function debugRevisionHandler( &$revision ) {
+               $this->debug( "Got revision:" );
+               if ( is_object( $revision->title ) ) {
+                       $this->debug( "-- Title: " . $revision->title->getPrefixedText() );
+               } else {
+                       $this->debug( "-- Title: <invalid>" );
+               }
+               $this->debug( "-- User: " . $revision->user_text );
+               $this->debug( "-- Timestamp: " . $revision->timestamp );
+               $this->debug( "-- Comment: " . $revision->comment );
+               $this->debug( "-- Text: " . $revision->text );
+       }
+
+       /**
+        * Notify the callback function of site info
+        * @param array $siteInfo
+        * @return bool|mixed
+        */
+       private function siteInfoCallback( $siteInfo ) {
+               if ( isset( $this->mSiteInfoCallback ) ) {
+                       return call_user_func_array( $this->mSiteInfoCallback,
+                                       array( $siteInfo, $this ) );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Notify the callback function when a new "<page>" is reached.
+        * @param Title $title
+        */
+       function pageCallback( $title ) {
+               if ( isset( $this->mPageCallback ) ) {
+                       call_user_func( $this->mPageCallback, $title );
+               }
+       }
+
+       /**
+        * Notify the callback function when a "</page>" is closed.
+        * @param Title $title
+        * @param ForeignTitle $foreignTitle
+        * @param int $revCount
+        * @param int $sucCount Number of revisions for which callback returned true
+        * @param array $pageInfo Associative array of page information
+        */
+       private function pageOutCallback( $title, $foreignTitle, $revCount,
+                       $sucCount, $pageInfo ) {
+               if ( isset( $this->mPageOutCallback ) ) {
+                       $args = func_get_args();
+                       call_user_func_array( $this->mPageOutCallback, $args );
+               }
+       }
+
+       /**
+        * Notify the callback function of a revision
+        * @param WikiRevision $revision
+        * @return bool|mixed
+        */
+       private function revisionCallback( $revision ) {
+               if ( isset( $this->mRevisionCallback ) ) {
+                       return call_user_func_array( $this->mRevisionCallback,
+                                       array( $revision, $this ) );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Notify the callback function of a new log item
+        * @param WikiRevision $revision
+        * @return bool|mixed
+        */
+       private function logItemCallback( $revision ) {
+               if ( isset( $this->mLogItemCallback ) ) {
+                       return call_user_func_array( $this->mLogItemCallback,
+                                       array( $revision, $this ) );
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Retrieves the contents of the named attribute of the current element.
+        * @param string $attr The name of the attribute
+        * @return string The value of the attribute or an empty string if it is not set in the current
+        * element.
+        */
+       public function nodeAttribute( $attr ) {
+               return $this->reader->getAttribute( $attr );
+       }
+
+       /**
+        * Shouldn't something like this be built-in to XMLReader?
+        * Fetches text contents of the current element, assuming
+        * no sub-elements or such scary things.
+        * @return string
+        * @access private
+        */
+       public function nodeContents() {
+               if ( $this->reader->isEmptyElement ) {
+                       return "";
+               }
+               $buffer = "";
+               while ( $this->reader->read() ) {
+                       switch ( $this->reader->nodeType ) {
+                       case XMLReader::TEXT:
+                       case XMLReader::CDATA:
+                       case XMLReader::SIGNIFICANT_WHITESPACE:
+                               $buffer .= $this->reader->value;
+                               break;
+                       case XMLReader::END_ELEMENT:
+                               return $buffer;
+                       }
+               }
+
+               $this->reader->close();
+               return '';
+       }
+
+       /**
+        * Primary entry point
+        * @throws MWException
+        * @return bool
+        */
+       public function doImport() {
+               // Calls to reader->read need to be wrapped in calls to
+               // libxml_disable_entity_loader() to avoid local file
+               // inclusion attacks (bug 46932).
+               $oldDisable = libxml_disable_entity_loader( true );
+               $this->reader->read();
+
+               if ( $this->reader->localName != 'mediawiki' ) {
+                       libxml_disable_entity_loader( $oldDisable );
+                       throw new MWException( "Expected <mediawiki> tag, got " .
+                               $this->reader->localName );
+               }
+               $this->debug( "<mediawiki> tag is correct." );
+
+               $this->debug( "Starting primary dump processing loop." );
+
+               $keepReading = $this->reader->read();
+               $skip = false;
+               $rethrow = null;
+               try {
+                       while ( $keepReading ) {
+                               $tag = $this->reader->localName;
+                               $type = $this->reader->nodeType;
+
+                               if ( !Hooks::run( 'ImportHandleToplevelXMLTag', array( $this ) ) ) {
+                                       // Do nothing
+                               } elseif ( $tag == 'mediawiki' && $type == XMLReader::END_ELEMENT ) {
+                                       break;
+                               } elseif ( $tag == 'siteinfo' ) {
+                                       $this->handleSiteInfo();
+                               } elseif ( $tag == 'page' ) {
+                                       $this->handlePage();
+                               } elseif ( $tag == 'logitem' ) {
+                                       $this->handleLogItem();
+                               } elseif ( $tag != '#text' ) {
+                                       $this->warn( "Unhandled top-level XML tag $tag" );
+
+                                       $skip = true;
+                               }
+
+                               if ( $skip ) {
+                                       $keepReading = $this->reader->next();
+                                       $skip = false;
+                                       $this->debug( "Skip" );
+                               } else {
+                                       $keepReading = $this->reader->read();
+                               }
+                       }
+               } catch ( Exception $ex ) {
+                       $rethrow = $ex;
+               }
+
+               // finally
+               libxml_disable_entity_loader( $oldDisable );
+               $this->reader->close();
+
+               if ( $rethrow ) {
+                       throw $rethrow;
+               }
+
+               return true;
+       }
+
+       private function handleSiteInfo() {
+               $this->debug( "Enter site info handler." );
+               $siteInfo = array();
+
+               // Fields that can just be stuffed in the siteInfo object
+               $normalFields = array( 'sitename', 'base', 'generator', 'case' );
+
+               while ( $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XmlReader::END_ELEMENT &&
+                                       $this->reader->localName == 'siteinfo' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( $tag == 'namespace' ) {
+                               $this->foreignNamespaces[$this->nodeAttribute( 'key' )] =
+                                       $this->nodeContents();
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $siteInfo[$tag] = $this->nodeContents();
+                       }
+               }
+
+               $siteInfo['_namespaces'] = $this->foreignNamespaces;
+               $this->siteInfoCallback( $siteInfo );
+       }
+
+       private function handleLogItem() {
+               $this->debug( "Enter log item handler." );
+               $logInfo = array();
+
+               // Fields that can just be stuffed in the pageInfo object
+               $normalFields = array( 'id', 'comment', 'type', 'action', 'timestamp',
+                                       'logtitle', 'params' );
+
+               while ( $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'logitem' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( !Hooks::run( 'ImportHandleLogItemXMLTag', array(
+                               $this, $logInfo
+                       ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $logInfo[$tag] = $this->nodeContents();
+                       } elseif ( $tag == 'contributor' ) {
+                               $logInfo['contributor'] = $this->handleContributor();
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled log-item XML tag $tag" );
+                       }
+               }
+
+               $this->processLogItem( $logInfo );
+       }
+
+       /**
+        * @param array $logInfo
+        * @return bool|mixed
+        */
+       private function processLogItem( $logInfo ) {
+
+               $revision = new WikiRevision( $this->config );
+
+               if ( isset( $logInfo['id'] ) ) {
+                       $revision->setID( $logInfo['id'] );
+               }
+               $revision->setType( $logInfo['type'] );
+               $revision->setAction( $logInfo['action'] );
+               if ( isset( $logInfo['timestamp'] ) ) {
+                       $revision->setTimestamp( $logInfo['timestamp'] );
+               }
+               if ( isset( $logInfo['params'] ) ) {
+                       $revision->setParams( $logInfo['params'] );
+               }
+               if ( isset( $logInfo['logtitle'] ) ) {
+                       // @todo Using Title for non-local titles is a recipe for disaster.
+                       // We should use ForeignTitle here instead.
+                       $revision->setTitle( Title::newFromText( $logInfo['logtitle'] ) );
+               }
+
+               $revision->setNoUpdates( $this->mNoUpdates );
+
+               if ( isset( $logInfo['comment'] ) ) {
+                       $revision->setComment( $logInfo['comment'] );
+               }
+
+               if ( isset( $logInfo['contributor']['ip'] ) ) {
+                       $revision->setUserIP( $logInfo['contributor']['ip'] );
+               }
+
+               if ( !isset( $logInfo['contributor']['username'] ) ) {
+                       $revision->setUsername( 'Unknown user' );
+               } else {
+                       $revision->setUserName( $logInfo['contributor']['username'] );
+               }
+
+               return $this->logItemCallback( $revision );
+       }
+
+       private function handlePage() {
+               // Handle page data.
+               $this->debug( "Enter page handler." );
+               $pageInfo = array( 'revisionCount' => 0, 'successfulRevisionCount' => 0 );
+
+               // Fields that can just be stuffed in the pageInfo object
+               $normalFields = array( 'title', 'ns', 'id', 'redirect', 'restrictions' );
+
+               $skip = false;
+               $badTitle = false;
+
+               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'page' ) {
+                               break;
+                       }
+
+                       $skip = false;
+
+                       $tag = $this->reader->localName;
+
+                       if ( $badTitle ) {
+                               // The title is invalid, bail out of this page
+                               $skip = true;
+                       } elseif ( !Hooks::run( 'ImportHandlePageXMLTag', array( $this,
+                                               &$pageInfo ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               // An XML snippet:
+                               // <page>
+                               //     <id>123</id>
+                               //     <title>Page</title>
+                               //     <redirect title="NewTitle"/>
+                               //     ...
+                               // Because the redirect tag is built differently, we need special handling for that case.
+                               if ( $tag == 'redirect' ) {
+                                       $pageInfo[$tag] = $this->nodeAttribute( 'title' );
+                               } else {
+                                       $pageInfo[$tag] = $this->nodeContents();
+                               }
+                       } elseif ( $tag == 'revision' || $tag == 'upload' ) {
+                               if ( !isset( $title ) ) {
+                                       $title = $this->processTitle( $pageInfo['title'],
+                                               isset( $pageInfo['ns'] ) ? $pageInfo['ns'] : null );
+
+                                       // $title is either an array of two titles or false.
+                                       if ( is_array( $title ) ) {
+                                               $this->pageCallback( $title );
+                                               list( $pageInfo['_title'], $foreignTitle ) = $title;
+                                       } else {
+                                               $badTitle = true;
+                                               $skip = true;
+                                       }
+                               }
+
+                               if ( $title ) {
+                                       if ( $tag == 'revision' ) {
+                                               $this->handleRevision( $pageInfo );
+                                       } else {
+                                               $this->handleUpload( $pageInfo );
+                                       }
+                               }
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled page XML tag $tag" );
+                               $skip = true;
+                       }
+               }
+
+               // @note $pageInfo is only set if a valid $title is processed above with
+               //       no error. If we have a valid $title, then pageCallback is called
+               //       above, $pageInfo['title'] is set and we do pageOutCallback here.
+               //       If $pageInfo['_title'] is not set, then $foreignTitle is also not
+               //       set since they both come from $title above.
+               if ( array_key_exists( '_title', $pageInfo ) ) {
+                       $this->pageOutCallback( $pageInfo['_title'], $foreignTitle,
+                                       $pageInfo['revisionCount'],
+                                       $pageInfo['successfulRevisionCount'],
+                                       $pageInfo );
+               }
+       }
+
+       /**
+        * @param array $pageInfo
+        */
+       private function handleRevision( &$pageInfo ) {
+               $this->debug( "Enter revision handler" );
+               $revisionInfo = array();
+
+               $normalFields = array( 'id', 'timestamp', 'comment', 'minor', 'model', 'format', 'text' );
+
+               $skip = false;
+
+               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'revision' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( !Hooks::run( 'ImportHandleRevisionXMLTag', array(
+                               $this, $pageInfo, $revisionInfo
+                       ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $revisionInfo[$tag] = $this->nodeContents();
+                       } elseif ( $tag == 'contributor' ) {
+                               $revisionInfo['contributor'] = $this->handleContributor();
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled revision XML tag $tag" );
+                               $skip = true;
+                       }
+               }
+
+               $pageInfo['revisionCount']++;
+               if ( $this->processRevision( $pageInfo, $revisionInfo ) ) {
+                       $pageInfo['successfulRevisionCount']++;
+               }
+       }
+
+       /**
+        * @param array $pageInfo
+        * @param array $revisionInfo
+        * @return bool|mixed
+        */
+       private function processRevision( $pageInfo, $revisionInfo ) {
+               global $wgMaxArticleSize;
+
+               // Make sure revisions won't violate $wgMaxArticleSize, which could lead to
+               // database errors and instability. Testing for revisions with only listed
+               // content models, as other content models might use serialization formats
+               // which aren't checked against $wgMaxArticleSize.
+               if ( ( !isset( $revisionInfo['model'] ) ||
+                       in_array( $revisionInfo['model'], array(
+                               'wikitext',
+                               'css',
+                               'json',
+                               'javascript',
+                               'text',
+                               ''
+                       ) ) ) &&
+                       (int)( strlen( $revisionInfo['text'] ) / 1024 ) > $wgMaxArticleSize
+               ) {
+                       throw new MWException( 'The text of ' .
+                               ( isset( $revisionInfo['id'] ) ?
+                                       "the revision with ID $revisionInfo[id]" :
+                                       'a revision'
+                               ) . " exceeds the maximum allowable size ($wgMaxArticleSize KB)" );
+               }
+
+               $revision = new WikiRevision( $this->config );
+
+               if ( isset( $revisionInfo['id'] ) ) {
+                       $revision->setID( $revisionInfo['id'] );
+               }
+               if ( isset( $revisionInfo['model'] ) ) {
+                       $revision->setModel( $revisionInfo['model'] );
+               }
+               if ( isset( $revisionInfo['format'] ) ) {
+                       $revision->setFormat( $revisionInfo['format'] );
+               }
+               $revision->setTitle( $pageInfo['_title'] );
+
+               if ( isset( $revisionInfo['text'] ) ) {
+                       $handler = $revision->getContentHandler();
+                       $text = $handler->importTransform(
+                               $revisionInfo['text'],
+                               $revision->getFormat() );
+
+                       $revision->setText( $text );
+               }
+               if ( isset( $revisionInfo['timestamp'] ) ) {
+                       $revision->setTimestamp( $revisionInfo['timestamp'] );
+               } else {
+                       $revision->setTimestamp( wfTimestampNow() );
+               }
+
+               if ( isset( $revisionInfo['comment'] ) ) {
+                       $revision->setComment( $revisionInfo['comment'] );
+               }
+
+               if ( isset( $revisionInfo['minor'] ) ) {
+                       $revision->setMinor( true );
+               }
+               if ( isset( $revisionInfo['contributor']['ip'] ) ) {
+                       $revision->setUserIP( $revisionInfo['contributor']['ip'] );
+               } elseif ( isset( $revisionInfo['contributor']['username'] ) ) {
+                       $revision->setUserName( $revisionInfo['contributor']['username'] );
+               } else {
+                       $revision->setUserName( 'Unknown user' );
+               }
+               $revision->setNoUpdates( $this->mNoUpdates );
+
+               return $this->revisionCallback( $revision );
+       }
+
+       /**
+        * @param array $pageInfo
+        * @return mixed
+        */
+       private function handleUpload( &$pageInfo ) {
+               $this->debug( "Enter upload handler" );
+               $uploadInfo = array();
+
+               $normalFields = array( 'timestamp', 'comment', 'filename', 'text',
+                                       'src', 'size', 'sha1base36', 'archivename', 'rel' );
+
+               $skip = false;
+
+               while ( $skip ? $this->reader->next() : $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'upload' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( !Hooks::run( 'ImportHandleUploadXMLTag', array(
+                               $this, $pageInfo
+                       ) ) ) {
+                               // Do nothing
+                       } elseif ( in_array( $tag, $normalFields ) ) {
+                               $uploadInfo[$tag] = $this->nodeContents();
+                       } elseif ( $tag == 'contributor' ) {
+                               $uploadInfo['contributor'] = $this->handleContributor();
+                       } elseif ( $tag == 'contents' ) {
+                               $contents = $this->nodeContents();
+                               $encoding = $this->reader->getAttribute( 'encoding' );
+                               if ( $encoding === 'base64' ) {
+                                       $uploadInfo['fileSrc'] = $this->dumpTemp( base64_decode( $contents ) );
+                                       $uploadInfo['isTempSrc'] = true;
+                               }
+                       } elseif ( $tag != '#text' ) {
+                               $this->warn( "Unhandled upload XML tag $tag" );
+                               $skip = true;
+                       }
+               }
+
+               if ( $this->mImageBasePath && isset( $uploadInfo['rel'] ) ) {
+                       $path = "{$this->mImageBasePath}/{$uploadInfo['rel']}";
+                       if ( file_exists( $path ) ) {
+                               $uploadInfo['fileSrc'] = $path;
+                               $uploadInfo['isTempSrc'] = false;
+                       }
+               }
+
+               if ( $this->mImportUploads ) {
+                       return $this->processUpload( $pageInfo, $uploadInfo );
+               }
+       }
+
+       /**
+        * @param string $contents
+        * @return string
+        */
+       private function dumpTemp( $contents ) {
+               $filename = tempnam( wfTempDir(), 'importupload' );
+               file_put_contents( $filename, $contents );
+               return $filename;
+       }
+
+       /**
+        * @param array $pageInfo
+        * @param array $uploadInfo
+        * @return mixed
+        */
+       private function processUpload( $pageInfo, $uploadInfo ) {
+               $revision = new WikiRevision( $this->config );
+               $text = isset( $uploadInfo['text'] ) ? $uploadInfo['text'] : '';
+
+               $revision->setTitle( $pageInfo['_title'] );
+               $revision->setID( $pageInfo['id'] );
+               $revision->setTimestamp( $uploadInfo['timestamp'] );
+               $revision->setText( $text );
+               $revision->setFilename( $uploadInfo['filename'] );
+               if ( isset( $uploadInfo['archivename'] ) ) {
+                       $revision->setArchiveName( $uploadInfo['archivename'] );
+               }
+               $revision->setSrc( $uploadInfo['src'] );
+               if ( isset( $uploadInfo['fileSrc'] ) ) {
+                       $revision->setFileSrc( $uploadInfo['fileSrc'],
+                               !empty( $uploadInfo['isTempSrc'] ) );
+               }
+               if ( isset( $uploadInfo['sha1base36'] ) ) {
+                       $revision->setSha1Base36( $uploadInfo['sha1base36'] );
+               }
+               $revision->setSize( intval( $uploadInfo['size'] ) );
+               $revision->setComment( $uploadInfo['comment'] );
+
+               if ( isset( $uploadInfo['contributor']['ip'] ) ) {
+                       $revision->setUserIP( $uploadInfo['contributor']['ip'] );
+               }
+               if ( isset( $uploadInfo['contributor']['username'] ) ) {
+                       $revision->setUserName( $uploadInfo['contributor']['username'] );
+               }
+               $revision->setNoUpdates( $this->mNoUpdates );
+
+               return call_user_func( $this->mUploadCallback, $revision );
+       }
+
+       /**
+        * @return array
+        */
+       private function handleContributor() {
+               $fields = array( 'id', 'ip', 'username' );
+               $info = array();
+
+               if ( $this->reader->isEmptyElement ) {
+                       return $info;
+               }
+               while ( $this->reader->read() ) {
+                       if ( $this->reader->nodeType == XMLReader::END_ELEMENT &&
+                                       $this->reader->localName == 'contributor' ) {
+                               break;
+                       }
+
+                       $tag = $this->reader->localName;
+
+                       if ( in_array( $tag, $fields ) ) {
+                               $info[$tag] = $this->nodeContents();
+                       }
+               }
+
+               return $info;
+       }
+
+       /**
+        * @param string $text
+        * @param string|null $ns
+        * @return array|bool
+        */
+       private function processTitle( $text, $ns = null ) {
+               if ( is_null( $this->foreignNamespaces ) ) {
+                       $foreignTitleFactory = new NaiveForeignTitleFactory();
+               } else {
+                       $foreignTitleFactory = new NamespaceAwareForeignTitleFactory(
+                               $this->foreignNamespaces );
+               }
+
+               $foreignTitle = $foreignTitleFactory->createForeignTitle( $text,
+                       intval( $ns ) );
+
+               $title = $this->importTitleFactory->createTitleFromForeignTitle(
+                       $foreignTitle );
+
+               $commandLineMode = $this->config->get( 'CommandLineMode' );
+               if ( is_null( $title ) ) {
+                       # Invalid page title? Ignore the page
+                       $this->notice( 'import-error-invalid', $foreignTitle->getFullText() );
+                       return false;
+               } elseif ( $title->isExternal() ) {
+                       $this->notice( 'import-error-interwiki', $title->getPrefixedText() );
+                       return false;
+               } elseif ( !$title->canExist() ) {
+                       $this->notice( 'import-error-special', $title->getPrefixedText() );
+                       return false;
+               } elseif ( !$title->userCan( 'edit' ) && !$commandLineMode ) {
+                       # Do not import if the importing wiki user cannot edit this page
+                       $this->notice( 'import-error-edit', $title->getPrefixedText() );
+                       return false;
+               } elseif ( !$title->exists() && !$title->userCan( 'create' ) && !$commandLineMode ) {
+                       # Do not import if the importing wiki user cannot create this page
+                       $this->notice( 'import-error-create', $title->getPrefixedText() );
+                       return false;
+               }
+
+               return array( $title, $foreignTitle );
+       }
+}
diff --git a/includes/import/WikiRevision.php b/includes/import/WikiRevision.php
new file mode 100644 (file)
index 0000000..d2cf7f6
--- /dev/null
@@ -0,0 +1,714 @@
+<?php
+/**
+ * MediaWiki page data importer.
+ *
+ * Copyright © 2003,2005 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 SpecialPage
+ */
+
+/**
+ * Represents a revision, log entry or upload during the import process.
+ * This class sticks closely to the structure of the XML dump.
+ *
+ * @ingroup SpecialPage
+ */
+class WikiRevision {
+       /** @todo Unused? */
+       public $importer = null;
+
+       /** @var Title */
+       public $title = null;
+
+       /** @var int */
+       public $id = 0;
+
+       /** @var string */
+       public $timestamp = "20010115000000";
+
+       /**
+        * @var int
+        * @todo Can't find any uses. Public, because that's suspicious. Get clarity. */
+       public $user = 0;
+
+       /** @var string */
+       public $user_text = "";
+
+       /** @var User */
+       public $userObj = null;
+
+       /** @var string */
+       public $model = null;
+
+       /** @var string */
+       public $format = null;
+
+       /** @var string */
+       public $text = "";
+
+       /** @var int */
+       protected $size;
+
+       /** @var Content */
+       public $content = null;
+
+       /** @var ContentHandler */
+       protected $contentHandler = null;
+
+       /** @var string */
+       public $comment = "";
+
+       /** @var bool */
+       public $minor = false;
+
+       /** @var string */
+       public $type = "";
+
+       /** @var string */
+       public $action = "";
+
+       /** @var string */
+       public $params = "";
+
+       /** @var string */
+       public $fileSrc = '';
+
+       /** @var bool|string */
+       public $sha1base36 = false;
+
+       /**
+        * @var bool
+        * @todo Unused?
+        */
+       public $isTemp = false;
+
+       /** @var string */
+       public $archiveName = '';
+
+       protected $filename;
+
+       /** @var mixed */
+       protected $src;
+
+       /** @todo Unused? */
+       public $fileIsTemp;
+
+       /** @var bool */
+       private $mNoUpdates = false;
+
+       /** @var Config $config */
+       private $config;
+
+       public function __construct( Config $config ) {
+               $this->config = $config;
+       }
+
+       /**
+        * @param Title $title
+        * @throws MWException
+        */
+       function setTitle( $title ) {
+               if ( is_object( $title ) ) {
+                       $this->title = $title;
+               } elseif ( is_null( $title ) ) {
+                       throw new MWException( "WikiRevision given a null title in import. "
+                               . "You may need to adjust \$wgLegalTitleChars." );
+               } else {
+                       throw new MWException( "WikiRevision given non-object title in import." );
+               }
+       }
+
+       /**
+        * @param int $id
+        */
+       function setID( $id ) {
+               $this->id = $id;
+       }
+
+       /**
+        * @param string $ts
+        */
+       function setTimestamp( $ts ) {
+               # 2003-08-05T18:30:02Z
+               $this->timestamp = wfTimestamp( TS_MW, $ts );
+       }
+
+       /**
+        * @param string $user
+        */
+       function setUsername( $user ) {
+               $this->user_text = $user;
+       }
+
+       /**
+        * @param User $user
+        */
+       function setUserObj( $user ) {
+               $this->userObj = $user;
+       }
+
+       /**
+        * @param string $ip
+        */
+       function setUserIP( $ip ) {
+               $this->user_text = $ip;
+       }
+
+       /**
+        * @param string $model
+        */
+       function setModel( $model ) {
+               $this->model = $model;
+       }
+
+       /**
+        * @param string $format
+        */
+       function setFormat( $format ) {
+               $this->format = $format;
+       }
+
+       /**
+        * @param string $text
+        */
+       function setText( $text ) {
+               $this->text = $text;
+       }
+
+       /**
+        * @param string $text
+        */
+       function setComment( $text ) {
+               $this->comment = $text;
+       }
+
+       /**
+        * @param bool $minor
+        */
+       function setMinor( $minor ) {
+               $this->minor = (bool)$minor;
+       }
+
+       /**
+        * @param mixed $src
+        */
+       function setSrc( $src ) {
+               $this->src = $src;
+       }
+
+       /**
+        * @param string $src
+        * @param bool $isTemp
+        */
+       function setFileSrc( $src, $isTemp ) {
+               $this->fileSrc = $src;
+               $this->fileIsTemp = $isTemp;
+       }
+
+       /**
+        * @param string $sha1base36
+        */
+       function setSha1Base36( $sha1base36 ) {
+               $this->sha1base36 = $sha1base36;
+       }
+
+       /**
+        * @param string $filename
+        */
+       function setFilename( $filename ) {
+               $this->filename = $filename;
+       }
+
+       /**
+        * @param string $archiveName
+        */
+       function setArchiveName( $archiveName ) {
+               $this->archiveName = $archiveName;
+       }
+
+       /**
+        * @param int $size
+        */
+       function setSize( $size ) {
+               $this->size = intval( $size );
+       }
+
+       /**
+        * @param string $type
+        */
+       function setType( $type ) {
+               $this->type = $type;
+       }
+
+       /**
+        * @param string $action
+        */
+       function setAction( $action ) {
+               $this->action = $action;
+       }
+
+       /**
+        * @param array $params
+        */
+       function setParams( $params ) {
+               $this->params = $params;
+       }
+
+       /**
+        * @param bool $noupdates
+        */
+       public function setNoUpdates( $noupdates ) {
+               $this->mNoUpdates = $noupdates;
+       }
+
+       /**
+        * @return Title
+        */
+       function getTitle() {
+               return $this->title;
+       }
+
+       /**
+        * @return int
+        */
+       function getID() {
+               return $this->id;
+       }
+
+       /**
+        * @return string
+        */
+       function getTimestamp() {
+               return $this->timestamp;
+       }
+
+       /**
+        * @return string
+        */
+       function getUser() {
+               return $this->user_text;
+       }
+
+       /**
+        * @return User
+        */
+       function getUserObj() {
+               return $this->userObj;
+       }
+
+       /**
+        * @return string
+        *
+        * @deprecated Since 1.21, use getContent() instead.
+        */
+       function getText() {
+               ContentHandler::deprecated( __METHOD__, '1.21' );
+
+               return $this->text;
+       }
+
+       /**
+        * @return ContentHandler
+        */
+       function getContentHandler() {
+               if ( is_null( $this->contentHandler ) ) {
+                       $this->contentHandler = ContentHandler::getForModelID( $this->getModel() );
+               }
+
+               return $this->contentHandler;
+       }
+
+       /**
+        * @return Content
+        */
+       function getContent() {
+               if ( is_null( $this->content ) ) {
+                       $handler = $this->getContentHandler();
+                       $this->content = $handler->unserializeContent( $this->text, $this->getFormat() );
+               }
+
+               return $this->content;
+       }
+
+       /**
+        * @return string
+        */
+       function getModel() {
+               if ( is_null( $this->model ) ) {
+                       $this->model = $this->getTitle()->getContentModel();
+               }
+
+               return $this->model;
+       }
+
+       /**
+        * @return string
+        */
+       function getFormat() {
+               if ( is_null( $this->format ) ) {
+                       $this->format = $this->getContentHandler()->getDefaultFormat();
+               }
+
+               return $this->format;
+       }
+
+       /**
+        * @return string
+        */
+       function getComment() {
+               return $this->comment;
+       }
+
+       /**
+        * @return bool
+        */
+       function getMinor() {
+               return $this->minor;
+       }
+
+       /**
+        * @return mixed
+        */
+       function getSrc() {
+               return $this->src;
+       }
+
+       /**
+        * @return bool|string
+        */
+       function getSha1() {
+               if ( $this->sha1base36 ) {
+                       return Wikimedia\base_convert( $this->sha1base36, 36, 16 );
+               }
+               return false;
+       }
+
+       /**
+        * @return string
+        */
+       function getFileSrc() {
+               return $this->fileSrc;
+       }
+
+       /**
+        * @return bool
+        */
+       function isTempSrc() {
+               return $this->isTemp;
+       }
+
+       /**
+        * @return mixed
+        */
+       function getFilename() {
+               return $this->filename;
+       }
+
+       /**
+        * @return string
+        */
+       function getArchiveName() {
+               return $this->archiveName;
+       }
+
+       /**
+        * @return mixed
+        */
+       function getSize() {
+               return $this->size;
+       }
+
+       /**
+        * @return string
+        */
+       function getType() {
+               return $this->type;
+       }
+
+       /**
+        * @return string
+        */
+       function getAction() {
+               return $this->action;
+       }
+
+       /**
+        * @return string
+        */
+       function getParams() {
+               return $this->params;
+       }
+
+       /**
+        * @return bool
+        */
+       function importOldRevision() {
+               $dbw = wfGetDB( DB_MASTER );
+
+               # Sneak a single revision into place
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
+               if ( $user ) {
+                       $userId = intval( $user->getId() );
+                       $userText = $user->getName();
+               } else {
+                       $userId = 0;
+                       $userText = $this->getUser();
+                       $user = new User;
+               }
+
+               // avoid memory leak...?
+               Title::clearCaches();
+
+               $page = WikiPage::factory( $this->title );
+               $page->loadPageData( 'fromdbmaster' );
+               if ( !$page->exists() ) {
+                       // must create the page...
+                       $pageId = $page->insertOn( $dbw );
+                       $created = true;
+                       $oldcountable = null;
+               } else {
+                       $pageId = $page->getId();
+                       $created = false;
+
+                       $prior = $dbw->selectField( 'revision', '1',
+                               array( 'rev_page' => $pageId,
+                                       'rev_timestamp' => $dbw->timestamp( $this->timestamp ),
+                                       'rev_user_text' => $userText,
+                                       'rev_comment' => $this->getComment() ),
+                               __METHOD__
+                       );
+                       if ( $prior ) {
+                               // @todo FIXME: This could fail slightly for multiple matches :P
+                               wfDebug( __METHOD__ . ": skipping existing revision for [[" .
+                                       $this->title->getPrefixedText() . "]], timestamp " . $this->timestamp . "\n" );
+                               return false;
+                       }
+               }
+
+               if ( !$pageId ) {
+                       // This seems to happen if two clients simultaneously try to import the
+                       // same page
+                       wfDebug( __METHOD__ . ': got invalid $pageId when importing revision of [[' .
+                               $this->title->getPrefixedText() . ']], timestamp ' . $this->timestamp . "\n" );
+                       return false;
+               }
+
+               // Select previous version to make size diffs correct
+               // @todo This assumes that multiple revisions of the same page are imported
+               // in order from oldest to newest.
+               $prevId = $dbw->selectField( 'revision', 'rev_id',
+                       array(
+                               'rev_page' => $pageId,
+                               'rev_timestamp <= ' . $dbw->addQuotes( $dbw->timestamp( $this->timestamp ) ),
+                       ),
+                       __METHOD__,
+                       array( 'ORDER BY' => array(
+                                       'rev_timestamp DESC',
+                                       'rev_id DESC', // timestamp is not unique per page
+                               )
+                       )
+               );
+
+               # @todo FIXME: Use original rev_id optionally (better for backups)
+               # Insert the row
+               $revision = new Revision( array(
+                       'title' => $this->title,
+                       'page' => $pageId,
+                       'content_model' => $this->getModel(),
+                       'content_format' => $this->getFormat(),
+                       // XXX: just set 'content' => $this->getContent()?
+                       'text' => $this->getContent()->serialize( $this->getFormat() ),
+                       'comment' => $this->getComment(),
+                       'user' => $userId,
+                       'user_text' => $userText,
+                       'timestamp' => $this->timestamp,
+                       'minor_edit' => $this->minor,
+                       'parent_id' => $prevId,
+                       ) );
+               $revision->insertOn( $dbw );
+               $changed = $page->updateIfNewerOn( $dbw, $revision );
+
+               if ( $changed !== false && !$this->mNoUpdates ) {
+                       wfDebug( __METHOD__ . ": running updates\n" );
+                       // countable/oldcountable stuff is handled in WikiImporter::finishImportPage
+                       $page->doEditUpdates(
+                               $revision,
+                               $user,
+                               array( 'created' => $created, 'oldcountable' => 'no-change' )
+                       );
+               }
+
+               return true;
+       }
+
+       function importLogItem() {
+               $dbw = wfGetDB( DB_MASTER );
+
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
+               if ( $user ) {
+                       $userId = intval( $user->getId() );
+                       $userText = $user->getName();
+               } else {
+                       $userId = 0;
+                       $userText = $this->getUser();
+               }
+
+               # @todo FIXME: This will not record autoblocks
+               if ( !$this->getTitle() ) {
+                       wfDebug( __METHOD__ . ": skipping invalid {$this->type}/{$this->action} log time, timestamp " .
+                               $this->timestamp . "\n" );
+                       return;
+               }
+               # Check if it exists already
+               // @todo FIXME: Use original log ID (better for backups)
+               $prior = $dbw->selectField( 'logging', '1',
+                       array( 'log_type' => $this->getType(),
+                               'log_action' => $this->getAction(),
+                               'log_timestamp' => $dbw->timestamp( $this->timestamp ),
+                               'log_namespace' => $this->getTitle()->getNamespace(),
+                               'log_title' => $this->getTitle()->getDBkey(),
+                               'log_comment' => $this->getComment(),
+                               # 'log_user_text' => $this->user_text,
+                               'log_params' => $this->params ),
+                       __METHOD__
+               );
+               // @todo FIXME: This could fail slightly for multiple matches :P
+               if ( $prior ) {
+                       wfDebug( __METHOD__
+                               . ": skipping existing item for Log:{$this->type}/{$this->action}, timestamp "
+                               . $this->timestamp . "\n" );
+                       return;
+               }
+               $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' );
+               $data = array(
+                       'log_id' => $log_id,
+                       'log_type' => $this->type,
+                       'log_action' => $this->action,
+                       'log_timestamp' => $dbw->timestamp( $this->timestamp ),
+                       'log_user' => $userId,
+                       'log_user_text' => $userText,
+                       'log_namespace' => $this->getTitle()->getNamespace(),
+                       'log_title' => $this->getTitle()->getDBkey(),
+                       'log_comment' => $this->getComment(),
+                       'log_params' => $this->params
+               );
+               $dbw->insert( 'logging', $data, __METHOD__ );
+       }
+
+       /**
+        * @return bool
+        */
+       function importUpload() {
+               # Construct a file
+               $archiveName = $this->getArchiveName();
+               if ( $archiveName ) {
+                       wfDebug( __METHOD__ . "Importing archived file as $archiveName\n" );
+                       $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
+                               RepoGroup::singleton()->getLocalRepo(), $archiveName );
+               } else {
+                       $file = wfLocalFile( $this->getTitle() );
+                       $file->load( File::READ_LATEST );
+                       wfDebug( __METHOD__ . 'Importing new file as ' . $file->getName() . "\n" );
+                       if ( $file->exists() && $file->getTimestamp() > $this->getTimestamp() ) {
+                               $archiveName = $file->getTimestamp() . '!' . $file->getName();
+                               $file = OldLocalFile::newFromArchiveName( $this->getTitle(),
+                                       RepoGroup::singleton()->getLocalRepo(), $archiveName );
+                               wfDebug( __METHOD__ . "File already exists; importing as $archiveName\n" );
+                       }
+               }
+               if ( !$file ) {
+                       wfDebug( __METHOD__ . ': Bad file for ' . $this->getTitle() . "\n" );
+                       return false;
+               }
+
+               # Get the file source or download if necessary
+               $source = $this->getFileSrc();
+               $flags = $this->isTempSrc() ? File::DELETE_SOURCE : 0;
+               if ( !$source ) {
+                       $source = $this->downloadSource();
+                       $flags |= File::DELETE_SOURCE;
+               }
+               if ( !$source ) {
+                       wfDebug( __METHOD__ . ": Could not fetch remote file.\n" );
+                       return false;
+               }
+               $sha1File = ltrim( sha1_file( $source ), '0' );
+               $sha1 = $this->getSha1();
+               if ( $sha1 && ( $sha1 !== $sha1File ) ) {
+                       if ( $flags & File::DELETE_SOURCE ) {
+                               # Broken file; delete it if it is a temporary file
+                               unlink( $source );
+                       }
+                       wfDebug( __METHOD__ . ": Corrupt file $source.\n" );
+                       return false;
+               }
+
+               $user = $this->getUserObj() ?: User::newFromName( $this->getUser() );
+
+               # Do the actual upload
+               if ( $archiveName ) {
+                       $status = $file->uploadOld( $source, $archiveName,
+                               $this->getTimestamp(), $this->getComment(), $user, $flags );
+               } else {
+                       $status = $file->upload( $source, $this->getComment(), $this->getComment(),
+                               $flags, false, $this->getTimestamp(), $user );
+               }
+
+               if ( $status->isGood() ) {
+                       wfDebug( __METHOD__ . ": Successful\n" );
+                       return true;
+               } else {
+                       wfDebug( __METHOD__ . ': failed: ' . $status->getHTML() . "\n" );
+                       return false;
+               }
+       }
+
+       /**
+        * @return bool|string
+        */
+       function downloadSource() {
+               if ( !$this->config->get( 'EnableUploads' ) ) {
+                       return false;
+               }
+
+               $tempo = tempnam( wfTempDir(), 'download' );
+               $f = fopen( $tempo, 'wb' );
+               if ( !$f ) {
+                       wfDebug( "IMPORT: couldn't write to temp file $tempo\n" );
+                       return false;
+               }
+
+               // @todo FIXME!
+               $src = $this->getSrc();
+               $data = Http::get( $src, array(), __METHOD__ );
+               if ( !$data ) {
+                       wfDebug( "IMPORT: couldn't fetch source $src\n" );
+                       fclose( $f );
+                       unlink( $tempo );
+                       return false;
+               }
+
+               fwrite( $f, $data );
+               fclose( $f );
+
+               return $tempo;
+       }
+
+}
index b863adc..de84199 100644 (file)
@@ -169,6 +169,7 @@ abstract class Installer {
                'wgEnotifUserTalk',
                'wgEnotifWatchlist',
                'wgEmailAuthentication',
+               'wgDBname',
                'wgDBtype',
                'wgDiff3',
                'wgImageMagickConvertCommand',
@@ -883,7 +884,13 @@ abstract class Installer {
                }
 
                if ( !$caches ) {
-                       $this->showMessage( 'config-no-cache' );
+                       $key = 'config-no-cache';
+                       // PHP >=5.5 is called APCu, earlier versions use APC (T61998).
+                       if ( !wfIsHHVM() && version_compare( PHP_VERSION, '5.5', '>=' ) ) {
+                               // config-no-cache-apcu
+                               $key .= '-apcu';
+                       }
+                       $this->showMessage( $key );
                }
 
                $this->setVar( '_Caches', $caches );
index add600d..4813bea 100644 (file)
@@ -278,6 +278,7 @@ class MysqlUpdater extends DatabaseUpdater {
                        // 1.27
                        array( 'dropTable', 'msg_resource_links' ),
                        array( 'dropTable', 'msg_resource' ),
+                       array( 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ),
                );
        }
 
index 7880557..21d5dbc 100644 (file)
@@ -89,6 +89,7 @@ class PostgresUpdater extends DatabaseUpdater {
                        array( 'addTable', 'uploadstash', 'patch-uploadstash.sql' ),
                        array( 'addTable', 'user_former_groups', 'patch-user_former_groups.sql' ),
                        array( 'addTable', 'sites', 'patch-sites.sql' ),
+                       array( 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ),
 
                        # Needed before new field
                        array( 'convertArchive2' ),
index 5279c2d..ba223c4 100644 (file)
@@ -147,6 +147,7 @@ class SqliteUpdater extends DatabaseUpdater {
                        // 1.27
                        array( 'dropTable', 'msg_resource_links' ),
                        array( 'dropTable', 'msg_resource' ),
+                       array( 'addTable', 'bot_passwords', 'patch-bot_passwords.sql' ),
                );
        }
 
diff --git a/includes/installer/WebInstallerComplete.php b/includes/installer/WebInstallerComplete.php
new file mode 100644 (file)
index 0000000..f443db4
--- /dev/null
@@ -0,0 +1,57 @@
+<?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 Deployment
+ */
+
+class WebInstallerComplete extends WebInstallerPage {
+
+       public function execute() {
+               // Pop up a dialog box, to make it difficult for the user to forget
+               // to download the file
+               $lsUrl = $this->getVar( 'wgServer' ) . $this->parent->getURL( array( 'localsettings' => 1 ) );
+               if ( isset( $_SERVER['HTTP_USER_AGENT'] ) &&
+                       strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false
+               ) {
+                       // JS appears to be the only method that works consistently with IE7+
+                       $this->addHtml( "\n<script>jQuery( function () { location.href = " .
+                               Xml::encodeJsVar( $lsUrl ) . "; } );</script>\n" );
+               } else {
+                       $this->parent->request->response()->header( "Refresh: 0;url=$lsUrl" );
+               }
+
+               $this->startForm();
+               $this->parent->disableLinkPopups();
+               $this->addHTML(
+                       $this->parent->getInfoBox(
+                               wfMessage( 'config-install-done',
+                                       $lsUrl,
+                                       $this->getVar( 'wgServer' ) .
+                                       $this->getVar( 'wgScriptPath' ) . '/index.php',
+                                       '<downloadlink/>'
+                               )->plain(), 'tick-32.png'
+                       )
+               );
+               $this->addHTML( $this->parent->getInfoBox(
+                       wfMessage( 'config-extension-link' )->text() ) );
+
+               $this->parent->restoreLinkPopups();
+               $this->endForm( false, false );
+       }
+
+}
diff --git a/includes/installer/WebInstallerCopying.php b/includes/installer/WebInstallerCopying.php
new file mode 100644 (file)
index 0000000..36fec86
--- /dev/null
@@ -0,0 +1,31 @@
+<?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 Deployment
+ */
+
+class WebInstallerCopying extends WebInstallerDocument {
+
+       /**
+        * @return string
+        */
+       protected function getFileName() {
+               return 'COPYING';
+       }
+
+}
diff --git a/includes/installer/WebInstallerDBConnect.php b/includes/installer/WebInstallerDBConnect.php
new file mode 100644 (file)
index 0000000..7a0825e
--- /dev/null
@@ -0,0 +1,121 @@
+<?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 Deployment
+ */
+
+class WebInstallerDBConnect extends WebInstallerPage {
+
+       /**
+        * @return string|null When string, "skip" or "continue"
+        */
+       public function execute() {
+               if ( $this->getVar( '_ExistingDBSettings' ) ) {
+                       return 'skip';
+               }
+
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $status = $this->submit();
+
+                       if ( $status->isGood() ) {
+                               $this->setVar( '_UpgradeDone', false );
+
+                               return 'continue';
+                       } else {
+                               $this->parent->showStatusBox( $status );
+                       }
+               }
+
+               $this->startForm();
+
+               $types = "<ul class=\"config-settings-block\">\n";
+               $settings = '';
+               $defaultType = $this->getVar( 'wgDBtype' );
+
+               // Messages: config-dbsupport-mysql, config-dbsupport-postgres, config-dbsupport-oracle,
+               // config-dbsupport-sqlite, config-dbsupport-mssql
+               $dbSupport = '';
+               foreach ( Installer::getDBTypes() as $type ) {
+                       $dbSupport .= wfMessage( "config-dbsupport-$type" )->plain() . "\n";
+               }
+               $this->addHTML( $this->parent->getInfoBox(
+                       wfMessage( 'config-support-info', trim( $dbSupport ) )->text() ) );
+
+               // It's possible that the library for the default DB type is not compiled in.
+               // In that case, instead select the first supported DB type in the list.
+               $compiledDBs = $this->parent->getCompiledDBs();
+               if ( !in_array( $defaultType, $compiledDBs ) ) {
+                       $defaultType = $compiledDBs[0];
+               }
+
+               foreach ( $compiledDBs as $type ) {
+                       $installer = $this->parent->getDBInstaller( $type );
+                       $types .=
+                               '<li>' .
+                               Xml::radioLabel(
+                                       $installer->getReadableName(),
+                                       'DBType',
+                                       $type,
+                                       "DBType_$type",
+                                       $type == $defaultType,
+                                       array( 'class' => 'dbRadio', 'rel' => "DB_wrapper_$type" )
+                               ) .
+                               "</li>\n";
+
+                       // Messages: config-header-mysql, config-header-postgres, config-header-oracle,
+                       // config-header-sqlite
+                       $settings .= Html::openElement(
+                                       'div',
+                                       array(
+                                               'id' => 'DB_wrapper_' . $type,
+                                               'class' => 'dbWrapper'
+                                       )
+                               ) .
+                               Html::element( 'h3', array(), wfMessage( 'config-header-' . $type )->text() ) .
+                               $installer->getConnectForm() .
+                               "</div>\n";
+               }
+
+               $types .= "</ul><br style=\"clear: left\"/>\n";
+
+               $this->addHTML( $this->parent->label( 'config-db-type', false, $types ) . $settings );
+               $this->endForm();
+
+               return null;
+       }
+
+       /**
+        * @return Status
+        */
+       public function submit() {
+               $r = $this->parent->request;
+               $type = $r->getVal( 'DBType' );
+               if ( !$type ) {
+                       return Status::newFatal( 'config-invalid-db-type' );
+               }
+               $this->setVar( 'wgDBtype', $type );
+               $installer = $this->parent->getDBInstaller( $type );
+               if ( !$installer ) {
+                       return Status::newFatal( 'config-invalid-db-type' );
+               }
+
+               return $installer->submitConnectForm();
+       }
+
+}
diff --git a/includes/installer/WebInstallerDBSettings.php b/includes/installer/WebInstallerDBSettings.php
new file mode 100644 (file)
index 0000000..f214663
--- /dev/null
@@ -0,0 +1,54 @@
+<?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 Deployment
+ */
+
+class WebInstallerDBSettings extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               $installer = $this->parent->getDBInstaller( $this->getVar( 'wgDBtype' ) );
+
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $status = $installer->submitSettingsForm();
+                       if ( $status === false ) {
+                               return 'skip';
+                       } elseif ( $status->isGood() ) {
+                               return 'continue';
+                       } else {
+                               $this->parent->showStatusBox( $status );
+                       }
+               }
+
+               $form = $installer->getSettingsForm();
+               if ( $form === false ) {
+                       return 'skip';
+               }
+
+               $this->startForm();
+               $this->addHTML( $form );
+               $this->endForm();
+
+               return null;
+       }
+
+}
diff --git a/includes/installer/WebInstallerDocument.php b/includes/installer/WebInstallerDocument.php
new file mode 100644 (file)
index 0000000..fc1c33f
--- /dev/null
@@ -0,0 +1,49 @@
+<?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 Deployment
+ */
+
+abstract class WebInstallerDocument extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       abstract protected function getFileName();
+
+       public function execute() {
+               $text = $this->getFileContents();
+               $text = InstallDocFormatter::format( $text );
+               $this->parent->output->addWikiText( $text );
+               $this->startForm();
+               $this->endForm( false );
+       }
+
+       /**
+        * @return string
+        */
+       public function getFileContents() {
+               $file = __DIR__ . '/../../' . $this->getFileName();
+               if ( !file_exists( $file ) ) {
+                       return wfMessage( 'config-nofile', $file )->plain();
+               }
+
+               return file_get_contents( $file );
+       }
+
+}
diff --git a/includes/installer/WebInstallerExistingWiki.php b/includes/installer/WebInstallerExistingWiki.php
new file mode 100644 (file)
index 0000000..2c08c9c
--- /dev/null
@@ -0,0 +1,184 @@
+<?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 Deployment
+ */
+
+class WebInstallerExistingWiki extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       public function execute() {
+               // If there is no LocalSettings.php, continue to the installer welcome page
+               $vars = Installer::getExistingLocalSettings();
+               if ( !$vars ) {
+                       return 'skip';
+               }
+
+               // Check if the upgrade key supplied to the user has appeared in LocalSettings.php
+               if ( $vars['wgUpgradeKey'] !== false
+                       && $this->getVar( '_UpgradeKeySupplied' )
+                       && $this->getVar( 'wgUpgradeKey' ) === $vars['wgUpgradeKey']
+               ) {
+                       // It's there, so the user is authorized
+                       $status = $this->handleExistingUpgrade( $vars );
+                       if ( $status->isOK() ) {
+                               return 'skip';
+                       } else {
+                               $this->startForm();
+                               $this->parent->showStatusBox( $status );
+                               $this->endForm( 'continue' );
+
+                               return 'output';
+                       }
+               }
+
+               // If there is no $wgUpgradeKey, tell the user to add one to LocalSettings.php
+               if ( $vars['wgUpgradeKey'] === false ) {
+                       if ( $this->getVar( 'wgUpgradeKey', false ) === false ) {
+                               $secretKey = $this->getVar( 'wgSecretKey' ); // preserve $wgSecretKey
+                               $this->parent->generateKeys();
+                               $this->setVar( 'wgSecretKey', $secretKey );
+                               $this->setVar( '_UpgradeKeySupplied', true );
+                       }
+                       $this->startForm();
+                       $this->addHTML( $this->parent->getInfoBox(
+                               wfMessage( 'config-upgrade-key-missing', "<pre dir=\"ltr\">\$wgUpgradeKey = '" .
+                                       $this->getVar( 'wgUpgradeKey' ) . "';</pre>" )->plain()
+                       ) );
+                       $this->endForm( 'continue' );
+
+                       return 'output';
+               }
+
+               // If there is an upgrade key, but it wasn't supplied, prompt the user to enter it
+
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $key = $r->getText( 'config_wgUpgradeKey' );
+                       if ( !$key || $key !== $vars['wgUpgradeKey'] ) {
+                               $this->parent->showError( 'config-localsettings-badkey' );
+                               $this->showKeyForm();
+
+                               return 'output';
+                       }
+                       // Key was OK
+                       $status = $this->handleExistingUpgrade( $vars );
+                       if ( $status->isOK() ) {
+                               return 'continue';
+                       } else {
+                               $this->parent->showStatusBox( $status );
+                               $this->showKeyForm();
+
+                               return 'output';
+                       }
+               } else {
+                       $this->showKeyForm();
+
+                       return 'output';
+               }
+       }
+
+       /**
+        * Show the "enter key" form
+        */
+       protected function showKeyForm() {
+               $this->startForm();
+               $this->addHTML(
+                       $this->parent->getInfoBox( wfMessage( 'config-localsettings-upgrade' )->plain() ) .
+                       '<br />' .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgUpgradeKey',
+                               'label' => 'config-localsettings-key',
+                               'attribs' => array( 'autocomplete' => 'off' ),
+                       ) )
+               );
+               $this->endForm( 'continue' );
+       }
+
+       /**
+        * @param string[] $names
+        * @param mixed[] $vars
+        *
+        * @return Status
+        */
+       protected function importVariables( $names, $vars ) {
+               $status = Status::newGood();
+               foreach ( $names as $name ) {
+                       if ( !isset( $vars[$name] ) ) {
+                               $status->fatal( 'config-localsettings-incomplete', $name );
+                       }
+                       $this->setVar( $name, $vars[$name] );
+               }
+
+               return $status;
+       }
+
+       /**
+        * Initiate an upgrade of the existing database
+        *
+        * @param mixed[] $vars Variables from LocalSettings.php
+        *
+        * @return Status
+        */
+       protected function handleExistingUpgrade( $vars ) {
+               // Check $wgDBtype
+               if ( !isset( $vars['wgDBtype'] ) ||
+                       !in_array( $vars['wgDBtype'], Installer::getDBTypes() )
+               ) {
+                       return Status::newFatal( 'config-localsettings-connection-error', '' );
+               }
+
+               // Set the relevant variables from LocalSettings.php
+               $requiredVars = array( 'wgDBtype' );
+               $status = $this->importVariables( $requiredVars, $vars );
+               $installer = $this->parent->getDBInstaller();
+               $status->merge( $this->importVariables( $installer->getGlobalNames(), $vars ) );
+               if ( !$status->isOK() ) {
+                       return $status;
+               }
+
+               if ( isset( $vars['wgDBadminuser'] ) ) {
+                       $this->setVar( '_InstallUser', $vars['wgDBadminuser'] );
+               } else {
+                       $this->setVar( '_InstallUser', $vars['wgDBuser'] );
+               }
+               if ( isset( $vars['wgDBadminpassword'] ) ) {
+                       $this->setVar( '_InstallPassword', $vars['wgDBadminpassword'] );
+               } else {
+                       $this->setVar( '_InstallPassword', $vars['wgDBpassword'] );
+               }
+
+               // Test the database connection
+               $status = $installer->getConnection();
+               if ( !$status->isOK() ) {
+                       // Adjust the error message to explain things correctly
+                       $status->replaceMessage( 'config-connection-error',
+                               'config-localsettings-connection-error' );
+
+                       return $status;
+               }
+
+               // All good
+               $this->setVar( '_ExistingDBSettings', true );
+
+               return $status;
+       }
+
+}
diff --git a/includes/installer/WebInstallerInstall.php b/includes/installer/WebInstallerInstall.php
new file mode 100644 (file)
index 0000000..51a58ae
--- /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 Deployment
+ */
+
+class WebInstallerInstall extends WebInstallerPage {
+
+       /**
+        * @return bool Always true.
+        */
+       public function isSlow() {
+               return true;
+       }
+
+       /**
+        * @return string|bool
+        */
+       public function execute() {
+               if ( $this->getVar( '_UpgradeDone' ) ) {
+                       return 'skip';
+               } elseif ( $this->getVar( '_InstallDone' ) ) {
+                       return 'continue';
+               } elseif ( $this->parent->request->wasPosted() ) {
+                       $this->startForm();
+                       $this->addHTML( "<ul>" );
+                       $results = $this->parent->performInstallation(
+                               array( $this, 'startStage' ),
+                               array( $this, 'endStage' )
+                       );
+                       $this->addHTML( "</ul>" );
+                       // PerformInstallation bails on a fatal, so make sure the last item
+                       // completed before giving 'next.' Likewise, only provide back on failure
+                       $lastStep = end( $results );
+                       $continue = $lastStep->isOK() ? 'continue' : false;
+                       $back = $lastStep->isOK() ? false : 'back';
+                       $this->endForm( $continue, $back );
+               } else {
+                       $this->startForm();
+                       $this->addHTML( $this->parent->getInfoBox( wfMessage( 'config-install-begin' )->plain() ) );
+                       $this->endForm();
+               }
+
+               return true;
+       }
+
+       /**
+        * @param string $step
+        */
+       public function startStage( $step ) {
+               // Messages: config-install-database, config-install-tables, config-install-interwiki,
+               // config-install-stats, config-install-keys, config-install-sysop, config-install-mainpage
+               $this->addHTML( "<li>" . wfMessage( "config-install-$step" )->escaped() .
+                       wfMessage( 'ellipsis' )->escaped() );
+
+               if ( $step == 'extension-tables' ) {
+                       $this->startLiveBox();
+               }
+       }
+
+       /**
+        * @param string $step
+        * @param Status $status
+        */
+       public function endStage( $step, $status ) {
+               if ( $step == 'extension-tables' ) {
+                       $this->endLiveBox();
+               }
+               $msg = $status->isOk() ? 'config-install-step-done' : 'config-install-step-failed';
+               $html = wfMessage( 'word-separator' )->escaped() . wfMessage( $msg )->escaped();
+               if ( !$status->isOk() ) {
+                       $html = "<span class=\"error\">$html</span>";
+               }
+               $this->addHTML( $html . "</li>\n" );
+               if ( !$status->isGood() ) {
+                       $this->parent->showStatusBox( $status );
+               }
+       }
+
+}
diff --git a/includes/installer/WebInstallerLanguage.php b/includes/installer/WebInstallerLanguage.php
new file mode 100644 (file)
index 0000000..cfd4a86
--- /dev/null
@@ -0,0 +1,121 @@
+<?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 Deployment
+ */
+
+class WebInstallerLanguage extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               global $wgLang;
+               $r = $this->parent->request;
+               $userLang = $r->getVal( 'uselang' );
+               $contLang = $r->getVal( 'ContLang' );
+
+               $languages = Language::fetchLanguageNames();
+               $lifetime = intval( ini_get( 'session.gc_maxlifetime' ) );
+               if ( !$lifetime ) {
+                       $lifetime = 1440; // PHP default
+               }
+
+               if ( $r->wasPosted() ) {
+                       # Do session test
+                       if ( $this->parent->getSession( 'test' ) === null ) {
+                               $requestTime = $r->getVal( 'LanguageRequestTime' );
+                               if ( !$requestTime ) {
+                                       // The most likely explanation is that the user was knocked back
+                                       // from another page on POST due to session expiry
+                                       $msg = 'config-session-expired';
+                               } elseif ( time() - $requestTime > $lifetime ) {
+                                       $msg = 'config-session-expired';
+                               } else {
+                                       $msg = 'config-no-session';
+                               }
+                               $this->parent->showError( $msg, $wgLang->formatTimePeriod( $lifetime ) );
+                       } else {
+                               if ( isset( $languages[$userLang] ) ) {
+                                       $this->setVar( '_UserLang', $userLang );
+                               }
+                               if ( isset( $languages[$contLang] ) ) {
+                                       $this->setVar( 'wgLanguageCode', $contLang );
+                               }
+
+                               return 'continue';
+                       }
+               } elseif ( $this->parent->showSessionWarning ) {
+                       # The user was knocked back from another page to the start
+                       # This probably indicates a session expiry
+                       $this->parent->showError( 'config-session-expired',
+                               $wgLang->formatTimePeriod( $lifetime ) );
+               }
+
+               $this->parent->setSession( 'test', true );
+
+               if ( !isset( $languages[$userLang] ) ) {
+                       $userLang = $this->getVar( '_UserLang', 'en' );
+               }
+               if ( !isset( $languages[$contLang] ) ) {
+                       $contLang = $this->getVar( 'wgLanguageCode', 'en' );
+               }
+               $this->startForm();
+               $s = Html::hidden( 'LanguageRequestTime', time() ) .
+                       $this->getLanguageSelector( 'uselang', 'config-your-language', $userLang,
+                               $this->parent->getHelpBox( 'config-your-language-help' ) ) .
+                       $this->getLanguageSelector( 'ContLang', 'config-wiki-language', $contLang,
+                               $this->parent->getHelpBox( 'config-wiki-language-help' ) );
+               $this->addHTML( $s );
+               $this->endForm( 'continue', false );
+
+               return null;
+       }
+
+       /**
+        * Get a "<select>" for selecting languages.
+        *
+        * @param string $name
+        * @param string $label
+        * @param string $selectedCode
+        * @param string $helpHtml
+        *
+        * @return string
+        */
+       public function getLanguageSelector( $name, $label, $selectedCode, $helpHtml = '' ) {
+               global $wgDummyLanguageCodes;
+
+               $output = $helpHtml;
+
+               $select = new XmlSelect( $name, $name, $selectedCode );
+               $select->setAttribute( 'tabindex', $this->parent->nextTabIndex() );
+
+               $languages = Language::fetchLanguageNames();
+               ksort( $languages );
+               foreach ( $languages as $code => $lang ) {
+                       if ( isset( $wgDummyLanguageCodes[$code] ) ) {
+                               continue;
+                       }
+                       $select->addOption( "$code - $lang", $code );
+               }
+
+               $output .= $select->getHTML();
+               return $this->parent->label( $label, $name, $output );
+       }
+
+}
diff --git a/includes/installer/WebInstallerName.php b/includes/installer/WebInstallerName.php
new file mode 100644 (file)
index 0000000..717d67b
--- /dev/null
@@ -0,0 +1,249 @@
+<?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 Deployment
+ */
+
+class WebInstallerName extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       public function execute() {
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       if ( $this->submit() ) {
+                               return 'continue';
+                       }
+               }
+
+               $this->startForm();
+
+               // Encourage people to not name their site 'MediaWiki' by blanking the
+               // field. I think that was the intent with the original $GLOBALS['wgSitename']
+               // but these two always were the same so had the effect of making the
+               // installer forget $wgSitename when navigating back to this page.
+               if ( $this->getVar( 'wgSitename' ) == 'MediaWiki' ) {
+                       $this->setVar( 'wgSitename', '' );
+               }
+
+               // Set wgMetaNamespace to something valid before we show the form.
+               // $wgMetaNamespace defaults to $wgSiteName which is 'MediaWiki'
+               $metaNS = $this->getVar( 'wgMetaNamespace' );
+               $this->setVar(
+                       'wgMetaNamespace',
+                       wfMessage( 'config-ns-other-default' )->inContentLanguage()->text()
+               );
+
+               $this->addHTML(
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgSitename',
+                               'label' => 'config-site-name',
+                               'help' => $this->parent->getHelpBox( 'config-site-name-help' )
+                       ) ) .
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-ns-site-name, config-ns-generic, config-ns-other
+                       $this->parent->getRadioSet( array(
+                               'var' => '_NamespaceType',
+                               'label' => 'config-project-namespace',
+                               'itemLabelPrefix' => 'config-ns-',
+                               'values' => array( 'site-name', 'generic', 'other' ),
+                               'commonAttribs' => array( 'class' => 'enableForOther',
+                                       'rel' => 'config_wgMetaNamespace' ),
+                               'help' => $this->parent->getHelpBox( 'config-project-namespace-help' )
+                       ) ) .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgMetaNamespace',
+                               'label' => '', // @todo Needs a label?
+                               'attribs' => array( 'readonly' => 'readonly', 'class' => 'enabledByOther' )
+                       ) ) .
+                       $this->getFieldSetStart( 'config-admin-box' ) .
+                       $this->parent->getTextBox( array(
+                               'var' => '_AdminName',
+                               'label' => 'config-admin-name',
+                               'help' => $this->parent->getHelpBox( 'config-admin-help' )
+                       ) ) .
+                       $this->parent->getPasswordBox( array(
+                               'var' => '_AdminPassword',
+                               'label' => 'config-admin-password',
+                       ) ) .
+                       $this->parent->getPasswordBox( array(
+                               'var' => '_AdminPasswordConfirm',
+                               'label' => 'config-admin-password-confirm'
+                       ) ) .
+                       $this->parent->getTextBox( array(
+                               'var' => '_AdminEmail',
+                               'attribs' => array(
+                                       'dir' => 'ltr',
+                               ),
+                               'label' => 'config-admin-email',
+                               'help' => $this->parent->getHelpBox( 'config-admin-email-help' )
+                       ) ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => '_Subscribe',
+                               'label' => 'config-subscribe',
+                               'help' => $this->parent->getHelpBox( 'config-subscribe-help' )
+                       ) ) .
+                       $this->getFieldSetEnd() .
+                       $this->parent->getInfoBox( wfMessage( 'config-almost-done' )->text() ) .
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-optional-continue, config-optional-skip
+                       $this->parent->getRadioSet( array(
+                               'var' => '_SkipOptional',
+                               'itemLabelPrefix' => 'config-optional-',
+                               'values' => array( 'continue', 'skip' )
+                       ) )
+               );
+
+               // Restore the default value
+               $this->setVar( 'wgMetaNamespace', $metaNS );
+
+               $this->endForm();
+
+               return 'output';
+       }
+
+       /**
+        * @return bool
+        */
+       public function submit() {
+               global $wgPasswordPolicy;
+
+               $retVal = true;
+               $this->parent->setVarsFromRequest( array( 'wgSitename', '_NamespaceType',
+                       '_AdminName', '_AdminPassword', '_AdminPasswordConfirm', '_AdminEmail',
+                       '_Subscribe', '_SkipOptional', 'wgMetaNamespace' ) );
+
+               // Validate site name
+               if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
+                       $this->parent->showError( 'config-site-name-blank' );
+                       $retVal = false;
+               }
+
+               // Fetch namespace
+               $nsType = $this->getVar( '_NamespaceType' );
+               if ( $nsType == 'site-name' ) {
+                       $name = $this->getVar( 'wgSitename' );
+                       // Sanitize for namespace
+                       // This algorithm should match the JS one in WebInstallerOutput.php
+                       $name = preg_replace( '/[\[\]\{\}|#<>%+? ]/', '_', $name );
+                       $name = str_replace( '&', '&amp;', $name );
+                       $name = preg_replace( '/__+/', '_', $name );
+                       $name = ucfirst( trim( $name, '_' ) );
+               } elseif ( $nsType == 'generic' ) {
+                       $name = wfMessage( 'config-ns-generic' )->text();
+               } else { // other
+                       $name = $this->getVar( 'wgMetaNamespace' );
+               }
+
+               // Validate namespace
+               if ( strpos( $name, ':' ) !== false ) {
+                       $good = false;
+               } else {
+                       // Title-style validation
+                       $title = Title::newFromText( $name );
+                       if ( !$title ) {
+                               $good = $nsType == 'site-name';
+                       } else {
+                               $name = $title->getDBkey();
+                               $good = true;
+                       }
+               }
+               if ( !$good ) {
+                       $this->parent->showError( 'config-ns-invalid', $name );
+                       $retVal = false;
+               }
+
+               // Make sure it won't conflict with any existing namespaces
+               global $wgContLang;
+               $nsIndex = $wgContLang->getNsIndex( $name );
+               if ( $nsIndex !== false && $nsIndex !== NS_PROJECT ) {
+                       $this->parent->showError( 'config-ns-conflict', $name );
+                       $retVal = false;
+               }
+
+               $this->setVar( 'wgMetaNamespace', $name );
+
+               // Validate username for creation
+               $name = $this->getVar( '_AdminName' );
+               if ( strval( $name ) === '' ) {
+                       $this->parent->showError( 'config-admin-name-blank' );
+                       $cname = $name;
+                       $retVal = false;
+               } else {
+                       $cname = User::getCanonicalName( $name, 'creatable' );
+                       if ( $cname === false ) {
+                               $this->parent->showError( 'config-admin-name-invalid', $name );
+                               $retVal = false;
+                       } else {
+                               $this->setVar( '_AdminName', $cname );
+                       }
+               }
+
+               // Validate password
+               $msg = false;
+               $pwd = $this->getVar( '_AdminPassword' );
+               $user = User::newFromName( $cname );
+               if ( $user ) {
+                       $upp = new UserPasswordPolicy(
+                               $wgPasswordPolicy['policies'],
+                               $wgPasswordPolicy['checks']
+                       );
+                       $status = $upp->checkUserPasswordForGroups(
+                               $user,
+                               $pwd,
+                               array( 'bureaucrat', 'sysop' )  // per Installer::createSysop()
+                       );
+                       $valid = $status->isGood() ? true : $status->getMessage();
+               } else {
+                       $valid = 'config-admin-name-invalid';
+               }
+               if ( strval( $pwd ) === '' ) {
+                       // Provide a more specific and helpful message if password field is left blank
+                       $msg = 'config-admin-password-blank';
+               } elseif ( $pwd !== $this->getVar( '_AdminPasswordConfirm' ) ) {
+                       $msg = 'config-admin-password-mismatch';
+               } elseif ( $valid !== true ) {
+                       $msg = $valid;
+               }
+               if ( $msg !== false ) {
+                       call_user_func( array( $this->parent, 'showError' ), $msg );
+                       $this->setVar( '_AdminPassword', '' );
+                       $this->setVar( '_AdminPasswordConfirm', '' );
+                       $retVal = false;
+               }
+
+               // Validate e-mail if provided
+               $email = $this->getVar( '_AdminEmail' );
+               if ( $email && !Sanitizer::validateEmail( $email ) ) {
+                       $this->parent->showError( 'config-admin-error-bademail' );
+                       $retVal = false;
+               }
+               // If they asked to subscribe to mediawiki-announce but didn't give
+               // an e-mail, show an error. Bug 29332
+               if ( !$email && $this->getVar( '_Subscribe' ) ) {
+                       $this->parent->showError( 'config-subscribe-noemail' );
+                       $retVal = false;
+               }
+
+               return $retVal;
+       }
+
+}
diff --git a/includes/installer/WebInstallerOptions.php b/includes/installer/WebInstallerOptions.php
new file mode 100644 (file)
index 0000000..fb8e35b
--- /dev/null
@@ -0,0 +1,460 @@
+<?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 Deployment
+ */
+
+class WebInstallerOptions extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
+                       $this->submitSkins();
+                       return 'skip';
+               }
+               if ( $this->parent->request->wasPosted() ) {
+                       if ( $this->submit() ) {
+                               return 'continue';
+                       }
+               }
+
+               $emailwrapperStyle = $this->getVar( 'wgEnableEmail' ) ? '' : 'display: none';
+               $this->startForm();
+               $this->addHTML(
+                       # User Rights
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-profile-wiki, config-profile-no-anon, config-profile-fishbowl, config-profile-private
+                       $this->parent->getRadioSet( array(
+                               'var' => '_RightsProfile',
+                               'label' => 'config-profile',
+                               'itemLabelPrefix' => 'config-profile-',
+                               'values' => array_keys( $this->parent->rightsProfiles ),
+                       ) ) .
+                       $this->parent->getInfoBox( wfMessage( 'config-profile-help' )->plain() ) .
+
+                       # Licensing
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
+                       // config-license-cc-0, config-license-pd, config-license-gfdl,
+                       // config-license-none, config-license-cc-choose
+                       $this->parent->getRadioSet( array(
+                               'var' => '_LicenseCode',
+                               'label' => 'config-license',
+                               'itemLabelPrefix' => 'config-license-',
+                               'values' => array_keys( $this->parent->licenses ),
+                               'commonAttribs' => array( 'class' => 'licenseRadio' ),
+                       ) ) .
+                       $this->getCCChooser() .
+                       $this->parent->getHelpBox( 'config-license-help' ) .
+
+                       # E-mail
+                       $this->getFieldSetStart( 'config-email-settings' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnableEmail',
+                               'label' => 'config-enable-email',
+                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ),
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-enable-email-help' ) .
+                       "<div id=\"emailwrapper\" style=\"$emailwrapperStyle\">" .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgPasswordSender',
+                               'label' => 'config-email-sender'
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-sender-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnableUserEmail',
+                               'label' => 'config-email-user',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-user-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnotifUserTalk',
+                               'label' => 'config-email-usertalk',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnotifWatchlist',
+                               'label' => 'config-email-watchlist',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEmailAuthentication',
+                               'label' => 'config-email-auth',
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-email-auth-help' ) .
+                       "</div>" .
+                       $this->getFieldSetEnd()
+               );
+
+               $skins = $this->parent->findExtensions( 'skins' );
+               $skinHtml = $this->getFieldSetStart( 'config-skins' );
+
+               $skinNames = array_map( 'strtolower', $skins );
+               $chosenSkinName = $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
+
+               if ( $skins ) {
+                       $radioButtons = $this->parent->getRadioElements( array(
+                               'var' => 'wgDefaultSkin',
+                               'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
+                               'values' => $skinNames,
+                               'value' => $chosenSkinName,
+                       ) );
+
+                       foreach ( $skins as $skin ) {
+                               $skinHtml .=
+                                       '<div class="config-skins-item">' .
+                                       $this->parent->getCheckBox( array(
+                                               'var' => "skin-$skin",
+                                               'rawtext' => $skin,
+                                               'value' => $this->getVar( "skin-$skin", true ), // all found skins enabled by default
+                                       ) ) .
+                                       '<div class="config-skins-use-as-default">' . $radioButtons[strtolower( $skin )] . '</div>' .
+                                       '</div>';
+                       }
+               } else {
+                       $skinHtml .=
+                               $this->parent->getWarningBox( wfMessage( 'config-skins-missing' )->plain() ) .
+                               Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
+               }
+
+               $skinHtml .= $this->parent->getHelpBox( 'config-skins-help' ) .
+                       $this->getFieldSetEnd();
+               $this->addHTML( $skinHtml );
+
+               $extensions = $this->parent->findExtensions();
+
+               if ( $extensions ) {
+                       $extHtml = $this->getFieldSetStart( 'config-extensions' );
+
+                       foreach ( $extensions as $ext ) {
+                               $extHtml .= $this->parent->getCheckBox( array(
+                                       'var' => "ext-$ext",
+                                       'rawtext' => $ext,
+                               ) );
+                       }
+
+                       $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
+                               $this->getFieldSetEnd();
+                       $this->addHTML( $extHtml );
+               }
+
+               // Having / in paths in Windows looks funny :)
+               $this->setVar( 'wgDeletedDirectory',
+                       str_replace(
+                               '/', DIRECTORY_SEPARATOR,
+                               $this->getVar( 'wgDeletedDirectory' )
+                       )
+               );
+
+               $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
+               $this->addHTML(
+                       # Uploading
+                       $this->getFieldSetStart( 'config-upload-settings' ) .
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgEnableUploads',
+                               'label' => 'config-upload-enable',
+                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ),
+                               'help' => $this->parent->getHelpBox( 'config-upload-help' )
+                       ) ) .
+                       '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgDeletedDirectory',
+                               'label' => 'config-upload-deleted',
+                               'attribs' => array( 'dir' => 'ltr' ),
+                               'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
+                       ) ) .
+                       '</div>' .
+                       $this->parent->getTextBox( array(
+                               'var' => 'wgLogo',
+                               'label' => 'config-logo',
+                               'attribs' => array( 'dir' => 'ltr' ),
+                               'help' => $this->parent->getHelpBox( 'config-logo-help' )
+                       ) )
+               );
+               $this->addHTML(
+                       $this->parent->getCheckBox( array(
+                               'var' => 'wgUseInstantCommons',
+                               'label' => 'config-instantcommons',
+                               'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
+                       ) ) .
+                       $this->getFieldSetEnd()
+               );
+
+               $caches = array( 'none' );
+               $cachevalDefault = 'none';
+
+               if ( count( $this->getVar( '_Caches' ) ) ) {
+                       // A CACHE_ACCEL implementation is available
+                       $caches[] = 'accel';
+                       $cachevalDefault = 'accel';
+               }
+               $caches[] = 'memcached';
+
+               // We'll hide/show this on demand when the value changes, see config.js.
+               $cacheval = $this->getVar( '_MainCacheType' );
+               if ( !$cacheval ) {
+                       // We need to set a default here; but don't hardcode it
+                       // or we lose it every time we reload the page for validation
+                       // or going back!
+                       $cacheval = $cachevalDefault;
+               }
+               $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
+               $this->addHTML(
+                       # Advanced settings
+                       $this->getFieldSetStart( 'config-advanced-settings' ) .
+                       # Object cache settings
+                       // getRadioSet() builds a set of labeled radio buttons.
+                       // For grep: The following messages are used as the item labels:
+                       // config-cache-none, config-cache-accel, config-cache-memcached
+                       $this->parent->getRadioSet( array(
+                               'var' => '_MainCacheType',
+                               'label' => 'config-cache-options',
+                               'itemLabelPrefix' => 'config-cache-',
+                               'values' => $caches,
+                               'value' => $cacheval,
+                       ) ) .
+                       $this->parent->getHelpBox( 'config-cache-help' ) .
+                       "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
+                       $this->parent->getTextArea( array(
+                               'var' => '_MemCachedServers',
+                               'label' => 'config-memcached-servers',
+                               'help' => $this->parent->getHelpBox( 'config-memcached-help' )
+                       ) ) .
+                       '</div>' .
+                       $this->getFieldSetEnd()
+               );
+               $this->endForm();
+
+               return null;
+       }
+
+       /**
+        * @return string
+        */
+       public function getCCPartnerUrl() {
+               $server = $this->getVar( 'wgServer' );
+               $exitUrl = $server . $this->parent->getUrl( array(
+                       'page' => 'Options',
+                       'SubmitCC' => 'indeed',
+                       'config__LicenseCode' => 'cc',
+                       'config_wgRightsUrl' => '[license_url]',
+                       'config_wgRightsText' => '[license_name]',
+                       'config_wgRightsIcon' => '[license_button]',
+               ) );
+               $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
+                       '/mw-config/config-cc.css';
+               $iframeUrl = '//creativecommons.org/license/?' .
+                       wfArrayToCgi( array(
+                               'partner' => 'MediaWiki',
+                               'exit_url' => $exitUrl,
+                               'lang' => $this->getVar( '_UserLang' ),
+                               'stylesheet' => $styleUrl,
+                       ) );
+
+               return $iframeUrl;
+       }
+
+       /**
+        * @return string
+        */
+       public function getCCChooser() {
+               $iframeAttribs = array(
+                       'class' => 'config-cc-iframe',
+                       'name' => 'config-cc-iframe',
+                       'id' => 'config-cc-iframe',
+                       'frameborder' => 0,
+                       'width' => '100%',
+                       'height' => '100%',
+               );
+               if ( $this->getVar( '_CCDone' ) ) {
+                       $iframeAttribs['src'] = $this->parent->getUrl( array( 'ShowCC' => 'yes' ) );
+               } else {
+                       $iframeAttribs['src'] = $this->getCCPartnerUrl();
+               }
+               $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
+
+               return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
+                       Html::element( 'iframe', $iframeAttribs, '', false /* not short */ ) .
+                       "</div>\n";
+       }
+
+       /**
+        * @return string
+        */
+       public function getCCDoneBox() {
+               $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
+               // If you change this height, also change it in config.css
+               $expandJs = str_replace( '$1', '54em', $js );
+               $reduceJs = str_replace( '$1', '70px', $js );
+
+               return '<p>' .
+                       Html::element( 'img', array( 'src' => $this->getVar( 'wgRightsIcon' ) ) ) .
+                       '&#160;&#160;' .
+                       htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
+                       "</p>\n" .
+                       "<p style=\"text-align: center;\">" .
+                       Html::element( 'a',
+                               array(
+                                       'href' => $this->getCCPartnerUrl(),
+                                       'onclick' => $expandJs,
+                               ),
+                               wfMessage( 'config-cc-again' )->text()
+                       ) .
+                       "</p>\n" .
+                       "<script>\n" .
+                       # Reduce the wrapper div height
+                       htmlspecialchars( $reduceJs ) .
+                       "\n" .
+                       "</script>\n";
+       }
+
+       public function submitCC() {
+               $newValues = $this->parent->setVarsFromRequest(
+                       array( 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ) );
+               if ( count( $newValues ) != 3 ) {
+                       $this->parent->showError( 'config-cc-error' );
+
+                       return;
+               }
+               $this->setVar( '_CCDone', true );
+               $this->addHTML( $this->getCCDoneBox() );
+       }
+
+       /**
+        * If the user skips this installer page, we still need to set up the default skins, but ignore
+        * everything else.
+        *
+        * @return bool
+        */
+       public function submitSkins() {
+               $skins = $this->parent->findExtensions( 'skins' );
+               $this->parent->setVar( '_Skins', $skins );
+
+               if ( $skins ) {
+                       $skinNames = array_map( 'strtolower', $skins );
+                       $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
+               }
+
+               return true;
+       }
+
+       /**
+        * @return bool
+        */
+       public function submit() {
+               $this->parent->setVarsFromRequest( array( '_RightsProfile', '_LicenseCode',
+                       'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', 'wgLogo',
+                       'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
+                       'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
+                       'wgUseInstantCommons', 'wgDefaultSkin' ) );
+
+               $retVal = true;
+
+               if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
+                       reset( $this->parent->rightsProfiles );
+                       $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
+               }
+
+               $code = $this->getVar( '_LicenseCode' );
+               if ( $code == 'cc-choose' ) {
+                       if ( !$this->getVar( '_CCDone' ) ) {
+                               $this->parent->showError( 'config-cc-not-chosen' );
+                               $retVal = false;
+                       }
+               } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
+                       // Messages:
+                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
+                       // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
+                       // config-license-cc-choose
+                       $entry = $this->parent->licenses[$code];
+                       if ( isset( $entry['text'] ) ) {
+                               $this->setVar( 'wgRightsText', $entry['text'] );
+                       } else {
+                               $this->setVar( 'wgRightsText', wfMessage( 'config-license-' . $code )->text() );
+                       }
+                       $this->setVar( 'wgRightsUrl', $entry['url'] );
+                       $this->setVar( 'wgRightsIcon', $entry['icon'] );
+               } else {
+                       $this->setVar( 'wgRightsText', '' );
+                       $this->setVar( 'wgRightsUrl', '' );
+                       $this->setVar( 'wgRightsIcon', '' );
+               }
+
+               $skinsAvailable = $this->parent->findExtensions( 'skins' );
+               $skinsToInstall = array();
+               foreach ( $skinsAvailable as $skin ) {
+                       $this->parent->setVarsFromRequest( array( "skin-$skin" ) );
+                       if ( $this->getVar( "skin-$skin" ) ) {
+                               $skinsToInstall[] = $skin;
+                       }
+               }
+               $this->parent->setVar( '_Skins', $skinsToInstall );
+
+               if ( !$skinsToInstall && $skinsAvailable ) {
+                       $this->parent->showError( 'config-skins-must-enable-some' );
+                       $retVal = false;
+               }
+               $defaultSkin = $this->getVar( 'wgDefaultSkin' );
+               $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
+               if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
+                       $this->parent->showError( 'config-skins-must-enable-default' );
+                       $retVal = false;
+               }
+
+               $extsAvailable = $this->parent->findExtensions();
+               $extsToInstall = array();
+               foreach ( $extsAvailable as $ext ) {
+                       $this->parent->setVarsFromRequest( array( "ext-$ext" ) );
+                       if ( $this->getVar( "ext-$ext" ) ) {
+                               $extsToInstall[] = $ext;
+                       }
+               }
+               $this->parent->setVar( '_Extensions', $extsToInstall );
+
+               if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
+                       $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
+                       if ( !$memcServers ) {
+                               $this->parent->showError( 'config-memcache-needservers' );
+                               $retVal = false;
+                       }
+
+                       foreach ( $memcServers as $server ) {
+                               $memcParts = explode( ":", $server, 2 );
+                               if ( !isset( $memcParts[0] )
+                                       || ( !IP::isValid( $memcParts[0] )
+                                               && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
+                               ) {
+                                       $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
+                                       $retVal = false;
+                               } elseif ( !isset( $memcParts[1] ) ) {
+                                       $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
+                                       $retVal = false;
+                               } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
+                                       $this->parent->showError( 'config-memcache-badport', 1, 65535 );
+                                       $retVal = false;
+                               }
+                       }
+               }
+
+               return $retVal;
+       }
+
+}
index 0fcda7d..a529939 100644 (file)
@@ -205,1405 +205,3 @@ abstract class WebInstallerPage {
        }
 
 }
-
-class WebInstallerLanguage extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               global $wgLang;
-               $r = $this->parent->request;
-               $userLang = $r->getVal( 'uselang' );
-               $contLang = $r->getVal( 'ContLang' );
-
-               $languages = Language::fetchLanguageNames();
-               $lifetime = intval( ini_get( 'session.gc_maxlifetime' ) );
-               if ( !$lifetime ) {
-                       $lifetime = 1440; // PHP default
-               }
-
-               if ( $r->wasPosted() ) {
-                       # Do session test
-                       if ( $this->parent->getSession( 'test' ) === null ) {
-                               $requestTime = $r->getVal( 'LanguageRequestTime' );
-                               if ( !$requestTime ) {
-                                       // The most likely explanation is that the user was knocked back
-                                       // from another page on POST due to session expiry
-                                       $msg = 'config-session-expired';
-                               } elseif ( time() - $requestTime > $lifetime ) {
-                                       $msg = 'config-session-expired';
-                               } else {
-                                       $msg = 'config-no-session';
-                               }
-                               $this->parent->showError( $msg, $wgLang->formatTimePeriod( $lifetime ) );
-                       } else {
-                               if ( isset( $languages[$userLang] ) ) {
-                                       $this->setVar( '_UserLang', $userLang );
-                               }
-                               if ( isset( $languages[$contLang] ) ) {
-                                       $this->setVar( 'wgLanguageCode', $contLang );
-                               }
-
-                               return 'continue';
-                       }
-               } elseif ( $this->parent->showSessionWarning ) {
-                       # The user was knocked back from another page to the start
-                       # This probably indicates a session expiry
-                       $this->parent->showError( 'config-session-expired',
-                               $wgLang->formatTimePeriod( $lifetime ) );
-               }
-
-               $this->parent->setSession( 'test', true );
-
-               if ( !isset( $languages[$userLang] ) ) {
-                       $userLang = $this->getVar( '_UserLang', 'en' );
-               }
-               if ( !isset( $languages[$contLang] ) ) {
-                       $contLang = $this->getVar( 'wgLanguageCode', 'en' );
-               }
-               $this->startForm();
-               $s = Html::hidden( 'LanguageRequestTime', time() ) .
-                       $this->getLanguageSelector( 'uselang', 'config-your-language', $userLang,
-                               $this->parent->getHelpBox( 'config-your-language-help' ) ) .
-                       $this->getLanguageSelector( 'ContLang', 'config-wiki-language', $contLang,
-                               $this->parent->getHelpBox( 'config-wiki-language-help' ) );
-               $this->addHTML( $s );
-               $this->endForm( 'continue', false );
-
-               return null;
-       }
-
-       /**
-        * Get a "<select>" for selecting languages.
-        *
-        * @param string $name
-        * @param string $label
-        * @param string $selectedCode
-        * @param string $helpHtml
-        *
-        * @return string
-        */
-       public function getLanguageSelector( $name, $label, $selectedCode, $helpHtml = '' ) {
-               global $wgDummyLanguageCodes;
-
-               $output = $helpHtml;
-
-               $select = new XmlSelect( $name, $name, $selectedCode );
-               $select->setAttribute( 'tabindex', $this->parent->nextTabIndex() );
-
-               $languages = Language::fetchLanguageNames();
-               ksort( $languages );
-               foreach ( $languages as $code => $lang ) {
-                       if ( isset( $wgDummyLanguageCodes[$code] ) ) {
-                               continue;
-                       }
-                       $select->addOption( "$code - $lang", $code );
-               }
-
-               $output .= $select->getHTML();
-               return $this->parent->label( $label, $name, $output );
-       }
-
-}
-
-class WebInstallerExistingWiki extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       public function execute() {
-               // If there is no LocalSettings.php, continue to the installer welcome page
-               $vars = Installer::getExistingLocalSettings();
-               if ( !$vars ) {
-                       return 'skip';
-               }
-
-               // Check if the upgrade key supplied to the user has appeared in LocalSettings.php
-               if ( $vars['wgUpgradeKey'] !== false
-                       && $this->getVar( '_UpgradeKeySupplied' )
-                       && $this->getVar( 'wgUpgradeKey' ) === $vars['wgUpgradeKey']
-               ) {
-                       // It's there, so the user is authorized
-                       $status = $this->handleExistingUpgrade( $vars );
-                       if ( $status->isOK() ) {
-                               return 'skip';
-                       } else {
-                               $this->startForm();
-                               $this->parent->showStatusBox( $status );
-                               $this->endForm( 'continue' );
-
-                               return 'output';
-                       }
-               }
-
-               // If there is no $wgUpgradeKey, tell the user to add one to LocalSettings.php
-               if ( $vars['wgUpgradeKey'] === false ) {
-                       if ( $this->getVar( 'wgUpgradeKey', false ) === false ) {
-                               $secretKey = $this->getVar( 'wgSecretKey' ); // preserve $wgSecretKey
-                               $this->parent->generateKeys();
-                               $this->setVar( 'wgSecretKey', $secretKey );
-                               $this->setVar( '_UpgradeKeySupplied', true );
-                       }
-                       $this->startForm();
-                       $this->addHTML( $this->parent->getInfoBox(
-                               wfMessage( 'config-upgrade-key-missing', "<pre dir=\"ltr\">\$wgUpgradeKey = '" .
-                                       $this->getVar( 'wgUpgradeKey' ) . "';</pre>" )->plain()
-                       ) );
-                       $this->endForm( 'continue' );
-
-                       return 'output';
-               }
-
-               // If there is an upgrade key, but it wasn't supplied, prompt the user to enter it
-
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $key = $r->getText( 'config_wgUpgradeKey' );
-                       if ( !$key || $key !== $vars['wgUpgradeKey'] ) {
-                               $this->parent->showError( 'config-localsettings-badkey' );
-                               $this->showKeyForm();
-
-                               return 'output';
-                       }
-                       // Key was OK
-                       $status = $this->handleExistingUpgrade( $vars );
-                       if ( $status->isOK() ) {
-                               return 'continue';
-                       } else {
-                               $this->parent->showStatusBox( $status );
-                               $this->showKeyForm();
-
-                               return 'output';
-                       }
-               } else {
-                       $this->showKeyForm();
-
-                       return 'output';
-               }
-       }
-
-       /**
-        * Show the "enter key" form
-        */
-       protected function showKeyForm() {
-               $this->startForm();
-               $this->addHTML(
-                       $this->parent->getInfoBox( wfMessage( 'config-localsettings-upgrade' )->plain() ) .
-                       '<br />' .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgUpgradeKey',
-                               'label' => 'config-localsettings-key',
-                               'attribs' => array( 'autocomplete' => 'off' ),
-                       ) )
-               );
-               $this->endForm( 'continue' );
-       }
-
-       /**
-        * @param string[] $names
-        * @param mixed[] $vars
-        *
-        * @return Status
-        */
-       protected function importVariables( $names, $vars ) {
-               $status = Status::newGood();
-               foreach ( $names as $name ) {
-                       if ( !isset( $vars[$name] ) ) {
-                               $status->fatal( 'config-localsettings-incomplete', $name );
-                       }
-                       $this->setVar( $name, $vars[$name] );
-               }
-
-               return $status;
-       }
-
-       /**
-        * Initiate an upgrade of the existing database
-        *
-        * @param mixed[] $vars Variables from LocalSettings.php
-        *
-        * @return Status
-        */
-       protected function handleExistingUpgrade( $vars ) {
-               // Check $wgDBtype
-               if ( !isset( $vars['wgDBtype'] ) ||
-                       !in_array( $vars['wgDBtype'], Installer::getDBTypes() )
-               ) {
-                       return Status::newFatal( 'config-localsettings-connection-error', '' );
-               }
-
-               // Set the relevant variables from LocalSettings.php
-               $requiredVars = array( 'wgDBtype' );
-               $status = $this->importVariables( $requiredVars, $vars );
-               $installer = $this->parent->getDBInstaller();
-               $status->merge( $this->importVariables( $installer->getGlobalNames(), $vars ) );
-               if ( !$status->isOK() ) {
-                       return $status;
-               }
-
-               if ( isset( $vars['wgDBadminuser'] ) ) {
-                       $this->setVar( '_InstallUser', $vars['wgDBadminuser'] );
-               } else {
-                       $this->setVar( '_InstallUser', $vars['wgDBuser'] );
-               }
-               if ( isset( $vars['wgDBadminpassword'] ) ) {
-                       $this->setVar( '_InstallPassword', $vars['wgDBadminpassword'] );
-               } else {
-                       $this->setVar( '_InstallPassword', $vars['wgDBpassword'] );
-               }
-
-               // Test the database connection
-               $status = $installer->getConnection();
-               if ( !$status->isOK() ) {
-                       // Adjust the error message to explain things correctly
-                       $status->replaceMessage( 'config-connection-error',
-                               'config-localsettings-connection-error' );
-
-                       return $status;
-               }
-
-               // All good
-               $this->setVar( '_ExistingDBSettings', true );
-
-               return $status;
-       }
-
-}
-
-class WebInstallerWelcome extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       public function execute() {
-               if ( $this->parent->request->wasPosted() ) {
-                       if ( $this->getVar( '_Environment' ) ) {
-                               return 'continue';
-                       }
-               }
-               $this->parent->output->addWikiText( wfMessage( 'config-welcome' )->plain() );
-               $status = $this->parent->doEnvironmentChecks();
-               if ( $status->isGood() ) {
-                       $this->parent->output->addHTML( '<span class="success-message">' .
-                               wfMessage( 'config-env-good' )->escaped() . '</span>' );
-                       $this->parent->output->addWikiText( wfMessage( 'config-copyright',
-                               SpecialVersion::getCopyrightAndAuthorList() )->plain() );
-                       $this->startForm();
-                       $this->endForm();
-               } else {
-                       $this->parent->showStatusMessage( $status );
-               }
-
-               return '';
-       }
-
-}
-
-class WebInstallerDBConnect extends WebInstallerPage {
-
-       /**
-        * @return string|null When string, "skip" or "continue"
-        */
-       public function execute() {
-               if ( $this->getVar( '_ExistingDBSettings' ) ) {
-                       return 'skip';
-               }
-
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $status = $this->submit();
-
-                       if ( $status->isGood() ) {
-                               $this->setVar( '_UpgradeDone', false );
-
-                               return 'continue';
-                       } else {
-                               $this->parent->showStatusBox( $status );
-                       }
-               }
-
-               $this->startForm();
-
-               $types = "<ul class=\"config-settings-block\">\n";
-               $settings = '';
-               $defaultType = $this->getVar( 'wgDBtype' );
-
-               // Messages: config-dbsupport-mysql, config-dbsupport-postgres, config-dbsupport-oracle,
-               // config-dbsupport-sqlite, config-dbsupport-mssql
-               $dbSupport = '';
-               foreach ( Installer::getDBTypes() as $type ) {
-                       $dbSupport .= wfMessage( "config-dbsupport-$type" )->plain() . "\n";
-               }
-               $this->addHTML( $this->parent->getInfoBox(
-                       wfMessage( 'config-support-info', trim( $dbSupport ) )->text() ) );
-
-               // It's possible that the library for the default DB type is not compiled in.
-               // In that case, instead select the first supported DB type in the list.
-               $compiledDBs = $this->parent->getCompiledDBs();
-               if ( !in_array( $defaultType, $compiledDBs ) ) {
-                       $defaultType = $compiledDBs[0];
-               }
-
-               foreach ( $compiledDBs as $type ) {
-                       $installer = $this->parent->getDBInstaller( $type );
-                       $types .=
-                               '<li>' .
-                               Xml::radioLabel(
-                                       $installer->getReadableName(),
-                                       'DBType',
-                                       $type,
-                                       "DBType_$type",
-                                       $type == $defaultType,
-                                       array( 'class' => 'dbRadio', 'rel' => "DB_wrapper_$type" )
-                               ) .
-                               "</li>\n";
-
-                       // Messages: config-header-mysql, config-header-postgres, config-header-oracle,
-                       // config-header-sqlite
-                       $settings .= Html::openElement(
-                                       'div',
-                                       array(
-                                               'id' => 'DB_wrapper_' . $type,
-                                               'class' => 'dbWrapper'
-                                       )
-                               ) .
-                               Html::element( 'h3', array(), wfMessage( 'config-header-' . $type )->text() ) .
-                               $installer->getConnectForm() .
-                               "</div>\n";
-               }
-
-               $types .= "</ul><br style=\"clear: left\"/>\n";
-
-               $this->addHTML( $this->parent->label( 'config-db-type', false, $types ) . $settings );
-               $this->endForm();
-
-               return null;
-       }
-
-       /**
-        * @return Status
-        */
-       public function submit() {
-               $r = $this->parent->request;
-               $type = $r->getVal( 'DBType' );
-               if ( !$type ) {
-                       return Status::newFatal( 'config-invalid-db-type' );
-               }
-               $this->setVar( 'wgDBtype', $type );
-               $installer = $this->parent->getDBInstaller( $type );
-               if ( !$installer ) {
-                       return Status::newFatal( 'config-invalid-db-type' );
-               }
-
-               return $installer->submitConnectForm();
-       }
-
-}
-
-class WebInstallerUpgrade extends WebInstallerPage {
-
-       /**
-        * @return bool Always true.
-        */
-       public function isSlow() {
-               return true;
-       }
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               if ( $this->getVar( '_UpgradeDone' ) ) {
-                       // Allow regeneration of LocalSettings.php, unless we are working
-                       // from a pre-existing LocalSettings.php file and we want to avoid
-                       // leaking its contents
-                       if ( $this->parent->request->wasPosted() && !$this->getVar( '_ExistingDBSettings' ) ) {
-                               // Done message acknowledged
-                               return 'continue';
-                       } else {
-                               // Back button click
-                               // Show the done message again
-                               // Make them click back again if they want to do the upgrade again
-                               $this->showDoneMessage();
-
-                               return 'output';
-                       }
-               }
-
-               // wgDBtype is generally valid here because otherwise the previous page
-               // (connect) wouldn't have declared its happiness
-               $type = $this->getVar( 'wgDBtype' );
-               $installer = $this->parent->getDBInstaller( $type );
-
-               if ( !$installer->needsUpgrade() ) {
-                       return 'skip';
-               }
-
-               if ( $this->parent->request->wasPosted() ) {
-                       $installer->preUpgrade();
-
-                       $this->startLiveBox();
-                       $result = $installer->doUpgrade();
-                       $this->endLiveBox();
-
-                       if ( $result ) {
-                               // If they're going to possibly regenerate LocalSettings, we
-                               // need to create the upgrade/secret keys. Bug 26481
-                               if ( !$this->getVar( '_ExistingDBSettings' ) ) {
-                                       $this->parent->generateKeys();
-                               }
-                               $this->setVar( '_UpgradeDone', true );
-                               $this->showDoneMessage();
-
-                               return 'output';
-                       }
-               }
-
-               $this->startForm();
-               $this->addHTML( $this->parent->getInfoBox(
-                       wfMessage( 'config-can-upgrade', $GLOBALS['wgVersion'] )->plain() ) );
-               $this->endForm();
-
-               return null;
-       }
-
-       public function showDoneMessage() {
-               $this->startForm();
-               $regenerate = !$this->getVar( '_ExistingDBSettings' );
-               if ( $regenerate ) {
-                       $msg = 'config-upgrade-done';
-               } else {
-                       $msg = 'config-upgrade-done-no-regenerate';
-               }
-               $this->parent->disableLinkPopups();
-               $this->addHTML(
-                       $this->parent->getInfoBox(
-                               wfMessage( $msg,
-                                       $this->getVar( 'wgServer' ) .
-                                       $this->getVar( 'wgScriptPath' ) . '/index.php'
-                               )->plain(), 'tick-32.png'
-                       )
-               );
-               $this->parent->restoreLinkPopups();
-               $this->endForm( $regenerate ? 'regenerate' : false, false );
-       }
-
-}
-
-class WebInstallerDBSettings extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               $installer = $this->parent->getDBInstaller( $this->getVar( 'wgDBtype' ) );
-
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $status = $installer->submitSettingsForm();
-                       if ( $status === false ) {
-                               return 'skip';
-                       } elseif ( $status->isGood() ) {
-                               return 'continue';
-                       } else {
-                               $this->parent->showStatusBox( $status );
-                       }
-               }
-
-               $form = $installer->getSettingsForm();
-               if ( $form === false ) {
-                       return 'skip';
-               }
-
-               $this->startForm();
-               $this->addHTML( $form );
-               $this->endForm();
-
-               return null;
-       }
-
-}
-
-class WebInstallerName extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       public function execute() {
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       if ( $this->submit() ) {
-                               return 'continue';
-                       }
-               }
-
-               $this->startForm();
-
-               // Encourage people to not name their site 'MediaWiki' by blanking the
-               // field. I think that was the intent with the original $GLOBALS['wgSitename']
-               // but these two always were the same so had the effect of making the
-               // installer forget $wgSitename when navigating back to this page.
-               if ( $this->getVar( 'wgSitename' ) == 'MediaWiki' ) {
-                       $this->setVar( 'wgSitename', '' );
-               }
-
-               // Set wgMetaNamespace to something valid before we show the form.
-               // $wgMetaNamespace defaults to $wgSiteName which is 'MediaWiki'
-               $metaNS = $this->getVar( 'wgMetaNamespace' );
-               $this->setVar(
-                       'wgMetaNamespace',
-                       wfMessage( 'config-ns-other-default' )->inContentLanguage()->text()
-               );
-
-               $this->addHTML(
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgSitename',
-                               'label' => 'config-site-name',
-                               'help' => $this->parent->getHelpBox( 'config-site-name-help' )
-                       ) ) .
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-ns-site-name, config-ns-generic, config-ns-other
-                       $this->parent->getRadioSet( array(
-                               'var' => '_NamespaceType',
-                               'label' => 'config-project-namespace',
-                               'itemLabelPrefix' => 'config-ns-',
-                               'values' => array( 'site-name', 'generic', 'other' ),
-                               'commonAttribs' => array( 'class' => 'enableForOther',
-                                       'rel' => 'config_wgMetaNamespace' ),
-                               'help' => $this->parent->getHelpBox( 'config-project-namespace-help' )
-                       ) ) .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgMetaNamespace',
-                               'label' => '', // @todo Needs a label?
-                               'attribs' => array( 'readonly' => 'readonly', 'class' => 'enabledByOther' )
-                       ) ) .
-                       $this->getFieldSetStart( 'config-admin-box' ) .
-                       $this->parent->getTextBox( array(
-                               'var' => '_AdminName',
-                               'label' => 'config-admin-name',
-                               'help' => $this->parent->getHelpBox( 'config-admin-help' )
-                       ) ) .
-                       $this->parent->getPasswordBox( array(
-                               'var' => '_AdminPassword',
-                               'label' => 'config-admin-password',
-                       ) ) .
-                       $this->parent->getPasswordBox( array(
-                               'var' => '_AdminPasswordConfirm',
-                               'label' => 'config-admin-password-confirm'
-                       ) ) .
-                       $this->parent->getTextBox( array(
-                               'var' => '_AdminEmail',
-                               'attribs' => array(
-                                       'dir' => 'ltr',
-                               ),
-                               'label' => 'config-admin-email',
-                               'help' => $this->parent->getHelpBox( 'config-admin-email-help' )
-                       ) ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => '_Subscribe',
-                               'label' => 'config-subscribe',
-                               'help' => $this->parent->getHelpBox( 'config-subscribe-help' )
-                       ) ) .
-                       $this->getFieldSetEnd() .
-                       $this->parent->getInfoBox( wfMessage( 'config-almost-done' )->text() ) .
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-optional-continue, config-optional-skip
-                       $this->parent->getRadioSet( array(
-                               'var' => '_SkipOptional',
-                               'itemLabelPrefix' => 'config-optional-',
-                               'values' => array( 'continue', 'skip' )
-                       ) )
-               );
-
-               // Restore the default value
-               $this->setVar( 'wgMetaNamespace', $metaNS );
-
-               $this->endForm();
-
-               return 'output';
-       }
-
-       /**
-        * @return bool
-        */
-       public function submit() {
-               global $wgPasswordPolicy;
-
-               $retVal = true;
-               $this->parent->setVarsFromRequest( array( 'wgSitename', '_NamespaceType',
-                       '_AdminName', '_AdminPassword', '_AdminPasswordConfirm', '_AdminEmail',
-                       '_Subscribe', '_SkipOptional', 'wgMetaNamespace' ) );
-
-               // Validate site name
-               if ( strval( $this->getVar( 'wgSitename' ) ) === '' ) {
-                       $this->parent->showError( 'config-site-name-blank' );
-                       $retVal = false;
-               }
-
-               // Fetch namespace
-               $nsType = $this->getVar( '_NamespaceType' );
-               if ( $nsType == 'site-name' ) {
-                       $name = $this->getVar( 'wgSitename' );
-                       // Sanitize for namespace
-                       // This algorithm should match the JS one in WebInstallerOutput.php
-                       $name = preg_replace( '/[\[\]\{\}|#<>%+? ]/', '_', $name );
-                       $name = str_replace( '&', '&amp;', $name );
-                       $name = preg_replace( '/__+/', '_', $name );
-                       $name = ucfirst( trim( $name, '_' ) );
-               } elseif ( $nsType == 'generic' ) {
-                       $name = wfMessage( 'config-ns-generic' )->text();
-               } else { // other
-                       $name = $this->getVar( 'wgMetaNamespace' );
-               }
-
-               // Validate namespace
-               if ( strpos( $name, ':' ) !== false ) {
-                       $good = false;
-               } else {
-                       // Title-style validation
-                       $title = Title::newFromText( $name );
-                       if ( !$title ) {
-                               $good = $nsType == 'site-name';
-                       } else {
-                               $name = $title->getDBkey();
-                               $good = true;
-                       }
-               }
-               if ( !$good ) {
-                       $this->parent->showError( 'config-ns-invalid', $name );
-                       $retVal = false;
-               }
-
-               // Make sure it won't conflict with any existing namespaces
-               global $wgContLang;
-               $nsIndex = $wgContLang->getNsIndex( $name );
-               if ( $nsIndex !== false && $nsIndex !== NS_PROJECT ) {
-                       $this->parent->showError( 'config-ns-conflict', $name );
-                       $retVal = false;
-               }
-
-               $this->setVar( 'wgMetaNamespace', $name );
-
-               // Validate username for creation
-               $name = $this->getVar( '_AdminName' );
-               if ( strval( $name ) === '' ) {
-                       $this->parent->showError( 'config-admin-name-blank' );
-                       $cname = $name;
-                       $retVal = false;
-               } else {
-                       $cname = User::getCanonicalName( $name, 'creatable' );
-                       if ( $cname === false ) {
-                               $this->parent->showError( 'config-admin-name-invalid', $name );
-                               $retVal = false;
-                       } else {
-                               $this->setVar( '_AdminName', $cname );
-                       }
-               }
-
-               // Validate password
-               $msg = false;
-               $pwd = $this->getVar( '_AdminPassword' );
-               $user = User::newFromName( $cname );
-               if ( $user ) {
-                       $upp = new UserPasswordPolicy(
-                               $wgPasswordPolicy['policies'],
-                               $wgPasswordPolicy['checks']
-                       );
-                       $status = $upp->checkUserPasswordForGroups(
-                               $user,
-                               $pwd,
-                               array( 'bureaucrat', 'sysop' )  // per Installer::createSysop()
-                       );
-                       $valid = $status->isGood() ? true : $status->getMessage();
-               } else {
-                       $valid = 'config-admin-name-invalid';
-               }
-               if ( strval( $pwd ) === '' ) {
-                       // Provide a more specific and helpful message if password field is left blank
-                       $msg = 'config-admin-password-blank';
-               } elseif ( $pwd !== $this->getVar( '_AdminPasswordConfirm' ) ) {
-                       $msg = 'config-admin-password-mismatch';
-               } elseif ( $valid !== true ) {
-                       $msg = $valid;
-               }
-               if ( $msg !== false ) {
-                       call_user_func( array( $this->parent, 'showError' ), $msg );
-                       $this->setVar( '_AdminPassword', '' );
-                       $this->setVar( '_AdminPasswordConfirm', '' );
-                       $retVal = false;
-               }
-
-               // Validate e-mail if provided
-               $email = $this->getVar( '_AdminEmail' );
-               if ( $email && !Sanitizer::validateEmail( $email ) ) {
-                       $this->parent->showError( 'config-admin-error-bademail' );
-                       $retVal = false;
-               }
-               // If they asked to subscribe to mediawiki-announce but didn't give
-               // an e-mail, show an error. Bug 29332
-               if ( !$email && $this->getVar( '_Subscribe' ) ) {
-                       $this->parent->showError( 'config-subscribe-noemail' );
-                       $retVal = false;
-               }
-
-               return $retVal;
-       }
-
-}
-
-class WebInstallerOptions extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               if ( $this->getVar( '_SkipOptional' ) == 'skip' ) {
-                       $this->submitSkins();
-                       return 'skip';
-               }
-               if ( $this->parent->request->wasPosted() ) {
-                       if ( $this->submit() ) {
-                               return 'continue';
-                       }
-               }
-
-               $emailwrapperStyle = $this->getVar( 'wgEnableEmail' ) ? '' : 'display: none';
-               $this->startForm();
-               $this->addHTML(
-                       # User Rights
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-profile-wiki, config-profile-no-anon, config-profile-fishbowl, config-profile-private
-                       $this->parent->getRadioSet( array(
-                               'var' => '_RightsProfile',
-                               'label' => 'config-profile',
-                               'itemLabelPrefix' => 'config-profile-',
-                               'values' => array_keys( $this->parent->rightsProfiles ),
-                       ) ) .
-                       $this->parent->getInfoBox( wfMessage( 'config-profile-help' )->plain() ) .
-
-                       # Licensing
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
-                       // config-license-cc-0, config-license-pd, config-license-gfdl,
-                       // config-license-none, config-license-cc-choose
-                       $this->parent->getRadioSet( array(
-                               'var' => '_LicenseCode',
-                               'label' => 'config-license',
-                               'itemLabelPrefix' => 'config-license-',
-                               'values' => array_keys( $this->parent->licenses ),
-                               'commonAttribs' => array( 'class' => 'licenseRadio' ),
-                       ) ) .
-                       $this->getCCChooser() .
-                       $this->parent->getHelpBox( 'config-license-help' ) .
-
-                       # E-mail
-                       $this->getFieldSetStart( 'config-email-settings' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnableEmail',
-                               'label' => 'config-enable-email',
-                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'emailwrapper' ),
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-enable-email-help' ) .
-                       "<div id=\"emailwrapper\" style=\"$emailwrapperStyle\">" .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgPasswordSender',
-                               'label' => 'config-email-sender'
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-sender-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnableUserEmail',
-                               'label' => 'config-email-user',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-user-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnotifUserTalk',
-                               'label' => 'config-email-usertalk',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-usertalk-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnotifWatchlist',
-                               'label' => 'config-email-watchlist',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-watchlist-help' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEmailAuthentication',
-                               'label' => 'config-email-auth',
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-email-auth-help' ) .
-                       "</div>" .
-                       $this->getFieldSetEnd()
-               );
-
-               $skins = $this->parent->findExtensions( 'skins' );
-               $skinHtml = $this->getFieldSetStart( 'config-skins' );
-
-               $skinNames = array_map( 'strtolower', $skins );
-               $chosenSkinName = $this->getVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
-
-               if ( $skins ) {
-                       $radioButtons = $this->parent->getRadioElements( array(
-                               'var' => 'wgDefaultSkin',
-                               'itemLabels' => array_fill_keys( $skinNames, 'config-skins-use-as-default' ),
-                               'values' => $skinNames,
-                               'value' => $chosenSkinName,
-                       ) );
-
-                       foreach ( $skins as $skin ) {
-                               $skinHtml .=
-                                       '<div class="config-skins-item">' .
-                                       $this->parent->getCheckBox( array(
-                                               'var' => "skin-$skin",
-                                               'rawtext' => $skin,
-                                               'value' => $this->getVar( "skin-$skin", true ), // all found skins enabled by default
-                                       ) ) .
-                                       '<div class="config-skins-use-as-default">' . $radioButtons[strtolower( $skin )] . '</div>' .
-                                       '</div>';
-                       }
-               } else {
-                       $skinHtml .=
-                               $this->parent->getWarningBox( wfMessage( 'config-skins-missing' )->plain() ) .
-                               Html::hidden( 'config_wgDefaultSkin', $chosenSkinName );
-               }
-
-               $skinHtml .= $this->parent->getHelpBox( 'config-skins-help' ) .
-                       $this->getFieldSetEnd();
-               $this->addHTML( $skinHtml );
-
-               $extensions = $this->parent->findExtensions();
-
-               if ( $extensions ) {
-                       $extHtml = $this->getFieldSetStart( 'config-extensions' );
-
-                       foreach ( $extensions as $ext ) {
-                               $extHtml .= $this->parent->getCheckBox( array(
-                                       'var' => "ext-$ext",
-                                       'rawtext' => $ext,
-                               ) );
-                       }
-
-                       $extHtml .= $this->parent->getHelpBox( 'config-extensions-help' ) .
-                               $this->getFieldSetEnd();
-                       $this->addHTML( $extHtml );
-               }
-
-               // Having / in paths in Windows looks funny :)
-               $this->setVar( 'wgDeletedDirectory',
-                       str_replace(
-                               '/', DIRECTORY_SEPARATOR,
-                               $this->getVar( 'wgDeletedDirectory' )
-                       )
-               );
-
-               $uploadwrapperStyle = $this->getVar( 'wgEnableUploads' ) ? '' : 'display: none';
-               $this->addHTML(
-                       # Uploading
-                       $this->getFieldSetStart( 'config-upload-settings' ) .
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgEnableUploads',
-                               'label' => 'config-upload-enable',
-                               'attribs' => array( 'class' => 'showHideRadio', 'rel' => 'uploadwrapper' ),
-                               'help' => $this->parent->getHelpBox( 'config-upload-help' )
-                       ) ) .
-                       '<div id="uploadwrapper" style="' . $uploadwrapperStyle . '">' .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgDeletedDirectory',
-                               'label' => 'config-upload-deleted',
-                               'attribs' => array( 'dir' => 'ltr' ),
-                               'help' => $this->parent->getHelpBox( 'config-upload-deleted-help' )
-                       ) ) .
-                       '</div>' .
-                       $this->parent->getTextBox( array(
-                               'var' => 'wgLogo',
-                               'label' => 'config-logo',
-                               'attribs' => array( 'dir' => 'ltr' ),
-                               'help' => $this->parent->getHelpBox( 'config-logo-help' )
-                       ) )
-               );
-               $this->addHTML(
-                       $this->parent->getCheckBox( array(
-                               'var' => 'wgUseInstantCommons',
-                               'label' => 'config-instantcommons',
-                               'help' => $this->parent->getHelpBox( 'config-instantcommons-help' )
-                       ) ) .
-                       $this->getFieldSetEnd()
-               );
-
-               $caches = array( 'none' );
-               if ( count( $this->getVar( '_Caches' ) ) ) {
-                       $caches[] = 'accel';
-               }
-               $caches[] = 'memcached';
-
-               // We'll hide/show this on demand when the value changes, see config.js.
-               $cacheval = $this->getVar( '_MainCacheType' );
-               if ( !$cacheval ) {
-                       // We need to set a default here; but don't hardcode it
-                       // or we lose it every time we reload the page for validation
-                       // or going back!
-                       $cacheval = 'none';
-               }
-               $hidden = ( $cacheval == 'memcached' ) ? '' : 'display: none';
-               $this->addHTML(
-                       # Advanced settings
-                       $this->getFieldSetStart( 'config-advanced-settings' ) .
-                       # Object cache settings
-                       // getRadioSet() builds a set of labeled radio buttons.
-                       // For grep: The following messages are used as the item labels:
-                       // config-cache-none, config-cache-accel, config-cache-memcached
-                       $this->parent->getRadioSet( array(
-                               'var' => '_MainCacheType',
-                               'label' => 'config-cache-options',
-                               'itemLabelPrefix' => 'config-cache-',
-                               'values' => $caches,
-                               'value' => $cacheval,
-                       ) ) .
-                       $this->parent->getHelpBox( 'config-cache-help' ) .
-                       "<div id=\"config-memcachewrapper\" style=\"$hidden\">" .
-                       $this->parent->getTextArea( array(
-                               'var' => '_MemCachedServers',
-                               'label' => 'config-memcached-servers',
-                               'help' => $this->parent->getHelpBox( 'config-memcached-help' )
-                       ) ) .
-                       '</div>' .
-                       $this->getFieldSetEnd()
-               );
-               $this->endForm();
-
-               return null;
-       }
-
-       /**
-        * @return string
-        */
-       public function getCCPartnerUrl() {
-               $server = $this->getVar( 'wgServer' );
-               $exitUrl = $server . $this->parent->getUrl( array(
-                       'page' => 'Options',
-                       'SubmitCC' => 'indeed',
-                       'config__LicenseCode' => 'cc',
-                       'config_wgRightsUrl' => '[license_url]',
-                       'config_wgRightsText' => '[license_name]',
-                       'config_wgRightsIcon' => '[license_button]',
-               ) );
-               $styleUrl = $server . dirname( dirname( $this->parent->getUrl() ) ) .
-                       '/mw-config/config-cc.css';
-               $iframeUrl = '//creativecommons.org/license/?' .
-                       wfArrayToCgi( array(
-                               'partner' => 'MediaWiki',
-                               'exit_url' => $exitUrl,
-                               'lang' => $this->getVar( '_UserLang' ),
-                               'stylesheet' => $styleUrl,
-                       ) );
-
-               return $iframeUrl;
-       }
-
-       /**
-        * @return string
-        */
-       public function getCCChooser() {
-               $iframeAttribs = array(
-                       'class' => 'config-cc-iframe',
-                       'name' => 'config-cc-iframe',
-                       'id' => 'config-cc-iframe',
-                       'frameborder' => 0,
-                       'width' => '100%',
-                       'height' => '100%',
-               );
-               if ( $this->getVar( '_CCDone' ) ) {
-                       $iframeAttribs['src'] = $this->parent->getUrl( array( 'ShowCC' => 'yes' ) );
-               } else {
-                       $iframeAttribs['src'] = $this->getCCPartnerUrl();
-               }
-               $wrapperStyle = ( $this->getVar( '_LicenseCode' ) == 'cc-choose' ) ? '' : 'display: none';
-
-               return "<div class=\"config-cc-wrapper\" id=\"config-cc-wrapper\" style=\"$wrapperStyle\">\n" .
-                       Html::element( 'iframe', $iframeAttribs, '', false /* not short */ ) .
-                       "</div>\n";
-       }
-
-       /**
-        * @return string
-        */
-       public function getCCDoneBox() {
-               $js = "parent.document.getElementById('config-cc-wrapper').style.height = '$1';";
-               // If you change this height, also change it in config.css
-               $expandJs = str_replace( '$1', '54em', $js );
-               $reduceJs = str_replace( '$1', '70px', $js );
-
-               return '<p>' .
-                       Html::element( 'img', array( 'src' => $this->getVar( 'wgRightsIcon' ) ) ) .
-                       '&#160;&#160;' .
-                       htmlspecialchars( $this->getVar( 'wgRightsText' ) ) .
-                       "</p>\n" .
-                       "<p style=\"text-align: center;\">" .
-                       Html::element( 'a',
-                               array(
-                                       'href' => $this->getCCPartnerUrl(),
-                                       'onclick' => $expandJs,
-                               ),
-                               wfMessage( 'config-cc-again' )->text()
-                       ) .
-                       "</p>\n" .
-                       "<script>\n" .
-                       # Reduce the wrapper div height
-                       htmlspecialchars( $reduceJs ) .
-                       "\n" .
-                       "</script>\n";
-       }
-
-       public function submitCC() {
-               $newValues = $this->parent->setVarsFromRequest(
-                       array( 'wgRightsUrl', 'wgRightsText', 'wgRightsIcon' ) );
-               if ( count( $newValues ) != 3 ) {
-                       $this->parent->showError( 'config-cc-error' );
-
-                       return;
-               }
-               $this->setVar( '_CCDone', true );
-               $this->addHTML( $this->getCCDoneBox() );
-       }
-
-       /**
-        * If the user skips this installer page, we still need to set up the default skins, but ignore
-        * everything else.
-        *
-        * @return bool
-        */
-       public function submitSkins() {
-               $skins = $this->parent->findExtensions( 'skins' );
-               $this->parent->setVar( '_Skins', $skins );
-
-               if ( $skins ) {
-                       $skinNames = array_map( 'strtolower', $skins );
-                       $this->parent->setVar( 'wgDefaultSkin', $this->parent->getDefaultSkin( $skinNames ) );
-               }
-
-               return true;
-       }
-
-       /**
-        * @return bool
-        */
-       public function submit() {
-               $this->parent->setVarsFromRequest( array( '_RightsProfile', '_LicenseCode',
-                       'wgEnableEmail', 'wgPasswordSender', 'wgEnableUploads', 'wgLogo',
-                       'wgEnableUserEmail', 'wgEnotifUserTalk', 'wgEnotifWatchlist',
-                       'wgEmailAuthentication', '_MainCacheType', '_MemCachedServers',
-                       'wgUseInstantCommons', 'wgDefaultSkin' ) );
-
-               $retVal = true;
-
-               if ( !array_key_exists( $this->getVar( '_RightsProfile' ), $this->parent->rightsProfiles ) ) {
-                       reset( $this->parent->rightsProfiles );
-                       $this->setVar( '_RightsProfile', key( $this->parent->rightsProfiles ) );
-               }
-
-               $code = $this->getVar( '_LicenseCode' );
-               if ( $code == 'cc-choose' ) {
-                       if ( !$this->getVar( '_CCDone' ) ) {
-                               $this->parent->showError( 'config-cc-not-chosen' );
-                               $retVal = false;
-                       }
-               } elseif ( array_key_exists( $code, $this->parent->licenses ) ) {
-                       // Messages:
-                       // config-license-cc-by, config-license-cc-by-sa, config-license-cc-by-nc-sa,
-                       // config-license-cc-0, config-license-pd, config-license-gfdl, config-license-none,
-                       // config-license-cc-choose
-                       $entry = $this->parent->licenses[$code];
-                       if ( isset( $entry['text'] ) ) {
-                               $this->setVar( 'wgRightsText', $entry['text'] );
-                       } else {
-                               $this->setVar( 'wgRightsText', wfMessage( 'config-license-' . $code )->text() );
-                       }
-                       $this->setVar( 'wgRightsUrl', $entry['url'] );
-                       $this->setVar( 'wgRightsIcon', $entry['icon'] );
-               } else {
-                       $this->setVar( 'wgRightsText', '' );
-                       $this->setVar( 'wgRightsUrl', '' );
-                       $this->setVar( 'wgRightsIcon', '' );
-               }
-
-               $skinsAvailable = $this->parent->findExtensions( 'skins' );
-               $skinsToInstall = array();
-               foreach ( $skinsAvailable as $skin ) {
-                       $this->parent->setVarsFromRequest( array( "skin-$skin" ) );
-                       if ( $this->getVar( "skin-$skin" ) ) {
-                               $skinsToInstall[] = $skin;
-                       }
-               }
-               $this->parent->setVar( '_Skins', $skinsToInstall );
-
-               if ( !$skinsToInstall && $skinsAvailable ) {
-                       $this->parent->showError( 'config-skins-must-enable-some' );
-                       $retVal = false;
-               }
-               $defaultSkin = $this->getVar( 'wgDefaultSkin' );
-               $skinsToInstallLowercase = array_map( 'strtolower', $skinsToInstall );
-               if ( $skinsToInstall && array_search( $defaultSkin, $skinsToInstallLowercase ) === false ) {
-                       $this->parent->showError( 'config-skins-must-enable-default' );
-                       $retVal = false;
-               }
-
-               $extsAvailable = $this->parent->findExtensions();
-               $extsToInstall = array();
-               foreach ( $extsAvailable as $ext ) {
-                       $this->parent->setVarsFromRequest( array( "ext-$ext" ) );
-                       if ( $this->getVar( "ext-$ext" ) ) {
-                               $extsToInstall[] = $ext;
-                       }
-               }
-               $this->parent->setVar( '_Extensions', $extsToInstall );
-
-               if ( $this->getVar( '_MainCacheType' ) == 'memcached' ) {
-                       $memcServers = explode( "\n", $this->getVar( '_MemCachedServers' ) );
-                       if ( !$memcServers ) {
-                               $this->parent->showError( 'config-memcache-needservers' );
-                               $retVal = false;
-                       }
-
-                       foreach ( $memcServers as $server ) {
-                               $memcParts = explode( ":", $server, 2 );
-                               if ( !isset( $memcParts[0] )
-                                       || ( !IP::isValid( $memcParts[0] )
-                                               && ( gethostbyname( $memcParts[0] ) == $memcParts[0] ) )
-                               ) {
-                                       $this->parent->showError( 'config-memcache-badip', $memcParts[0] );
-                                       $retVal = false;
-                               } elseif ( !isset( $memcParts[1] ) ) {
-                                       $this->parent->showError( 'config-memcache-noport', $memcParts[0] );
-                                       $retVal = false;
-                               } elseif ( $memcParts[1] < 1 || $memcParts[1] > 65535 ) {
-                                       $this->parent->showError( 'config-memcache-badport', 1, 65535 );
-                                       $retVal = false;
-                               }
-                       }
-               }
-
-               return $retVal;
-       }
-
-}
-
-class WebInstallerInstall extends WebInstallerPage {
-
-       /**
-        * @return bool Always true.
-        */
-       public function isSlow() {
-               return true;
-       }
-
-       /**
-        * @return string|bool
-        */
-       public function execute() {
-               if ( $this->getVar( '_UpgradeDone' ) ) {
-                       return 'skip';
-               } elseif ( $this->getVar( '_InstallDone' ) ) {
-                       return 'continue';
-               } elseif ( $this->parent->request->wasPosted() ) {
-                       $this->startForm();
-                       $this->addHTML( "<ul>" );
-                       $results = $this->parent->performInstallation(
-                               array( $this, 'startStage' ),
-                               array( $this, 'endStage' )
-                       );
-                       $this->addHTML( "</ul>" );
-                       // PerformInstallation bails on a fatal, so make sure the last item
-                       // completed before giving 'next.' Likewise, only provide back on failure
-                       $lastStep = end( $results );
-                       $continue = $lastStep->isOK() ? 'continue' : false;
-                       $back = $lastStep->isOK() ? false : 'back';
-                       $this->endForm( $continue, $back );
-               } else {
-                       $this->startForm();
-                       $this->addHTML( $this->parent->getInfoBox( wfMessage( 'config-install-begin' )->plain() ) );
-                       $this->endForm();
-               }
-
-               return true;
-       }
-
-       /**
-        * @param string $step
-        */
-       public function startStage( $step ) {
-               // Messages: config-install-database, config-install-tables, config-install-interwiki,
-               // config-install-stats, config-install-keys, config-install-sysop, config-install-mainpage
-               $this->addHTML( "<li>" . wfMessage( "config-install-$step" )->escaped() .
-                       wfMessage( 'ellipsis' )->escaped() );
-
-               if ( $step == 'extension-tables' ) {
-                       $this->startLiveBox();
-               }
-       }
-
-       /**
-        * @param string $step
-        * @param Status $status
-        */
-       public function endStage( $step, $status ) {
-               if ( $step == 'extension-tables' ) {
-                       $this->endLiveBox();
-               }
-               $msg = $status->isOk() ? 'config-install-step-done' : 'config-install-step-failed';
-               $html = wfMessage( 'word-separator' )->escaped() . wfMessage( $msg )->escaped();
-               if ( !$status->isOk() ) {
-                       $html = "<span class=\"error\">$html</span>";
-               }
-               $this->addHTML( $html . "</li>\n" );
-               if ( !$status->isGood() ) {
-                       $this->parent->showStatusBox( $status );
-               }
-       }
-
-}
-
-class WebInstallerComplete extends WebInstallerPage {
-
-       public function execute() {
-               // Pop up a dialog box, to make it difficult for the user to forget
-               // to download the file
-               $lsUrl = $this->getVar( 'wgServer' ) . $this->parent->getURL( array( 'localsettings' => 1 ) );
-               if ( isset( $_SERVER['HTTP_USER_AGENT'] ) &&
-                       strpos( $_SERVER['HTTP_USER_AGENT'], 'MSIE' ) !== false
-               ) {
-                       // JS appears to be the only method that works consistently with IE7+
-                       $this->addHtml( "\n<script>jQuery( function () { location.href = " .
-                               Xml::encodeJsVar( $lsUrl ) . "; } );</script>\n" );
-               } else {
-                       $this->parent->request->response()->header( "Refresh: 0;url=$lsUrl" );
-               }
-
-               $this->startForm();
-               $this->parent->disableLinkPopups();
-               $this->addHTML(
-                       $this->parent->getInfoBox(
-                               wfMessage( 'config-install-done',
-                                       $lsUrl,
-                                       $this->getVar( 'wgServer' ) .
-                                       $this->getVar( 'wgScriptPath' ) . '/index.php',
-                                       '<downloadlink/>'
-                               )->plain(), 'tick-32.png'
-                       )
-               );
-               $this->addHTML( $this->parent->getInfoBox(
-                       wfMessage( 'config-extension-link' )->text() ) );
-
-               $this->parent->restoreLinkPopups();
-               $this->endForm( false, false );
-       }
-
-}
-
-class WebInstallerRestart extends WebInstallerPage {
-
-       /**
-        * @return string|null
-        */
-       public function execute() {
-               $r = $this->parent->request;
-               if ( $r->wasPosted() ) {
-                       $really = $r->getVal( 'submit-restart' );
-                       if ( $really ) {
-                               $this->parent->reset();
-                       }
-
-                       return 'continue';
-               }
-
-               $this->startForm();
-               $s = $this->parent->getWarningBox( wfMessage( 'config-help-restart' )->plain() );
-               $this->addHTML( $s );
-               $this->endForm( 'restart' );
-
-               return null;
-       }
-
-}
-
-abstract class WebInstallerDocument extends WebInstallerPage {
-
-       /**
-        * @return string
-        */
-       abstract protected function getFileName();
-
-       public function execute() {
-               $text = $this->getFileContents();
-               $text = InstallDocFormatter::format( $text );
-               $this->parent->output->addWikiText( $text );
-               $this->startForm();
-               $this->endForm( false );
-       }
-
-       /**
-        * @return string
-        */
-       public function getFileContents() {
-               $file = __DIR__ . '/../../' . $this->getFileName();
-               if ( !file_exists( $file ) ) {
-                       return wfMessage( 'config-nofile', $file )->plain();
-               }
-
-               return file_get_contents( $file );
-       }
-
-}
-
-class WebInstallerReadme extends WebInstallerDocument {
-
-       /**
-        * @return string
-        */
-       protected function getFileName() {
-               return 'README';
-       }
-
-}
-
-class WebInstallerReleaseNotes extends WebInstallerDocument {
-
-       /**
-        * @throws MWException
-        * @return string
-        */
-       protected function getFileName() {
-               global $wgVersion;
-
-               if ( !preg_match( '/^(\d+)\.(\d+).*/i', $wgVersion, $result ) ) {
-                       throw new MWException( 'Variable $wgVersion has an invalid value.' );
-               }
-
-               return 'RELEASE-NOTES-' . $result[1] . '.' . $result[2];
-       }
-
-}
-
-class WebInstallerUpgradeDoc extends WebInstallerDocument {
-
-       /**
-        * @return string
-        */
-       protected function getFileName() {
-               return 'UPGRADE';
-       }
-
-}
-
-class WebInstallerCopying extends WebInstallerDocument {
-
-       /**
-        * @return string
-        */
-       protected function getFileName() {
-               return 'COPYING';
-       }
-
-}
diff --git a/includes/installer/WebInstallerReadme.php b/includes/installer/WebInstallerReadme.php
new file mode 100644 (file)
index 0000000..97c9f83
--- /dev/null
@@ -0,0 +1,31 @@
+<?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 Deployment
+ */
+
+class WebInstallerReadme extends WebInstallerDocument {
+
+       /**
+        * @return string
+        */
+       protected function getFileName() {
+               return 'README';
+       }
+
+}
diff --git a/includes/installer/WebInstallerReleaseNotes.php b/includes/installer/WebInstallerReleaseNotes.php
new file mode 100644 (file)
index 0000000..c0a8d71
--- /dev/null
@@ -0,0 +1,38 @@
+<?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 Deployment
+ */
+
+class WebInstallerReleaseNotes extends WebInstallerDocument {
+
+       /**
+        * @throws MWException
+        * @return string
+        */
+       protected function getFileName() {
+               global $wgVersion;
+
+               if ( !preg_match( '/^(\d+)\.(\d+).*/i', $wgVersion, $result ) ) {
+                       throw new MWException( 'Variable $wgVersion has an invalid value.' );
+               }
+
+               return 'RELEASE-NOTES-' . $result[1] . '.' . $result[2];
+       }
+
+}
diff --git a/includes/installer/WebInstallerRestart.php b/includes/installer/WebInstallerRestart.php
new file mode 100644 (file)
index 0000000..be55c32
--- /dev/null
@@ -0,0 +1,46 @@
+<?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 Deployment
+ */
+
+class WebInstallerRestart extends WebInstallerPage {
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               $r = $this->parent->request;
+               if ( $r->wasPosted() ) {
+                       $really = $r->getVal( 'submit-restart' );
+                       if ( $really ) {
+                               $this->parent->reset();
+                       }
+
+                       return 'continue';
+               }
+
+               $this->startForm();
+               $s = $this->parent->getWarningBox( wfMessage( 'config-help-restart' )->plain() );
+               $this->addHTML( $s );
+               $this->endForm( 'restart' );
+
+               return null;
+       }
+
+}
diff --git a/includes/installer/WebInstallerUpgrade.php b/includes/installer/WebInstallerUpgrade.php
new file mode 100644 (file)
index 0000000..72973e7
--- /dev/null
@@ -0,0 +1,110 @@
+<?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 Deployment
+ */
+
+class WebInstallerUpgrade extends WebInstallerPage {
+
+       /**
+        * @return bool Always true.
+        */
+       public function isSlow() {
+               return true;
+       }
+
+       /**
+        * @return string|null
+        */
+       public function execute() {
+               if ( $this->getVar( '_UpgradeDone' ) ) {
+                       // Allow regeneration of LocalSettings.php, unless we are working
+                       // from a pre-existing LocalSettings.php file and we want to avoid
+                       // leaking its contents
+                       if ( $this->parent->request->wasPosted() && !$this->getVar( '_ExistingDBSettings' ) ) {
+                               // Done message acknowledged
+                               return 'continue';
+                       } else {
+                               // Back button click
+                               // Show the done message again
+                               // Make them click back again if they want to do the upgrade again
+                               $this->showDoneMessage();
+
+                               return 'output';
+                       }
+               }
+
+               // wgDBtype is generally valid here because otherwise the previous page
+               // (connect) wouldn't have declared its happiness
+               $type = $this->getVar( 'wgDBtype' );
+               $installer = $this->parent->getDBInstaller( $type );
+
+               if ( !$installer->needsUpgrade() ) {
+                       return 'skip';
+               }
+
+               if ( $this->parent->request->wasPosted() ) {
+                       $installer->preUpgrade();
+
+                       $this->startLiveBox();
+                       $result = $installer->doUpgrade();
+                       $this->endLiveBox();
+
+                       if ( $result ) {
+                               // If they're going to possibly regenerate LocalSettings, we
+                               // need to create the upgrade/secret keys. Bug 26481
+                               if ( !$this->getVar( '_ExistingDBSettings' ) ) {
+                                       $this->parent->generateKeys();
+                               }
+                               $this->setVar( '_UpgradeDone', true );
+                               $this->showDoneMessage();
+
+                               return 'output';
+                       }
+               }
+
+               $this->startForm();
+               $this->addHTML( $this->parent->getInfoBox(
+                       wfMessage( 'config-can-upgrade', $GLOBALS['wgVersion'] )->plain() ) );
+               $this->endForm();
+
+               return null;
+       }
+
+       public function showDoneMessage() {
+               $this->startForm();
+               $regenerate = !$this->getVar( '_ExistingDBSettings' );
+               if ( $regenerate ) {
+                       $msg = 'config-upgrade-done';
+               } else {
+                       $msg = 'config-upgrade-done-no-regenerate';
+               }
+               $this->parent->disableLinkPopups();
+               $this->addHTML(
+                       $this->parent->getInfoBox(
+                               wfMessage( $msg,
+                                       $this->getVar( 'wgServer' ) .
+                                       $this->getVar( 'wgScriptPath' ) . '/index.php'
+                               )->plain(), 'tick-32.png'
+                       )
+               );
+               $this->parent->restoreLinkPopups();
+               $this->endForm( $regenerate ? 'regenerate' : false, false );
+       }
+
+}
diff --git a/includes/installer/WebInstallerUpgradeDoc.php b/includes/installer/WebInstallerUpgradeDoc.php
new file mode 100644 (file)
index 0000000..f8fa736
--- /dev/null
@@ -0,0 +1,31 @@
+<?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 Deployment
+ */
+
+class WebInstallerUpgradeDoc extends WebInstallerDocument {
+
+       /**
+        * @return string
+        */
+       protected function getFileName() {
+               return 'UPGRADE';
+       }
+
+}
diff --git a/includes/installer/WebInstallerWelcome.php b/includes/installer/WebInstallerWelcome.php
new file mode 100644 (file)
index 0000000..44ff0bb
--- /dev/null
@@ -0,0 +1,49 @@
+<?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 Deployment
+ */
+
+class WebInstallerWelcome extends WebInstallerPage {
+
+       /**
+        * @return string
+        */
+       public function execute() {
+               if ( $this->parent->request->wasPosted() ) {
+                       if ( $this->getVar( '_Environment' ) ) {
+                               return 'continue';
+                       }
+               }
+               $this->parent->output->addWikiText( wfMessage( 'config-welcome' )->plain() );
+               $status = $this->parent->doEnvironmentChecks();
+               if ( $status->isGood() ) {
+                       $this->parent->output->addHTML( '<span class="success-message">' .
+                               wfMessage( 'config-env-good' )->escaped() . '</span>' );
+                       $this->parent->output->addWikiText( wfMessage( 'config-copyright',
+                               SpecialVersion::getCopyrightAndAuthorList() )->plain() );
+                       $this->startForm();
+                       $this->endForm();
+               } else {
+                       $this->parent->showStatusMessage( $status );
+               }
+
+               return '';
+       }
+
+}
index 41fcba6..da2a1c5 100644 (file)
@@ -77,6 +77,7 @@
        "config-charset-mysql5": "MySQL 4.1/5.0 UTF-8",
        "config-type-mysql": "MySQL (o compatible)",
        "config-type-mssql": "Microsoft SQL Server",
+       "config-invalid-db-type": "Triba non válida de base de datos.",
        "config-missing-db-name": "Tienes d'introducir un valor pa «{{int:config-db-name}}».",
        "config-mysql-innodb": "InnoDB",
        "config-mysql-myisam": "MyISAM",
index 06e38d5..7926f5a 100644 (file)
@@ -76,6 +76,7 @@
        "config-apc": "[http://www.php.net/apc APC] усталяваны",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] усталяваны",
        "config-no-cache": "'''Папярэджаньне:''' немагчыма знайсьці [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] ці [http://www.iis.net/download/WinCacheForPhp WinCache].\nАб’ектнае кэшаваньне ня ўключанае.",
+       "config-no-cache-apcu": "<strong>Папярэджаньне:</strong> ня знойдзеныя [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ці [http://www.iis.net/download/WinCacheForPhp WinCache]. Кэшаваньне аб’ектаў адключанае.",
        "config-mod-security": "'''Папярэджаньне''': на Вашым ўэб-сэрверы ўключаны [http://modsecurity.org/ mod_security]. У выпадку няслушнай наладцы, ён можа стаць прычынай праблемаў для MediaWiki ці іншага праграмнага забесьпячэньня, якое дазваляе ўдзельнікам дасылаць на сэрвэр любы зьмест.\nГлядзіце [http://modsecurity.org/documentation/ дакумэнтацыю mod_security] ці зьвярніцеся ў падтрымку Вашага хосту, калі ў Вас узьнікаюць выпадковыя праблемы.",
        "config-diff3-bad": "GNU diff3 ня знойдзены.",
        "config-git": "Знойдзеная сыстэма канстролю вэрсіяў Git: <code>$1</code>",
index 2023b64..f455ced 100644 (file)
@@ -62,7 +62,7 @@
        "config-magic-quotes-sybase": "'''Фатално: [http://www.php.net/manual/en/ref.info.php#ini.magic-quotes-sybase magic_quotes_sybase] е активирана!'''\nТова може да повреди непредвидимо въвеждането на данните.\nИнсталацията на МедияУики е невъзможна докато тази настройка не бъде изключена.",
        "config-mbstring": "'''Фатално: [http://www.php.net/manual/en/ref.mbstring.php#mbstring.overload mbstring.func_overload] е активирана!'''\nТова може да повреди непредвидимо въвеждането на данните.\nИнсталацията на МедияУики е невъзможна докато тази настройка не бъде изключена.",
        "config-safe-mode": "'''Предупреждение:''' PHP работи в [http://www.php.net/features.safe-mode безопасен режим].\nТова може да създаде проблеми, особено ако качването на файлове е разрешено, както и при поддръжката на <code>math</code>.",
-       "config-xml-bad": "Ð\9bипÑ\81ва XML Ð¼Ð¾Ð´Ñ\83лÑ\8aÑ\82 Ð½Ð° PHP.\nÐ\9cедиÑ\8fУики Ñ\81е Ð½Ñ\83ждае Ð¾Ñ\82 Ð½Ñ\8fкои Ñ\84Ñ\83нкÑ\86ии Ð¾Ñ\82 Ñ\82ози Ð¼Ð¾Ð´Ñ\83л Ð¸ Ð½Ñ\8fма Ð´Ð° Ñ\80абоÑ\82и Ð¿Ñ\80и Ð½Ð°Ð»Ð¸Ñ\87наÑ\82а ÐºÐ¾Ð½Ñ\84игÑ\83Ñ\80аÑ\86иÑ\8f.\nÐ\9fÑ\80и Mandrake, Ð½ÐµÐ¾Ð±Ñ\85одимо Ðµ Ð´Ð° Ñ\81е Ð¸Ð½Ñ\81Ñ\82алиÑ\80а Ð¿Ð°ÐºÐµÑ\82Ñ\8aт php-xml.",
+       "config-xml-bad": "Ð\97а PHP Ð»Ð¸Ð¿Ñ\81ва XML Ð¼Ð¾Ð´Ñ\83л.\nÐ\9cедиÑ\8fÑ\83ики Ð½Ñ\8fма Ð´Ð° Ñ\80абоÑ\82и Ð² Ñ\82ази ÐºÐ¾Ð½Ñ\84игÑ\83Ñ\80аÑ\86иÑ\8f, Ñ\82Ñ\8aй ÐºÐ°Ñ\82о Ñ\81е Ð¸Ð·Ð¸Ñ\81ква Ñ\84Ñ\83нкÑ\86ионалноÑ\81Ñ\82 Ð½Ð° Ñ\82ози Ð¼Ð¾Ð´Ñ\83л.\nÐ\9cоже Ð±Ð¸ Ñ\89е Ñ\82Ñ\80Ñ\8fбва Ð´Ð° Ð¸Ð½Ñ\81Ñ\82алиÑ\80аÑ\82е RPM-пакет php-xml.",
        "config-pcre-old": "<strong>Фатална грешка:</strong> Изисква се PCRE версия $1 или по-нова.\nИзпълнимият файл на PHP е свързан с PCRE версия $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/Повече информация за PCRE].",
        "config-pcre-no-utf8": "'''Фатално''': Модулът PCRE на PHP изглежда е компилиран без поддръжка на PCRE_UTF8.\nЗа да функционира правилно, МедияУики изисква поддръжка на UTF-8.",
        "config-memory-raised": "<code>memory_limit</code> на PHP е $1, увеличаване до $2.",
        "config-db-install-account": "Потребителска сметка за инсталацията",
        "config-db-username": "Потребителско име за базата от данни:",
        "config-db-password": "Парола за базата от данни:",
-       "config-db-password-empty": "Въведете парола за новия потребител на базата от данни: $1.\nВъпреки че е допустимо да се създават потребители без пароли, това е незащитено действие.",
-       "config-db-username-empty": "Необходимо е да се въведе стойност за „{{int:config-db-username}}“.",
        "config-db-install-username": "Въвежда се потребителско име, което ще се използва за свързване с базата от данни по време на процеса по инсталация.\nТова не е потребителско име за сметка в МедияУики; това е потребителско име за базата от данни.",
        "config-db-install-password": "Въвежда се парола, която ще бъде използвана за свързване с базата от данни по време на инсталационния процес.\nТова не е парола за сметка в МедияУики; това е парола за базата от данни.",
        "config-db-install-help": "Въвеждат се потребителско име и парола, които ще бъдат използвани за свързване с базата от данни по време на инсталационния процес.",
        "config-oracle-def-ts": "Таблично пространство по подразбиране:",
        "config-oracle-temp-ts": "Временно таблично пространство:",
        "config-type-mysql": "MySQL (или съвместима)",
-       "config-type-mssql": "Microsoft SQL Сървър",
+       "config-type-mssql": "Microsoft SQL сървър",
        "config-support-info": "МедияУики поддържа следните системи за бази от данни:\n\n$1\n\nАко не виждате желаната за използване система в списъка по-долу, следвайте инструкциите за активиране на поддръжка по-горе.",
        "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] е най-важна за МедияУики и се поддържа най-добре. МедияУики работи също така с [{{int:version-db-mariadb-url}} MariaDB] и [{{int:version-db-percona-url}} Percona Server], които са съвместими с MySQL.\n([http://www.php.net/manual/bg/mysqli.installation.php Как се компилира PHP с поддръжка на MySQL])",
        "config-dbsupport-postgres": "* [{{int:version-db-postgres-url}} PostgreSQL] е популярна система за управление на бази от данни, алтернатива на MySQL. Възможно е все още да има грешки, затова не се препоръчва да се използва в общодостъпна среда.([http://www.php.net/manual/bg/pgsql.installation.php Как се компилира PHP с поддръжка на PostgreSQL])",
        "config-header-postgres": "Настройки за PostgreSQL",
        "config-header-sqlite": "Настройки за SQLite",
        "config-header-oracle": "Настройки за Oracle",
-       "config-header-mssql": "Настройки за Microsoft SQL Сървър",
+       "config-header-mssql": "Настройки за Microsoft SQL сървър",
        "config-invalid-db-type": "Невалиден тип база от данни",
        "config-missing-db-name": "Необходимо е да се въведе стойност за „{{int:config-db-name}}“.",
        "config-missing-db-host": "Необходимо е да се въведе стойност за „{{int:config-db-host}}“.",
        "config-ns-invalid": "Посоченото именно пространство \"<nowiki>$1</nowiki>\" е невалидно.\nНеобходимо е да бъде посочено друго.",
        "config-ns-conflict": "Посоченото именно пространство \"<nowiki>$1</nowiki>\" е в конфликт с използваното по подразбиране именно пространство MediaWiki.\nНеобходимо е да се посочи друго именно пространство.",
        "config-admin-box": "Администраторска сметка",
-       "config-admin-name": "Ð\9fотребителско име:",
+       "config-admin-name": "Ð\92аÑ\88еÑ\82о Ð¿отребителско име:",
        "config-admin-password": "Парола:",
        "config-admin-password-confirm": "Парола (повторно):",
        "config-admin-help": "Въвежда се предпочитаното потребителско име, например \"Иванчо Иванчев\".\nТова ще е потребителското име, което администраторът ще използва за влизане в уикито.",
        "config-upload-help": "Качването на файлове е възможно да доведе до пробели със сигурността на сървъра.\nПовече информация по темата има в [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security раздела за сигурност] в Наръчника.\n\nЗа позволяване качването на файлове, необходимо е уебсървърът да може да записва в поддиректорията на МедияУики <code>images</code>.\nСлед като това условие е изпълнено, функционалността може да бъде активирана.",
        "config-upload-deleted": "Директория за изтритите файлове:",
        "config-upload-deleted-help": "Избиране на директория, в която ще се складират изтритите файлове.\nВ най-добрия случай тя не трябва да е достъпна през уеб.",
-       "config-logo": "Адрес на логото:",
+       "config-logo": "URL адрес на логото:",
        "config-logo-help": "Обликът по подразбиране на МедияУики вклчва място с размери 135х160 пиксела за лого над страничното меню.\nАко има наличен файл с подходящ размер, неговият адрес може да бъде посочен тук.\n\nМоже да се използва <code>$wgStylePath</code> или <code>$wgScriptPath</code> ако логото е относително към тези пътища.\n\nАко не е необходимо лого, полето може да се остави празно.",
        "config-instantcommons": "Включване на Instant Commons",
        "config-instantcommons-help": "[//www.mediawiki.org/wiki/InstantCommons Instant Commons] е функционалност, която позволява на уикитата да използват картинки, звуци и друга медиа от сайта на Уикимедия [//commons.wikimedia.org/ Общомедия].\nЗа да е възможно това, МедияУики изисква достъп до Интернет.\n\nПовече информация за тази функционалност, както и инструкции за настройване за други уикита, различни от Общомедия, е налична в [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgForeignFileRepos наръчника].",
        "config-nofile": "Файлът „$1“ не може да бъде открит. Да не е бил изтрит?",
        "config-extension-link": "Знаете ли, че това уики поддържа [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions разширения]?\n\nМожете да разгледате [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category разширенията по категория] или [//www.mediawiki.org/wiki/Extension_Matrix Матрицата на разширенията] за пълен списък на разширенията.",
        "mainpagetext": "'''Уикито беше успешно инсталирано.'''",
-       "mainpagedocfooter": "Разгледайте [//meta.wikimedia.org/wiki/Help:Contents ръководството] за подробна информация относно използването на уики софтуера.\n\n== Първи стъпки ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Настройки за конфигуриране]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ЧЗВ за МедияУики]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Пощенски списък относно нови версии на МедияУики]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локализиране на МедияУики]"
+       "mainpagedocfooter": "Разгледайте [//meta.wikimedia.org/wiki/Help:Contents ръководството] за подробна информация относно използването на уики софтуера.\n\n== Първи стъпки ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Настройки за конфигуриране]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ЧЗВ за МедияУики]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Пощенски списък относно нови версии на МедияУики]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Локализиране на МедияУики]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Научете как да се справяте със спама във вашето уики]"
 }
index 0709665..757e2e2 100644 (file)
@@ -48,6 +48,8 @@
        "config-site-name-blank": "Язъе сайтан цӀе.",
        "config-project-namespace": "ЦӀерийн ана проектан:",
        "config-ns-generic": "Проект",
+       "config-ns-other-default": "MyWiki",
+       "config-admin-password": "Пароль:",
        "config-admin-password-confirm": "Кхин цӀа пароль:",
        "config-profile-wiki": "Елин вики",
        "config-profile-no-anon": "ДӀаяздар кхолла деза",
        "config-email-settings": "Электронан пошт нисяр",
        "config-enable-email": "Латае дӀайохьуьйту e-mail",
        "config-upload-deleted": "ДӀаяхна файлийн директори:",
+       "config-logo": "Логотипан URL:",
        "config-cc-again": "Хьаржа кхин цӀа…",
        "config-skins": "Кечяран тема",
        "config-skins-use-as-default": "ХӀара тема Ӏад йитарца лелае",
        "config-skins-must-enable-some": "Ахьа цхьаъ мукъа тема латина йита езаш ю.",
        "config-skins-must-enable-default": "Ӏад йитарца йолу тема латина хила еза.",
+       "config-install-step-done": "кхочушдина",
+       "config-install-step-failed": "тар цаделира",
        "config-install-user": "Декъашхочун хаамийн база кхоллар",
        "config-install-user-alreadyexists": "Декъашхо «$1» хӀинцале волуш ву",
        "config-install-user-create-failed": "Декъашхо «$1» кхолла цаделира: $2",
index 9ddbfa4..e9ca48b 100644 (file)
@@ -77,6 +77,7 @@
        "config-apc": "Je nainstalováno [http://www.php.net/apc APC]",
        "config-wincache": "Je nainstalována [http://www.iis.net/download/WinCacheForPhp WinCache]",
        "config-no-cache": "'''Upozornění:''' Nebylo nalezeno [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache], ani [http://www.iis.net/download/WinCacheForPhp WinCache].\nKešování objektů bude vypnuto.",
+       "config-no-cache-apcu": "<strong>Upozornění:</strong> Nebylo nalezeno [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache], ani [http://www.iis.net/download/WinCacheForPhp WinCache].\nKešování objektů bude vypnuto.",
        "config-mod-security": "'''Upozornění''': váš webový server má zapnuto [http://modsecurity.org/ mod_security]. Při chybné konfiguraci může způsobovat potíže MediaWiki či dalším programům, které umožňují ukládat libovolný obsah.\nPokud narazíte na náhodné chyby, podívejte se do [http://modsecurity.org/documentation/ dokumentace mod_security] nebo kontaktujte technickou podporu vašeho poskytovatele.",
        "config-diff3-bad": "Nebyl nalezen GNU diff3.",
        "config-git": "Nalezen software pro správu verzí Git: <code>$1</code>.",
index c23a268..53a4d9a 100644 (file)
@@ -84,6 +84,7 @@
        "config-apc": "[http://www.php.net/apc APC] ist installiert",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] ist installiert",
        "config-no-cache": "'''Warnung:''' [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] oder [http://www.iis.net/download/WinCacheForPhp WinCache] wurden nicht gefunden.\nDas Objektcaching kann daher nicht aktiviert werden.",
+       "config-no-cache-apcu": "<strong>Warnung:</strong> [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] oder [http://www.iis.net/download/WinCacheForPhp WinCache] konnten nicht gefunden werden.\nDer Objektcache ist nicht aktiviert.",
        "config-mod-security": "'''Warnung:''' Auf dem Webserver wurde [http://modsecurity.org/ ModSecurity] aktiviert. Sofern falsch konfiguriert, kann dies zu Problemen mit MediaWiki sowie anderer Software auf dem Server führen und es Benutzern ermöglichen, beliebige Inhalte im Wiki einzustellen.\nFür weitere Informationen empfehlen wir die [http://modsecurity.org/documentation/ Dokumentation zu ModSecurity] oder den Kontakt zum Hoster, sofern Fehler auftreten.",
        "config-diff3-bad": "GNU diff3 wurde nicht gefunden.",
        "config-git": "Die Versionsverwaltungssoftware „Git“ wurde gefunden: <code>$1</code>.",
index ca3f758..2d34299 100644 (file)
@@ -7,7 +7,8 @@
                        "Astralnet",
                        "Geraki",
                        "Stam.nikos",
-                       "Giorgos456"
+                       "Giorgos456",
+                       "Badseed"
                ]
        },
        "config-desc": "Το πρόγραμμα εγκατάστασης για το MediaWiki",
        "config-localsettings-badkey": "Το κλειδί που δώσατε είναι εσφαλμένο.",
        "config-upgrade-key-missing": "Έχει εντοπιστεί μια υπάρχουσα εγκατάσταση του MediaWiki.\nΓια να αναβαθμίσετε αυτήν την εγκατάσταση, παρακαλούμε να βάλετε την ακόλουθη γραμμή στο κάτω μέρος του <code>LocalSettings.php</code> σας:\n\n$1",
        "config-localsettings-incomplete": "Το υπάρχον <code>LocalSettings.php</code> φαίνεται να είναι ελλιπές.\nΤο $1 μεταβλητή δεν έχει οριστεί.\nΠαρακαλούμε να αλλάξετε  το <code>LocalSettings.php</code> έτσι ώστε αυτή η μεταβλητή έχει οριστεί, και κάντε κλικ στο \"{{int:Config-continue}}\".",
+       "config-localsettings-connection-error": "Ένα σφάλμα παρουσιάστηκε κατά τη σύνδεση με τη βάση δεδομένων και με τη χρήση των ρυθμίσεων που ορίστηκαν στο <code>LocalSettings.php</code>. Παρακαλούμε διορθώστε αυτές τις ρυθμίσεις και δοκιμάστε ξανά.\n\n$1",
        "config-session-error": "Σφάλμα κατά την εκκίνηση συνεδρίας: $1",
        "config-session-expired": "Τα δεδομένα συνόδου φαίνεται να έχουν λήξει.\nΣυνεδρίες έχουν ρυθμιστεί για μια διάρκεια ζωής $1.\nΜπορείτε να αυξήσετε αυτό βάζοντας  <code>session.gc_maxlifetime</code> στο php.ini.\nΚάντε επανεκκίνηση της διαδικασίας εγκατάστασης.",
+       "config-no-session": "Η συνεδρία δεδομένων σας έχει χαθεί!Ελέγξτε το αρχείο php.ini και βεβαιωθείτε ότι το <code>session.save_path</code> έχει μπει στον κατάλληλο κατάλογο.",
        "config-your-language": "Η γλώσσα σας:",
        "config-your-language-help": "Επιλέξτε μία γλώσσα για τη διαδικασία της εγκατάστασης.",
        "config-wiki-language": "Γλώσσα του wiki:",
-       "config-wiki-language-help": "Î\95Ï\80ιλέξÏ\84ε Ï\84η Î³Î»Ï\8eÏ\83Ï\83α Ï\80οÏ\85 Î¸Î± Î³Ï\81αÏ\86εί Ï\84ο wiki Ï\80Ï\81Ï\89Ï\84αÏ\81Ï\87ικά.",
+       "config-wiki-language-help": "Î\95Ï\80ιλέξÏ\84ε Ï\84η Î³Î»Ï\8eÏ\83Ï\83α Ï\83Ï\84ην Î¿Ï\80οία Î¸Î± Î³Ï\81άÏ\86εÏ\84αι ÎºÏ\85Ï\81ίÏ\89Ï\82 Ï\84ο wiki.",
        "config-back": "← Πίσω",
        "config-continue": "Συνέχεια →",
        "config-page-language": "Γλώσσα",
@@ -43,7 +46,7 @@
        "config-page-upgradedoc": "Αναβάθμιση",
        "config-page-existingwiki": "Υπάρχον wiki",
        "config-help-restart": "Θέλετε να καταργήσετε όλα τα αποθηκευμένα δεδομένα που έχετε εισαγάγει και να επανεκκινήσετε τη διαδικασία εγκατάστασης;",
-       "config-restart": "Î\9dαι, ÎºÎ¬Î½Ï\84ε ÎµÏ\80ανεκκίνηÏ\83η",
+       "config-restart": "Ναι, επανεκκίνηση",
        "config-welcome": "=== Περιβαλλοντικοί έλεγχοι ===\nΤώρα θα γίνουν βασικοί έλεγχοι για να δούμε αν αυτό το περιβάλλον είναι κατάλληλο για την εγκατάσταση του MediaWiki.\nΘυμηθείτε να συμπεριλάβετε αυτές τις πληροφορίες εάν αναζητήσετε υποστήριξη για το πώς να ολοκληρώσετε την εγκατάσταση.",
        "config-copyright": "=== Πνευματικά δικαιώματα και Όροι ===\n\n$1\n\nΑυτό το πρόγραμμα είναι ελεύθερο λογισμικό• μπορείτε να το αναδιανείμετε ή και να το τροποποιήσετε υπό τους όρους της Γενικής Άδειας Δημόσιας Χρήσης GNU, όπως αυτή δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού• είτε της έκδοσης 2 της Άδειας, είτε (κατά την επιλογή σας) οποιασδήποτε μεταγενέστερης έκδοσης.\n\nΑυτό το πρόγραμμα διανέμεται με την ελπίδα ότι θα είναι χρήσιμο, αλλά <strong>χωρίς καμία εγγύηση</strong>• χωρίς καν την υπονοούμενη εγγύηση της <strong>εμπορευσιμότητας</strong> ή της <strong>καταλληλοτότητας για συγκεκριμένο σκοπό</strong>.\nΔείτε την Γενική Άδεια Δημόσιας Χρήσης GNU για περισσότερες λεπτομέρειες.\n\nΘα πρέπει να έχετε λάβει <doclink href=\"Copying\">ένα αντίγραφο της Γενικής Άδειας Δημόσιας Χρήσης GNU</doclink> μαζί με αυτό το πρόγραμμα• αν όχι, γράψτε στο Free Software Foundation,\n51 Franklin Street, Fifth Floor,\nBoston, MA 02110-1335\nUSA ή [http://www.gnu.org/copyleft/gpl.html διαβάστε online].",
        "config-sidebar": "* [//www.mediawiki.org Αρχική MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Οδηγός Χρήστη]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Οδηγός Διαχειριστή]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Συχνές ερωτήσεις]\n----\n* <doclink href=\"Readme\">Διαβάστε με</doclink>\n* <doclink href=\"ReleaseNotes\">Σημειώσεις έκδοσης</doclink>\n* <doclink href=\"Copying\">Αντιγραφή</doclink>\n* <doclink href=\"UpgradeDoc\">Αναβάθμιση</doclink>",
        "config-env-hhvm": "Το HHVM $1 είναι εγκατεστημένο.",
        "config-unicode-using-intl": "Χρησιμοποιώντας την [http://pecl.php.net/intl επέκταση intl PECL] για κανονικοποίηση Unicode.",
        "config-unicode-pure-php-warning": "<strong>Προειδοποίηση:</strong> Η [http://pecl.php.net/intl επέκταση intl PECL] δεν είναι διαθέσιμη για να χειριστεί την κανονικοποίηση Unicode, επιστρέφουμε στην αργή αμιγώς PHP εφαρμογή.\nΕάν λειτουργείτε έναν ιστότοπο υψηλής επισκεψιμότητας, θα πρέπει να ρίξετε μια ματιά στην [//www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations κανονικοποίηση Unicode].",
+       "config-no-db": "Δεν βρέθηκε κάποιο κατάλληλο πρόγραμμα οδήγησης βάσης δεδομένων! Θα πρέπει να εγκαταστήσετε ένα πρόγραμμα οδήγησης βάσης δεδομένων για  PHP.\nΟ παρακάτω {{PLURAL:$2|τύπος βάσης δεδομένων|τύποι βάσεων δεδομένων}} υποστηρίζονται: $1.\n\nΑν κάνετε compile την PHP μόνοι σας, ρυθμίστε ξανά τις παραμέτρους με κάποιον ενεργοποιημένο εξυπηρετητή βάσεων  δεδομένων, για παράδειγμα, χρησιμοποιώντας την εντολή <code>./configure --with-mysqli</code>.\nΕάν έχετε εγκαταστήσει την PHP από κάποιο πακέτο στο Debian ή στο Ubuntu, τότε θα πρέπει να εγκαταστήσετε επίσης, για παράδειγμα, το πακέτο <code>php5-mysql</code>.",
+       "config-outdated-sqlite": "<strong>Προειδοποίηση:</strong> έχετε την SQLite έκδοση $1, που είναι χαμηλότερη από την ελάχιστη απαιτούμενη έκδοση $2. Η SQLite δεν θα είναι διαθέσιμη.",
+       "config-register-globals-error": "<strong>Σφάλμα: Η επιλογή της PHP <code>[http://php.net/register_globals τις register_globals]</code> είναι ενεργοποιημένη.\nΘα πρέπει να απενεργοποιηθεί για να συνεχιστεί η εγκατάσταση.</strong>\nΔείτε τη σελίδα [https://www.mediawiki.org/wiki/register_globals https://www.mediawiki.org/wiki/register_globals] για βοήθεια σχετικά με αυτήν την ενέργεια.",
+       "config-safe-mode": "<strong>Προειδοποίηση:</strong> Το [http://www.php.net/features.safe-mode safe mode] της PHP είναι ενεργό.\nΑυτό μπορεί να προκαλέσει προβλήματα, ιδιαίτερα εάν γίνεται χρήση φόρτωσης αρχείων και  υποστήριξης <code>math</code>.",
+       "config-xml-bad": "Το PHP XML module λείπει.\nΤο MediaWiki απαιτεί συναρτήσεις που περιλαμβάνονται σε αυτό το module και δεν θα λειτουργήσει με αυτή την παραμετροποίηση. \nΊσως χρειάζεται να εγκαταστήσετε το πακέτο RPM php-xml.",
+       "config-pcre-no-utf8": "<strong>Κρίσιμο:</strong> Το PCRE module της PHP  φαίνεται να έχει μεταγλωττιστεί χωρίς υποστήριξη  PCRE_UTF8.\nΓια τη σωστή λειτουργία του MediaWiki απαιτείται υποστήριξη UTF-8.",
        "config-memory-raised": "Το  <code>memory_limit</code> της PHP είναι  $1 και αυξήθηκε σε  $2.",
-       "config-memory-bad": "<strong>Προειδοποίηση:</strong> η <code>memory_limit</code> της PHP είναι $1.\nΑυτό είναι πιθανώς πολύ χαμηλό.\n\nΗ εγκατάσταση ενδέχεται να αποτύχει!",
+       "config-memory-bad": "<strong>Προειδοποίηση:</strong> το <code>memory_limit</code> της PHP είναι $1.\nΑυτή η τιμή είναι πιθανώς πολύ χαμηλή.\n\nΗ εγκατάσταση ενδέχεται να αποτύχει!",
        "config-xcache": "[http://xcache.lighttpd.net/ Το XCache] είναι εγκατεστημένο",
        "config-apc": "Το [http://www.php.net/apc APC] είναι εγκατεστημένο",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp Το WinCache] είναι εγκατεστημένο",
        "config-diff3-bad": "Το GNU diff3 δεν βρέθηκε.",
        "config-git": "Βρέθηκε η Git έκδοση λογισμικού ελέγχου: <code>$1</code>.",
-       "config-git-bad": "Η Git έκδοση του λογισμικού ελέγχου δε βρέθηκε.",
-       "config-no-uri": "<strong>ΣÏ\86άλμα:</strong> Î\94εν Î¼Ï\80Ï\8cÏ\81εÏ\83ε Î½Î± ÎºÎ±Î¸Î¿Ï\81ίÏ\83ει το τρέχον URI.\nΗ εγκατάσταση ματαιώθηκε.",
-       "config-using-server": "Χρησιμοποιώντας το όνομα του διακομιστή \"<nowiki>$1</nowiki>\".",
+       "config-git-bad": "Η Git έκδοση του λογισμικού ελέγχου δεν βρέθηκε.",
+       "config-no-uri": "<strong>ΣÏ\86άλμα:</strong> Î\94εν Î®Ï\84αν Î´Ï\85ναÏ\84Ï\8c Î½Î± ÎºÎ±Î¸Î¿Ï\81ιÏ\83Ï\84εί το τρέχον URI.\nΗ εγκατάσταση ματαιώθηκε.",
+       "config-using-server": "Χρησιμοποιείται το όνομα διακομιστή \"<nowiki>$1</nowiki>\".",
        "config-using-uri": "Χρησιμοποιώντας την διεύθυνση URL του διακομιστή \"<nowiki>$1$2</nowiki>\".",
-       "config-brokenlibxml": "Το σύστημά σας έχει ένα συνδυασμό εκδόσεων της PHP και libxml2 που είναι προβληματικές και μπορεί να προκαλέσει κρυμμένα στοιχεία διαφθοράς στο MediaWiki και άλλες εφαρμογές web.\nΑναβαθμίστε σε libxml2 2.7.3 ή μεταγενέστερο ([https://bugs.php.net/bug.php?id=45996 bug κατατεθεί με την PHP]).\nΗ εγκατάσταση ματαιώθηκε.",
+       "config-brokenlibxml": "Το σύστημά σας έχει έναν συνδυασμό εκδόσεων της PHP και της libxml2 που είναι προβληματικός και μπορεί να προκαλέσει καταστροφή κρυμμένων στοιχείων στο MediaWiki και σε άλλες εφαρμογές ιστού.\nΑναβαθμίστε σε libxml2 2.7.3 ή μεταγενέστερη έκδοση ([https://bugs.php.net/bug.php?id=45996 bug που έχει καταχωριστεί για την PHP]).\nΗ εγκατάσταση ματαιώθηκε.",
        "config-db-type": "Τύπος βάσης δεδομένων:",
        "config-db-host": "Φιλοξενία βάσης δεδομένων:",
        "config-db-host-oracle": "Βάση δεδομένων TNS:",
        "config-db-wiki-settings": "Αναγνώριση αυτού του wiki",
        "config-db-name": "Όνομα βάσης δεδομένων:",
+       "config-db-name-help": "Επιλέξτε ένα όνομα που ταιριάζει στο  wiki σας. Δεν πρέπει να περιέχει κενά διαστήματα.\n\nΕάν χρησιμοποιείτε κοινόχρηστο web hosting, ο πάροχος φιλοξενίας είτε θα σας δώσει ένα συγκεκριμένο όνομα βάσης δεδομένων για να χρησιμοποιήσετε ή θα σας δώσει τη δυνατότητα να δημιουργήσετε βάσεις δεδομένων μέσω ενός πίνακα ελέγχου.",
        "config-db-name-oracle": "Σχήμα βάσης δεδομένων:",
        "config-db-install-account": "Λογαριασμός χρήστη για την εγκατάσταση",
        "config-db-username": "Όνομα χρήστη βάσης δεδομένων:",
        "config-db-password": "Κωδικός πρόσβασης βάσης δεδομένων:",
-       "config-db-account-lock": "Χρησιμοποιήστε το ίδιο όνομα χρήστη και κωδικό πρόσβασης στη διάρκεια της κανονικής λειτουργίας",
+       "config-db-install-help": "Εισαγάγετε το όνομα χρήστη και τον κωδικό πρόσβασης που θα χρησιμοποιηθεί για τη σύνδεση με τη βάση δεδομένων κατά τη διάρκεια της διαδικασίας εγκατάστασης.",
+       "config-db-account-lock": "Χρησιμοποιήστε το ίδιο όνομα χρήστη και κωδικό πρόσβασης μετά την εγκατάσταση",
        "config-db-wiki-account": "Λογαριασμός χρήστη για κανονική λειτουργία",
+       "config-db-wiki-help": "Πληκτρολογήστε το όνομα χρήστη και τον κωδικό πρόσβασης που θα χρησιμοποιηθεί για τη σύνδεση με τη βάση δεδομένων κατά τη διάρκεια της κανονικής λειτουργίας του wiki.\nΕάν ο λογαριασμός δεν υπάρχει και o λογαριασμός εγκατάστασης  έχει επαρκή δικαιώματα, αυτός ο λογαριασμός χρήστη θα δημιουργηθεί με τα ελάχιστα δικαιώματα που απαιτούνται για τη λειτουργία του wiki.",
        "config-db-prefix": "Πρόθεμα πίνακα βάσης δεδομένων:",
+       "config-db-prefix-help": "Εάν χρειάζεται να μοιραστείτε μία βάση δεδομένων μεταξύ πολλαπλών wikis, ή μεταξύ του MediaWiki και μιας άλλης web εφαρμογής, μπορείτε να προσθέσετε ένα πρόθεμα σε όλα τα ονόματα πίνακα για να αποφεύγονται οι διενέξεις.\nΜην χρησιμοποιείτε κενά διαστήματα.\n\nΑυτό το πεδίο αφήνεται συνήθως κενό.",
        "config-db-charset": "Σύνολο χαρακτήρων βάσης δεδομένων",
        "config-charset-mysql5-binary": "MySQL 4.1/5.0 δυαδικό",
        "config-charset-mysql5": "MySQL 4.1/5.0 UTF-8",
-       "config-charset-mysql4": "UTF-8 συμβατό προς τα πίσω με MySQL 4.0",
+       "config-charset-mysql4": "UTF-8 συμβατό με MySQL 4.0 και παλαιότερες εκδόσεις",
        "config-mysql-old": "Απαιτείται Microsoft SQL Server $1 ή νεότερο. Εσείς έχετε $2.",
        "config-db-port": "Θύρα βάσης δεδομένων:",
        "config-db-schema": "Σχήμα για MediaWiki:",
-       "config-db-schema-help": "Î\91Ï\85Ï\84Ï\8c Ï\84ο Ï\83Ï\87ήμα Ï\83Ï\85νήθÏ\89Ï\82 Î¸Î± ÎµÎ¯Î½Î±Î¹ ÎµÎ½Ï\84άξει.\nÎ\86λλαξε Ï\84ο Î¼Ï\8cνο Î±Î½ Î¾Î­Ï\81ειÏ\82 Ï\8cÏ\84ι Ï\84ο Ï\87Ï\81ειάζεÏ\83αι.",
+       "config-db-schema-help": "Î\91Ï\85Ï\84Ï\8c Ï\84ο Ï\83Ï\87ήμα Ï\83Ï\85νήθÏ\89Ï\82 Î±Ï\81κεί.\nÎ\91λλάξÏ\84ε Ï\84ο Î¼Ï\8cνο Î±Î½ ÎµÎ¯Ï\83Ï\84ε Î²Î­Î²Î±Î¹Î¿Î¹ Ï\8cÏ\84ι Ï\87Ï\81ειάζεÏ\84αι.",
        "config-pg-test-error": "Δεν μπορεί να συνδεθεί στη βάση δεδομένων <strong>$1</strong>: $2",
+       "config-sqlite-dir": "SQLite κατάλογος δεδομένων:",
        "config-oracle-temp-ts": "Προσωρινό tablespace:",
        "config-type-mysql": "MySQL (ή συμβατό)",
        "config-type-postgres": "PostgreSQL",
        "config-type-sqlite": "SQLite",
        "config-type-oracle": "Oracle",
        "config-type-mssql": "Microsoft SQL Server",
-       "config-support-info": "To MediaWiki υποστηρίζει τα ακόλουθα συστήματα βάσεων δεδομένων:\n\n$1\n\nΑν δε βλέπεις στο σύστημα βάσεων δεδομένων που θέλεις να χρησιμοποιήσεις να υπάρχει παρακάτω, τότε ακολούθησε τις οδηγίες που δίνονται παραπάνω για να ενεργοποιήσεις την υποστήριξη.",
+       "config-support-info": "To MediaWiki υποστηρίζει τα ακόλουθα συστήματα βάσεων δεδομένων:\n\n$1\n\nΑν δεν εμφανίζεται παρακάτω το σύστημα βάσης δεδομένων που θέλετε να χρησιμοποιήσετε, τότε ακολουθήστε τις οδηγίες στον παραπάνω σύνδεσμο για να ενεργοποιήσετε την υποστήριξη.",
        "config-header-mysql": "Ρυθμίσεις MySQL",
        "config-header-postgres": "Ρυθμίσεις PostgreSQL",
        "config-header-sqlite": "Ρυθμίσεις SQLite",
        "config-email-settings": "Ρυθμίσεις ηλεκτρονικού ταχυδρομείου",
        "config-email-user": "Ενεργοποίηση ηλεκτρονικού ταχυδρομείου από χρήστη σε χρήστη",
        "config-email-usertalk": "Ενεργοποίηση ειδοποίησης σελίδας συζήτησης χρήστη",
+       "config-email-watchlist": "Ενεργοποίηση ειδοποίησης λίστας παρακολούθησης",
+       "config-email-watchlist-help": "Επιτρέψτε στους χρήστες να λαμβάνουν ειδοποιήσεις για τις σελίδες που παρακολουθούν αν το έχουν ενεργοποιήσει στις προτιμήσεις τους.",
        "config-email-auth": "Ενεργοποίηση ταυτοποίησης μέσω ηλεκτρονικού ταχυδρομείου",
        "config-email-sender": "Διεύθυνση ηλεκτρονικού ταχυδρομείου επιστροφής:",
        "config-upload-settings": "Ανέβασμα εικόνων και άλλων αρχείων",
        "config-cc-not-chosen": "Επιλέξτε την  άδεια Creative Commons που θέλετε και κάντε κλικ στο κουμπί \"συνέχεια\".",
        "config-advanced-settings": "Προηγμένες ρυθμίσεις παραμέτρων",
        "config-cache-options": "Ρυθμίσεις για την προσωρινή αποθήκευση αντικειμένου:",
+       "config-memcache-badip": "Έχετε εισάγει μια μη έγκυρη διεύθυνση IP για το Memcached: $1.",
        "config-memcache-noport": "Δεν καθορίσατε μια θύρα για να χρησιμοποιήσετε για το Memcached server: $1.\nΑν δεν ξέρετε τη θύρα, η προεπιλογή είναι 11211.",
        "config-memcache-badport": "Οι Memcached αριθμοί θύρας θα πρέπει να είναι μεταξύ $1 και $2.",
        "config-extensions": "Επεκτάσεις",
        "config-install-interwiki-exists": "<strong>Προειδοποίηση:</strong> O πίνακας interwiki φαίνεται να έχει ήδη καταχωρηθεί.\nΠαρακάμπτοντας προεπιλεγμένη λίστα.",
        "config-install-stats": "Γίνεται αρχικοποίηση των στατιστικών",
        "config-install-keys": "Γίνεται δημιουργία των μυστικών κλειδιών",
+       "config-install-updates": "Αποτρέψτε αχρειάστες ενημερώσεις",
+       "config-install-updates-failed": "<strong>Σφάλμα:</strong> Η εισαγωγή κλειδιών ενημέρωσης  σε πίνακες απέτυχε με το ακόλουθο σφάλμα: $1",
        "config-install-sysop": "Γίνεται δημιουργία του λογαριασμού χρήστη του διαχειριστή",
        "config-install-subscribe-fail": "Ανίκανος να εγγραφείτε στο mediawiki-ανακοινώση: $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": "<strong>Συγχαρητήρια!</strong>\nΈχετε εγκαταστήσει με επιτυχία το MediaWiki.\n\nΤο πρόγραμμα εγκατάστασης έχει δημιουργήσει το  αρχείο   <code>LocalSettings.php</code>.\nΠεριέχει όλες τις ρυθμίσεις παραμέτρων σας.\n\nΘα πρέπει να το κατεβάσετε και να το βάλετε στη βάση της εγκατάστασης του  wiki σας (στον ίδιο κατάλογο όπως το  index.php). Η λήψη θα αρχίσει αυτόματα.\n\nΑν η λήψη δεν προσφέφθηκε, ή αν την ακυρώσατε, μπορείτε να επανεκκινήσετε τη λήψη κάνοντας κλικ στο παρακάτω link:\n\n$3\n\n<strong>Σημείωση:</strong> Εάν δεν το κάνετε αυτό τώρα, αυτό το  αρχείο ρύθμισης παραμέτρων δεν θα είναι διαθέσιμο για σας αργότερα, αν βγείτε από την εγκατάσταση, χωρίς να το κατεβάσετε!\n\nΌταν γίνει αυτό, μπορείτε να <strong>[$2 μπείτε στο wiki σας]</strong>.",
        "config-download-localsettings": "Λήψη του <code>LocalSettings.php</code>",
        "config-help": "βοήθεια",
        "config-help-tooltip": "κλικ για ανάπτυξη",
        "config-nofile": "Το αρχείο «$1» δεν μπορεί να βρεθεί. Μήπως έχει διαγραφεί;",
+       "config-extension-link": "Γνωρίζατε ότι το wiki σας υποστηρίζει [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions επεκτάσεις];\n\nΜπορείτε να περιηγηθείτε [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category επεκτάσεις ανά κατηγορία] ή το [//www.mediawiki.org/wiki/Extension_Matrix Extension Matrix] για να δείτε την πλήρη λίστα των επεκτάσεων.",
        "mainpagetext": "<strong>To MediaWiki εγκαταστάθηκε με επιτυχία.</strong>",
-       "mainpagedocfooter": "ΣÏ\85μβοÏ\85λεÏ\85Ï\84είÏ\84ε Ï\84ο [//meta.wikimedia.org/wiki/Help:Contents Î\95γÏ\87ειÏ\81ίδιο Ï\87Ï\81ήÏ\83Ï\84η] Î³Î¹Î± Ï\80ληÏ\81οÏ\86οÏ\81ίεÏ\82 Ï\83Ï\87εÏ\84ικά Î¼Îµ Ï\84η Ï\87Ï\81ήÏ\83η Ï\84οÏ\85 Î»Î¿Î³Î¹Ï\83μικοÏ\8d wiki.\n\n== Î\9eεκινÏ\8eνÏ\84αÏ\82 ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Î\9aαÏ\84άλογοÏ\82 Ï\81Ï\85θμίÏ\83εÏ\89ν]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Î\9bίÏ\83Ï\84α Ï\84αÏ\87Ï\85δÏ\81ομείοÏ\85 ÎµÎºÎ´Ï\8cÏ\83εÏ\89ν MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Î¤Î¿Ï\80ικοÏ\80οιήÏ\83Ï\84ε Ï\84ο MediaWiki Î³Î¹Î± Ï\84η Î³Î»Ï\8eÏ\83Ï\83α σας]"
+       "mainpagedocfooter": "ΣÏ\85μβοÏ\85λεÏ\85Ï\84είÏ\84ε Ï\84ο [//meta.wikimedia.org/wiki/Help:Contents Î\9fδηγÏ\8cÏ\82 Î§Ï\81ήÏ\83Ï\84η] Î³Î¹Î± Ï\80ληÏ\81οÏ\86οÏ\81ίεÏ\82 Ï\83Ï\87εÏ\84ικά Î¼Îµ Ï\84ο Î»Î¿Î³Î¹Ï\83μικÏ\8c wiki.\n\n== Î\9eεκινÏ\8eνÏ\84αÏ\82 ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Ï\81Ï\85θμίÏ\83ειÏ\82 Î\94ιαμÏ\8cÏ\81Ï\86Ï\89Ï\83ηÏ\82 Î»Î¯Ï\83Ï\84α]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Ï\84ο MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Ï\84ο MediaWiki Î±Ï\80ελεÏ\85θέÏ\81Ï\89Ï\83η mailing list]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Î\88Ï\87οÏ\85ν MediaWiki Î³Î¹Î± Ï\84η Î³Î»Ï\8eÏ\83Ï\83α Ï\83αÏ\82]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Î\9cάθεÏ\84ε Ï\80Ï\8eÏ\82 Î½Î± ÎºÎ±Ï\84αÏ\80ολεμήÏ\83εÏ\84ε Ï\84ο spam Ï\83Ï\84ο wiki σας]"
 }
index ac3178f..f2ec7a7 100644 (file)
@@ -69,6 +69,7 @@
        "config-apc": "[http://www.php.net/apc APC] is installed",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] is installed",
        "config-no-cache": "<strong>Warning:</strong> Could not find [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] or [http://www.iis.net/download/WinCacheForPhp WinCache].\nObject caching is not enabled.",
+       "config-no-cache-apcu": "<strong>Warning:</strong> Could not find [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] or [http://www.iis.net/download/WinCacheForPhp WinCache].\nObject caching is not enabled.",
        "config-mod-security": "<strong>Warning:</strong> Your web server has [http://modsecurity.org/ mod_security]/mod_security2 enabled. Many common configurations of this will cause problems for MediaWiki and other software that allows users to post arbitrary content.\nIf possible, this should be disabled. Otherwise, refer to [http://modsecurity.org/documentation/ mod_security documentation] or contact your host's support if you encounter random errors.",
        "config-diff3-bad": "GNU diff3 not found.",
        "config-git": "Found the Git version control software: <code>$1</code>.",
index bbab9a1..77f0529 100644 (file)
@@ -96,6 +96,7 @@
        "config-apc": "[http://www.php.net/apc APC] está instalado",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] está instalado",
        "config-no-cache": "<strong>Advertencia:</strong> no pudo encontrarse [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nEl caché de objetos no está activado.",
+       "config-no-cache-apcu": "<strong>Advertencia:</strong> No se pudo encontrar [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache].\nEl caché de objetos no está activado.",
        "config-mod-security": "<strong>Advertencia:</strong> tu servidor web tiene activado [http://modsecurity.org/ mod_security]/mod_security2. Muchas de sus configuraciones comunes pueden causar problemas a MediaWiki u otro software que permita a los usuarios publicar contenido arbitrario. De ser posible, deberías desactivarlo. Si no, consulta la [http://modsecurity.org/documentation/ documentación de mod_security] o contacta con el administrador de tu servidor si encuentras errores aleatorios.",
        "config-diff3-bad": "GNU diff3 no se encuentra.",
        "config-git": "Se encontró el software de control de versiones Git: <code>$1</code>.",
index c2889bf..c4fc0cd 100644 (file)
@@ -80,6 +80,7 @@
        "config-apc": "[http://www.php.net/apc APC] نصب شده‌است.",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] نصب شده‌است.",
        "config-no-cache": "'''هشدار:''' [http://www.php.net/apc APC],[http://xcache.lighttpd.net/ XCache] یا [http://www.iis.net/download/WinCacheForPhp WinCache] را نتوانست پیدا کند.\nذخیره شی فعال نیست.",
+       "config-no-cache-apcu": "<strong>هشدار:</strong> پیوند [http://www.php.net/apcu APCu]، [http://xcache.lighttpd.net/ XCache] یا [http://www.iis.net/download/WinCacheForPhp WinCache] یافت نشد. ذخیره شی فعال نیست.",
        "config-mod-security": "'''هشدار:''' وب سرور شما [http://modsecurity.org/ mod_security] فعال است.اگر اشتباه پیکربندی شده‌‌ باشد،می تواند باعث ایجاد مشکلاتی برای مدیاویکی یا دیگر نرم‌افزاری شود که به کاربران اجازه می‌دهد پیام دلخواه ارسال کنند.\nبه [http://modsecurity.org/documentation/ mod_security documentation] مراجعه کنید یا اگر با خطاهای اتفاقی مواجه شدید با پشتیبانی میزبان خود در تماس باشید.",
        "config-diff3-bad": "جی‌ان‌یو دیف۳ پیدا نشد.",
        "config-git": "کنترل نسخهٔ نرم‌افزار گیت پیدا شد: <code>$1</code>.",
index 370874b..c031466 100644 (file)
@@ -92,6 +92,7 @@
        "config-apc": "[http://www.php.net/apc APC] est installé",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] est installé",
        "config-no-cache": "'''Attention :''' Impossible de trouver [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nLa mise en cache d'objets n'est pas activée.",
+       "config-no-cache-apcu": "'''Attention :''' Impossible de trouver [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nLa mise en cache d'objets n'est pas activée.",
        "config-mod-security": "'''Attention''': Votre serveur web a [http://modsecurity.org/ mod_security] activé. S'il est mal configuré, cela peut poser des problèmes à MediaWiki ou à d'autres applications qui permettent aux utilisateurs de publier un contenu quelconque.\nReportez-vous à [http://modsecurity.org/documentation/ la documentation de mod_security] ou contactez le support de votre hébergeur si vous rencontrez des erreurs aléatoires.",
        "config-diff3-bad": "GNU diff3 introuvable.",
        "config-git": "Logiciel de contrôle de version Git trouvé : <code>$1</code>.",
index 643a370..9458a8f 100644 (file)
@@ -74,6 +74,7 @@
        "config-apc": "[http://www.php.net/apc APC] está instalado",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] está instalado",
        "config-no-cache": "<strong>Atención:</strong> Non se puido atopar [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nA caché de obxectos está desactivada.",
+       "config-no-cache-apcu": "<strong>Advertencia:</strong> Non se puido atopar [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] ou [http://www.iis.net/download/WinCacheForPhp WinCache].\nA caché de obxectos non está activada.",
        "config-mod-security": "<strong>Atención:</strong> O seu servidor web ten o [http://modsecurity.org/ mod_security] activado. Se estivese mal configurado, pode causar problemas a MediaWiki ou calquera outro software que permita aos usuarios publicar contidos arbitrarios.\nOlle a [http://modsecurity.org/documentation/ documentación do mod_security] ou póñase en contacto co soporte do seu servidor se atopa erros aleatorios.",
        "config-diff3-bad": "GNU diff3 non se atopou.",
        "config-git": "Atopouse o software de control da versión de Git: <code>$1</code>.",
index b8c5102..14f5684 100644 (file)
@@ -1,5 +1,26 @@
 {
-       "@metadata": [],
+       "@metadata": {
+               "authors": [
+                       "Vrhnje"
+               ]
+       },
+       "config-information": "Informacije",
+       "config-your-language": "Vaš jezik:",
+       "config-your-language-help": "Odaberite jezik na kojem će se pojaviti proces instalacije.",
+       "config-wiki-language": "Wiki jezik:",
+       "config-back": "← Povratak",
+       "config-continue": "Dalje →",
+       "config-page-language": "Jezik",
+       "config-page-welcome": "Dobrodošli na MediaWiki!",
+       "config-page-dbconnect": "Spajanje na bazu podataka",
+       "config-page-upgrade": "Ažuriranje postojeće instalacije",
+       "config-page-restart": "Ponovno pokreni instalaciju",
+       "config-page-readme": "Pročitajte više",
+       "config-page-releasenotes": "Informacije o verziji",
+       "config-page-copying": "Kopiranje",
+       "config-page-upgradedoc": "Ažuriranje",
+       "config-page-existingwiki": "Postojeći wiki",
+       "config-restart": "Da, počni iznova",
        "mainpagetext": "'''Softver MediaWiki je uspješno instaliran.'''",
        "mainpagedocfooter": "Pogledajte [//meta.wikimedia.org/wiki/MediaWiki_localisation dokumentaciju o prilagodbi sučelja]\ni [//meta.wikimedia.org/wiki/MediaWiki_User%27s_Guide Vodič za suradnike] za pomoć pri uporabi i podešavanju."
 }
index 7ed7067..572f3af 100644 (file)
@@ -85,6 +85,7 @@
        "config-apc": "[http://www.php.net/apc APC] è installato",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] è installato",
        "config-no-cache": "'''Attenzione:''' [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache] non sono stati trovati.\nLa caching degli oggetti non è attivata.",
+       "config-no-cache-apcu": "'''Attenzione:''' [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] o [http://www.iis.net/download/WinCacheForPhp WinCache] non sono stati trovati.\nLa caching degli oggetti non è attivata.",
        "config-mod-security": "<strong>Attenzione:</strong> Il tuo server web ha il [http://modsecurity.org/ mod_security] abilitato. Se non correttamente configurato, può creare problemi a MediaWiki o ad altro software che permette agli utenti di pubblicare contenuto.\nFai riferimento alla [http://modsecurity.org/documentation/ documentazione sul mod_security] o contatta il supporto tecnico del tuo provider di hosting se si verificano errori.",
        "config-diff3-bad": "GNU diff3 non trovato.",
        "config-git": "Trovato software di controllo della versione Git: <code>$1</code>.",
index 31547ad..c9db449 100644 (file)
@@ -7,7 +7,8 @@
                        "Priviet",
                        "Namoroka",
                        "Revi",
-                       "Alex00728"
+                       "Alex00728",
+                       "Hwangjy9"
                ]
        },
        "config-desc": "미디어위키를 위한 설치 관리자",
@@ -77,6 +78,7 @@
        "config-apc": "[http://www.php.net/apc APC]가 설치되었습니다",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache]가 설치되었습니다",
        "config-no-cache": "<strong>경고:</strong> [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] 또는 [http://www.iis.net/download/WinCacheForPhp WinCache]를 찾을 수 없습니다.\n개체 캐싱을 활성화하지 않습니다.",
+       "config-no-cache-apcu": "<strong>경고:</strong> [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] 또는 [http://www.iis.net/download/WinCacheForPhp WinCache]를 찾을 수 없습니다.\n개체 캐싱을 활성화할 수 없습니다.",
        "config-mod-security": "<strong>경고:</strong> 웹 서버에 [http://modsecurity.org/ mod_security]가 허용되었습니다. 잘못 설정된 경우 미디어위키나 사용자가 임의의 내용을 게시할 수 있는 다른 소프트웨어에 대한 문제를 일으킬 수 있습니다.\n[http://modsecurity.org/documentation/ mod_security] 문서를 참고하거나 임의의 오류가 발생할 경우 호스트의 지원 요청에 문의하십시오.",
        "config-diff3-bad": "GNU diff3를 찾을 수 없습니다.",
        "config-git": "Git 버전 관리 소프트웨어를 찾았습니다: <code>$1</code>.",
        "config-nofile": "\"$1\" 파일을 찾을 수 없습니다. 이미 삭제되었나요?",
        "config-extension-link": "당신의 위키가 [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions 확장 기능]을 지원한다는 것을 알고 계십니까?\n\n[//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category 분류별 확장 기능]을 찾아보실 수 있습니다.",
        "mainpagetext": "<strong>미디어위키가 성공적으로 설치되었습니다.</strong>",
-       "mainpagedocfooter": "[//meta.wikimedia.org/wiki/Help:Contents 이곳]에서 위키 소프트웨어에 대한 정보를 얻을 수 있습니다.\n\n== 시작하기 ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 설정하기 목록]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ 미디어위키 FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce 미디어위키 릴리스 메일링 리스트]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources 내 언어로 미디어위키 지역화]"
+       "mainpagedocfooter": "[//meta.wikimedia.org/wiki/Help:Contents 이곳]에서 위키 소프트웨어에 대한 정보를 얻을 수 있습니다.\n\n== 시작하기 ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings 설정하기 목록]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ 미디어위키 FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce 미디어위키 릴리스 메일링 리스트]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources 내 언어로 미디어위키 지역화]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam 당신의 위키에서 스팸에 대처하는 법을 배우세요]"
 }
index fd7dd5c..ac76621 100644 (file)
        "config-db-web-account": "Dä Zohjang zor Daatebangk för et Wiki",
        "config-db-web-help": "Donn ene Name un e Paßwoot för der Zohjang zor Daatebangk för et Wiki em nomaale Bedrief aanjävve.",
        "config-db-web-account-same": "Donn dersällve Zohjang nämme, wi heh beim Opsäze.",
-       "config-db-web-create": "Donn dä Zohjang aanlääje, wann dä noch nit doh es.",
+       "config-db-web-create": "Donn dä Zohjang aanlähje, wann dä noch nit doh es.",
        "config-db-web-no-create-privs": "Dä Zohjang för et Opsäze es nit berääschtesch, ene ander Zohjan enzereeschte.\nDä aanjejovve Zohjang för der Nomaalbedrief moß dröm schunn enjersht sen!",
        "config-mysql-engine": "De Zoot udder et Fommaat vun de Tabälle:",
        "config-mysql-innodb": "InnoDB",
index e104137..268092c 100644 (file)
        "config-page-dbconnect": "اتصال به پایگاه داده",
        "config-page-upgrade": "ارتقای نصب موجود",
        "config-page-dbsettings": "تنظیمات پایگاه داده",
-       "config-page-name": "نؤم",
+       "config-page-name": "نۆم",
        "config-page-options": "گزینۀل",
        "config-page-install": "نۀصب",
        "config-page-complete": "انجؤم دریا-انجؤم هنگت",
        "config-page-restart": "راه‌اندازی دوواره نصب",
-       "config-page-readme": "بخؤÛ\80Ù\86 Ø¦Û\80راÙ\86Ù\85",
+       "config-page-readme": "Ø£Ú\95اÙ\86Ù\85 Ø¨Ø®Ù\88Ù\88Û\95Ù\86",
        "config-page-releasenotes": "یادداشت‌های انتشار",
        "config-page-copying": "کپی",
        "config-page-upgradedoc": "ارتقاء",
index 5d4c5d7..63cb259 100644 (file)
@@ -72,6 +72,7 @@
        "config-apc": "[http://www.php.net/apc APC] е воспоставен",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] е воспоставен",
        "config-no-cache": "<strong>Предупредување:</strong> Не можев да го најдам [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] или [http://www.iis.net/download/WinCacheForPhp WinCache].\nМеѓускладирањето на објекти не е овозможено.",
+       "config-no-cache-apcu": "<strong>Предупредување:</strong> Не можев да го најдам [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] или [http://www.iis.net/download/WinCacheForPhp WinCache].\nМеѓускладирањето на објекти не е овозможено",
        "config-mod-security": "'''Предупредување''': на вашиот опслужувач има овозможено [http://modsecurity.org/ mod_security]. Ако не е поставено како што треба, ова може да предизвика проблеми кај МедијаВики и други програми што им овозможуваат на корисниците да објавуваат произволни содржини.\nПогледнете ја [http://modsecurity.org/documentation/ mod_security документацијата] или обратете се кај домаќинот ако наидете на случајни грешки.",
        "config-diff3-bad": "GNU diff3 не е пронајден.",
        "config-git": "Го пронајдов Git програмот за контрола на верзии: <code>$1</code>.",
index b7b11c3..734b747 100644 (file)
                ]
        },
        "config-desc": "Het installatieprogramma voor MediaWiki",
-       "config-title": "Installatie MediaWiki $1",
+       "config-title": "Installatie van MediaWiki $1",
        "config-information": "Gegevens",
        "config-localsettings-upgrade": "Er is een bestaand instellingenbestand <code>LocalSettings.php</code> gevonden.\nVoer de waarde van <code>$wgUpgradeKey</code> in in onderstaande invoerveld om deze installatie bij te werken.\nDe instelling is terug te vinden in <code>LocalSettings.php</code>.",
        "config-localsettings-cli-upgrade": "Het bestand <code>LocalSettings.php</code> is al aanwezig.\nVoer <code>update.php</code> uit om deze installatie bij te werken.",
        "config-localsettings-key": "Upgradesleutel:",
-       "config-localsettings-badkey": "De sleutel die u hebt opgegeven is onjuist",
+       "config-localsettings-badkey": "De sleutel die u hebt opgegeven is onjuist.",
        "config-upgrade-key-missing": "Er is een bestaande installatie van MediaWiki aangetroffen.\nPlaats de volgende regel onderaan uw <code>LocalSettings.php</code> om deze installatie bij te werken:\n\n$1",
        "config-localsettings-incomplete": "De bestaande inhoud van <code>LocalSettings.php</code> lijkt incompleet.\nDe variabele $1 is niet ingesteld.\nWijzig <code>LocalSettings.php</code> zodat deze variabele is ingesteld en klik op \"{{int:Config-continue}}\".",
        "config-localsettings-connection-error": "Er is een fout opgetreden tijdens het verbinden met de database met de instellingen uit <code>LocalSettings.php</code>. Los het probleem met de instellingen op en probeer het daarna opnieuw.\n\n$1",
        "config-nofile": "Het bestand \"$1\" is niet gevonden. Is het verwijderd?",
        "config-extension-link": "Weet u dat u [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions uitbreidingen] kunt gebruiken voor uw wiki?\nU kunt [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category uitbreidingen op categorie] bekijken of ga naar de [//www.mediawiki.org/wiki/Extension_Matrix uitbreidingenmatrix] om de volledige lijst met uitbreidingen te bekijken.",
        "mainpagetext": "'''De installatie van MediaWiki is geslaagd.'''",
-       "mainpagedocfooter": "Raadpleeg de [//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Contents handleiding] voor informatie over het gebruik van de wikisoftware.\n\n== Meer hulp over MediaWiki ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lijst met instellingen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Veelgestelde vragen (FAQ)]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mailinglijst voor aankondigingen van nieuwe versies]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Maak MediaWiki beschikbaar in uw taal]"
+       "mainpagedocfooter": "Raadpleeg de [//meta.wikimedia.org/wiki/Special:MyLanguage/Help:Contents handleiding] voor informatie over het gebruik van de wikisoftware.\n\n== Meer hulp over MediaWiki ==\n\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lijst met instellingen]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Veelgestelde vragen (FAQ)]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce Mailinglijst voor aankondigingen van nieuwe versies]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Maak MediaWiki beschikbaar in uw taal]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Leer hoe u spam kunt voorkomen op uw wiki]"
 }
index bb42d88..372b228 100644 (file)
@@ -87,6 +87,7 @@
        "config-apc": "Message indicates if this program is available",
        "config-wincache": "Message indicates if this program is available",
        "config-no-cache": "Status message in the MediaWiki installer environment checks.",
+       "config-no-cache-apcu": "Status message in the MediaWiki installer environment checks.",
        "config-mod-security": "Status message in the MediaWiki installer environment checks.",
        "config-diff3-bad": "Status message in the MediaWiki installer environment checks.",
        "config-git": "Message if Git version control software is available.\nParameter:\n* $1 is the <code>Git</code> executable file name.",
index c987980..bd9e762 100644 (file)
@@ -18,7 +18,8 @@
                        "Meshkov.a",
                        "Eroha",
                        "Seb35",
-                       "Striking Blue"
+                       "Striking Blue",
+                       "Ильнар"
                ]
        },
        "config-desc": "Инсталлятор MediaWiki",
@@ -41,7 +42,7 @@
        "config-back": "← Назад",
        "config-continue": "Далее →",
        "config-page-language": "Язык",
-       "config-page-welcome": "Добро пожаловать в MediaWiki!",
+       "config-page-welcome": "MediaWiki проектына рәхим итегез!",
        "config-page-dbconnect": "Подключение к базе данных",
        "config-page-upgrade": "Обновление существующей установки",
        "config-page-dbsettings": "Настройки базы данных",
@@ -88,6 +89,7 @@
        "config-apc": "[http://www.php.net/apc APC] установлен",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] установлен",
        "config-no-cache": "'''Внимание:''' Не найдены [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] или [http://www.iis.net/download/WinCacheForPhp WinCache].\nКэширование объектов будет отключено.",
+       "config-no-cache-apcu": "'''Внимание:''' Не найдены [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] или [http://www.iis.net/download/WinCacheForPhp WinCache].\nКэширование объектов будет отключено.",
        "config-mod-security": "<strong>Внимание</strong>: На вашем веб-сервере включен [http://modsecurity.org/ mod_security]/mod_security2. Многие его стандартные настройки могут вызывать проблемы для MediaWiki или другого ПО, позволяющего пользователям отправлять на сервер произвольный контент.\nОбратитесь к [http://modsecurity.org/documentation/ документации mod_security] или в службу поддержки вашего хостинг-провайдера, если вы сталкиваетесь со случайными ошибками.",
        "config-diff3-bad": "GNU diff3 не найден.",
        "config-git": "Найдена система контроля версий Git: <code>$1</code>.",
index 68b2695..7bf41b4 100644 (file)
@@ -78,6 +78,7 @@
        "config-apc": "[http://www.php.net/apc APC] är installerat",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] är installerat",
        "config-no-cache": "'''Varning:''' Kunde inte hitta [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] eller [http://www.iis.net/download/WinCacheForPhp WinCache].\nCachelagring av objekt är inte aktiverat.",
+       "config-no-cache-apcu": "'''Varning:''' Kunde inte hitta [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] eller [http://www.iis.net/download/WinCacheForPhp WinCache].\nCachelagring av objekt är inte aktiverat.",
        "config-mod-security": "'''Varning:''' Din webbserver har [http://modsecurity.org/ mod_security] aktiverat. Om felaktigt konfigurerat kan den skapa problem för MediaWiki eller annan programvara som tillåter användaren att posta godtyckligt innehåll.\nTitta på [http://modsecurity.org/documentation/ mod_security-dokumentationen] eller kontakta din värd om du påträffar slumpmässiga fel.",
        "config-diff3-bad": "GNU diff3 hittades inte.",
        "config-git": "Hittade Git-mjukvara för versionskontroll: <code>$1</code>.",
        "config-nofile": "Filen \"$1\" kunde inte hittas. Har den raderats?",
        "config-extension-link": "Visste du att din wiki stödjer [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Extensions tillägg]?\n\nDu kan bläddra [//www.mediawiki.org/wiki/Special:MyLanguage/Category:Extensions_by_category tillägg efter kategori].",
        "mainpagetext": "'''MediaWiki har installerats utan problem.'''",
-       "mainpagedocfooter": "Information om hur wiki-programvaran används finns i [//meta.wikimedia.org/wiki/Help:Contents användarguiden].\n\n== Att komma igång ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista över konfigurationsinställningar]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce E-postlista för nya versioner av MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Lokalisera MediaWiki för ditt språk]"
+       "mainpagedocfooter": "Information om hur wiki-programvaran används finns i [//meta.wikimedia.org/wiki/Help:Contents användarguiden].\n\n== Att komma igång ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Lista över konfigurationsinställningar]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki FAQ]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce E-postlista för nya versioner av MediaWiki]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Lokalisera MediaWiki för ditt språk]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Läs om hur du bekämpar spam på din wiki]"
 }
index 13cd520..0f62503 100644 (file)
@@ -6,10 +6,13 @@
                        "Ильнар"
                ]
        },
+       "config-desc": "MediaWiki йөкләүче",
+       "config-title": "MediaWiki $1 куелышы",
+       "config-information": "Мәгълүмат",
        "config-back": "← Артка",
        "config-continue": "Киләсе →",
        "config-page-language": "Тел",
        "config-page-welcome": "MediaWiki проектына рәхим итегез!",
-       "mainpagetext": "«MediaWiki» уңышлы куелды.",
+       "mainpagetext": "<strong>«MediaWiki» уңышлы куелды.</strong>",
        "mainpagedocfooter": "Бу вики турында мәгълүматны [//meta.wikimedia.org/wiki/Help:Contents биредә] табып була.\n\n== Кайбер файдалы ресурслар ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings Көйләнмәләр исемлеге (инг.)];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki турында еш бирелгән сораулар һәм җаваплар (инг.)];\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki'ның яңа версияләре турында хәбәрләр яздырып алу];\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Localisation#Translation_resources Localise MediaWiki for your language].\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Combating_spam Learn how to combat spam on your wiki]"
 }
index b4a03cb..65ffc10 100644 (file)
@@ -10,7 +10,8 @@
                        "Ата",
                        "Тест",
                        "아라",
-                       "Amire80"
+                       "Amire80",
+                       "Piramidion"
                ]
        },
        "config-desc": "Інсталятор MediaWiki",
@@ -80,6 +81,7 @@
        "config-apc": "[http://www.php.net/apc APC] встановлено",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache] встановлено",
        "config-no-cache": "'''Увага:''' Не вдалося знайти [http://www.php.net/apc APC], [http://xcache.lighttpd.net/ XCache] чи [http://www.iis.net/download/WinCacheForPhp WinCache].\nКешування об'єктів не ввімкнено.",
+       "config-no-cache-apcu": "<strong>Увага:</strong> Не вдалося знайти [http://www.php.net/apcu APCu], [http://xcache.lighttpd.net/ XCache] чи [http://www.iis.net/download/WinCacheForPhp WinCache].\nКешування об'єктів не ввімкнено.",
        "config-mod-security": "'''Увага''': на Вашому веб-сервері увімкнено [http://modsecurity.org/ mod_security]. У разі неправильних налаштувать, він може викликати проблеми MediaWiki або іншого ПЗ, яке дозволяє користувачам надсилати довільний вміст.\nЗверніться до [http://modsecurity.org/documentation/ документації mod_security] або підтримки Вашого хостера, якщо під час роботи виникають незрозумілі помилки.",
        "config-diff3-bad": "GNU diff3 не знайдено.",
        "config-git": "Знайшов програму управління версіями Git: <code>$1</code>.",
        "config-oracle-temp-ts": "Тимчасовий простір таблиць:",
        "config-type-mysql": "MySQL (або сумісний)",
        "config-type-mssql": "Microsoft SQL Server",
-       "config-support-info": "MediaWiki підтримує таки системи баз даних:\n\n$1\n\nЯкщо Ви не бачите серед перерахованих систему баз даних, яку використовуєте, виконайте вказівки, вказані вище, щоб увімкнути підтримку.",
+       "config-support-info": "MediaWiki підтримує такі системи баз даних:\n\n$1\n\nЯкщо Ви не бачите серед перерахованих систему баз даних, яку використовуєте, виконайте вказівки, вказані вище, щоб увімкнути підтримку.",
        "config-dbsupport-mysql": "* [{{int:version-db-mysql-url}} MySQL] є основною для MediaWiki і найкраще підтримується.  MediaWiki також працює із [{{int:version-db-mariadb-url}} MariaDB] та [{{int:version-db-percona-url}} Percona Server], які сумісні з MySQL.  ([http://www.php.net/manual/en/mysqli.installation.php як зібрати PHP з допомогою MySQL])",
        "config-dbsupport-postgres": "*  [{{int:version-db-postgres-url}} PostgreSQL] — популярна відкрита СУБД, альтернатива MySQL. Можуть зустрічатись деякі невеликі невиправлені помилки, не рекомендується використовувати у робочій системі.([http://www.php.net/manual/en/pgsql.installation.php як зібрати PHP з допомогою PostgreSQL]).",
        "config-dbsupport-sqlite": "*  [{{int:version-db-sqlite-url}} SQLite] — легка система баз даних, яка дуже добре підтримується. ([http://www.php.net/manual/en/pdo.installation.php Як зібрати PHP з допомогою SQLite], що використовує PDO)",
        "config-header-oracle": "Налаштування Oracle",
        "config-header-mssql": "Параметри Microsoft SQL Server",
        "config-invalid-db-type": "Невірний тип бази даних",
-       "config-missing-db-name": "Ви повинні ввести значення параметру  \"{{int:config-db-name}}\".",
-       "config-missing-db-host": "Ви повинні ввести значення параметру \"{{int:config-db-host}}\".",
-       "config-missing-db-server-oracle": "Ви повинні ввести значення параметру  \"{{int:config-db-host-oracle}}\".",
+       "config-missing-db-name": "Ви повинні ввести значення параметра «{{int:config-db-name}}».",
+       "config-missing-db-host": "Ви повинні ввести значення параметра «{{int:config-db-host}}».",
+       "config-missing-db-server-oracle": "Ви повинні ввести значення параметра «{{int:config-db-host-oracle}}».",
        "config-invalid-db-server-oracle": "Неприпустиме TNS бази даних \"$1\".\nВикористовуйте \"TNS Name\" або рядок \"Easy Connect\"  ([http://docs.oracle.com/cd/E11882_01/network.112/e10836/naming.htm Методи найменування Oracle])",
        "config-invalid-db-name": "Неприпустима назва бази даних \"$1\".\nВикористовуйте тільки ASCII букви (a-z, A-Z), цифри (0-9), знаки підкреслення (_) і дефіси (-).",
        "config-invalid-db-prefix": "Неприпустимий префікс бази даних \"$1\".\nВикористовуйте тільки ASCII букви (a-z, A-Z), цифри (0-9), знаки підкреслення (_) і дефіси (-).",
index 283ad3c..1443452 100644 (file)
@@ -2,10 +2,12 @@
        "@metadata": {
                "authors": [
                        "Wu-chinese.com",
-                       "Poiuyt"
+                       "Poiuyt",
+                       "飞舞回堂前"
                ]
        },
        "config-information": "信息",
+       "config-page-language": "闲话",
        "mainpagetext": "'''MediaWiki安装成功哉!'''",
        "mainpagedocfooter": "请访问[//meta.wikimedia.org/wiki/Help:Contents 用户手册]以获得使用此维基软件个信息!\n\n== 入门 ==\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Configuration_settings MediaWiki 配置设置列表]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MediaWiki 常见问题解答]\n* [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki 发布邮件列表]"
 }
index 5e7ab06..d64e7f5 100644 (file)
@@ -92,6 +92,7 @@
        "config-apc": "[http://www.php.net/apc APC]已安装",
        "config-wincache": "[http://www.iis.net/download/WinCacheForPhp WinCache]已安装",
        "config-no-cache": "'''警告:'''找不到[http://www.php.net/apc APC]、[http://xcache.lighttpd.net/ XCache]或[http://www.iis.net/download/WinCacheForPhp WinCache],无法启用对象缓存。\nObject caching is not enabled.",
+       "config-no-cache-apcu": "<strong>警告:</strong>找不到[http://www.php.net/apcu APCu]、[http://xcache.lighttpd.net/ XCache]或[http://www.iis.net/download/WinCacheForPhp WinCache]。\n对象缓存未启用。",
        "config-mod-security": "'''警告''':您的服务器已启动[http://modsecurity.org/ mod_security]。若其配置错误, 会导致MediaWiki和其他软件的错误并允许用户任意发布内容。如果您遇到任何错误,请查阅[http://modsecurity.org/documentation/ mod_security文档]或联系您的客服。",
        "config-diff3-bad": "找不到GNU diff3。",
        "config-git": "发现Git版本控制软件:<code>$1</code>",
index bd8291f..be16bcf 100644 (file)
@@ -137,7 +137,7 @@ class Interwiki {
                $value = self::getInterwikiCacheEntry( $prefix );
 
                $s = new Interwiki( $prefix );
-               if ( $value != '' ) {
+               if ( $value ) {
                        // Split values
                        list( $local, $url ) = explode( ' ', $value, 2 );
                        $s->mURL = $url;
@@ -155,34 +155,31 @@ class Interwiki {
         * @note More logic is explained in DefaultSettings.
         *
         * @param string $prefix Database key
-        * @return string The interwiki entry
+        * @return bool|string The interwiki entry or false if not found
         */
        protected static function getInterwikiCacheEntry( $prefix ) {
-               global $wgInterwikiCache, $wgInterwikiScopes, $wgInterwikiFallbackSite;
-               static $db, $site;
+               global $wgInterwikiScopes, $wgInterwikiFallbackSite;
+               static $site;
 
                wfDebug( __METHOD__ . "( $prefix )\n" );
                $value = false;
                try {
-                       if ( !$db ) {
-                               $db = CdbReader::open( $wgInterwikiCache );
-                       }
-                       /* Resolve site name */
+                       // Resolve site name
                        if ( $wgInterwikiScopes >= 3 && !$site ) {
-                               $site = $db->get( '__sites:' . wfWikiID() );
+                               $site = self::getCacheValue( '__sites:' . wfWikiID() );
                                if ( $site == '' ) {
                                        $site = $wgInterwikiFallbackSite;
                                }
                        }
 
-                       $value = $db->get( wfMemcKey( $prefix ) );
+                       $value = self::getCacheValue( wfMemcKey( $prefix ) );
                        // Site level
                        if ( $value == '' && $wgInterwikiScopes >= 3 ) {
-                               $value = $db->get( "_{$site}:{$prefix}" );
+                               $value = self::getCacheValue( "_{$site}:{$prefix}" );
                        }
                        // Global Level
                        if ( $value == '' && $wgInterwikiScopes >= 2 ) {
-                               $value = $db->get( "__global:{$prefix}" );
+                               $value = self::getCacheValue( "__global:{$prefix}" );
                        }
                        if ( $value == 'undef' ) {
                                $value = '';
@@ -195,6 +192,19 @@ class Interwiki {
                return $value;
        }
 
+       private static function getCacheValue( $key ) {
+               global $wgInterwikiCache;
+               static $reader;
+               if ( $reader === null ) {
+                       $reader = is_array( $wgInterwikiCache ) ? false : CdbReader::open( $wgInterwikiCache );
+               }
+               if ( $reader ) {
+                       return $reader->get( $key );
+               } else {
+                       return isset( $wgInterwikiCache[$key] ) ? $wgInterwikiCache[$key] : false;
+               }
+       }
+
        /**
         * Load the interwiki, trying first memcached then the DB
         *
index 89948f4..3ff2724 100644 (file)
@@ -286,7 +286,7 @@ abstract class JobQueue {
         * This does not require $wgJobClasses to be set for the given job type.
         * Outside callers should use JobQueueGroup::push() instead of this function.
         *
-        * @param JobSpecification|JobSpecification[] $jobs
+        * @param IJobSpecification|IJobSpecification[] $jobs
         * @param int $flags Bitfield (supports JobQueue::QOS_ATOMIC)
         * @return void
         * @throws JobQueueError
@@ -301,7 +301,7 @@ abstract class JobQueue {
         * This does not require $wgJobClasses to be set for the given job type.
         * Outside callers should use JobQueueGroup::push() instead of this function.
         *
-        * @param JobSpecification[] $jobs
+        * @param IJobSpecification[] $jobs
         * @param int $flags Bitfield (supports JobQueue::QOS_ATOMIC)
         * @return void
         * @throws MWException
@@ -333,7 +333,7 @@ abstract class JobQueue {
 
        /**
         * @see JobQueue::batchPush()
-        * @param JobSpecification[] $jobs
+        * @param IJobSpecification[] $jobs
         * @param int $flags
         */
        abstract protected function doBatchPush( array $jobs, $flags );
@@ -377,7 +377,7 @@ abstract class JobQueue {
 
        /**
         * @see JobQueue::pop()
-        * @return Job
+        * @return Job|bool
         */
        abstract protected function doPop();
 
diff --git a/includes/jobqueue/JobQueueMemory.php b/includes/jobqueue/JobQueueMemory.php
new file mode 100644 (file)
index 0000000..7dad748
--- /dev/null
@@ -0,0 +1,233 @@
+<?php
+/**
+ * PHP memory-backed job queue 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 Aaron Schulz
+ */
+
+/**
+ * Class to handle job queues stored in PHP memory for testing
+ *
+ * JobQueueGroup does not remember every queue instance, so statically track it here
+ *
+ * @ingroup JobQueue
+ * @since 1.27
+ */
+class JobQueueMemory extends JobQueue {
+       /** @var array[] */
+       protected static $data = array();
+
+       /**
+        * @see JobQueue::doBatchPush
+        *
+        * @param IJobSpecification[] $jobs
+        * @param int $flags
+        */
+       protected function doBatchPush( array $jobs, $flags ) {
+               $unclaimed =& $this->getQueueData( 'unclaimed', array() );
+
+               foreach ( $jobs as $job ) {
+                       if ( $job->ignoreDuplicates() ) {
+                               $sha1 = Wikimedia\base_convert(
+                                       sha1( serialize( $job->getDeduplicationInfo() ) ),
+                                       16, 36, 31
+                               );
+                               if ( !isset( $unclaimed[$sha1] ) ) {
+                                       $unclaimed[$sha1] = $job;
+                               }
+                       } else {
+                               $unclaimed[] = $job;
+                       }
+               }
+       }
+
+       /**
+        * @see JobQueue::supportedOrders
+        *
+        * @return string[]
+        */
+       protected function supportedOrders() {
+               return array( 'random', 'timestamp', 'fifo' );
+       }
+
+       /**
+        * @see JobQueue::optimalOrder
+        *
+        * @return string
+        */
+       protected function optimalOrder() {
+               return 'fifo';
+       }
+
+       /**
+        * @see JobQueue::doIsEmpty
+        *
+        * @return bool
+        */
+       protected function doIsEmpty() {
+               return ( $this->doGetSize() == 0 );
+       }
+
+       /**
+        * @see JobQueue::doGetSize
+        *
+        * @return int
+        */
+       protected function doGetSize() {
+               $unclaimed = $this->getQueueData( 'unclaimed' );
+
+               return $unclaimed ? count( $unclaimed ) : 0;
+       }
+
+       /**
+        * @see JobQueue::doGetAcquiredCount
+        *
+        * @return int
+        */
+       protected function doGetAcquiredCount() {
+               $claimed = $this->getQueueData( 'claimed' );
+
+               return $claimed ? count( $claimed ) : 0;
+       }
+
+       /**
+        * @see JobQueue::doPop
+        *
+        * @return Job|bool
+        */
+       protected function doPop() {
+               if ( $this->doGetSize() == 0 ) {
+                       return false;
+               }
+
+               $unclaimed =& $this->getQueueData( 'unclaimed' );
+               $claimed =& $this->getQueueData( 'claimed', array() );
+
+               if ( $this->order === 'random' ) {
+                       $key = array_rand( $unclaimed );
+               } else {
+                       reset( $unclaimed );
+                       $key = key( $unclaimed );
+               }
+
+               $spec = $unclaimed[$key];
+               unset( $unclaimed[$key] );
+               $claimed[] = $spec;
+
+               $job = $this->jobFromSpecInternal( $spec );
+
+               end( $claimed );
+               $job->metadata['claimId'] = key( $claimed );
+
+               return $job;
+       }
+
+       /**
+        * @see JobQueue::doAck
+        *
+        * @param Job $job
+        */
+       protected function doAck( Job $job ) {
+               if ( $this->getAcquiredCount() == 0 ) {
+                       return;
+               }
+
+               $claimed =& $this->getQueueData( 'claimed' );
+               unset( $claimed[$job->metadata['claimId']] );
+       }
+
+       /**
+        * @see JobQueue::doDelete
+        */
+       protected function doDelete() {
+               if ( isset( self::$data[$this->type][$this->wiki] ) ) {
+                       unset( self::$data[$this->type][$this->wiki] );
+                       if ( !self::$data[$this->type] ) {
+                               unset( self::$data[$this->type] );
+                       }
+               }
+       }
+
+       /**
+        * @see JobQueue::getAllQueuedJobs
+        *
+        * @return Iterator of Job objects.
+        */
+       public function getAllQueuedJobs() {
+               $unclaimed = $this->getQueueData( 'unclaimed' );
+               if ( !$unclaimed ) {
+                       return new ArrayIterator( array() );
+               }
+
+               $that = $this;
+               return new MappedIterator(
+                       $unclaimed,
+                       function ( $value ) use ( $that ) {
+                               $that->jobFromSpecInternal( $value );
+                       }
+               );
+       }
+
+       /**
+        * @see JobQueue::getAllAcquiredJobs
+        *
+        * @return Iterator of Job objects.
+        */
+       public function getAllAcquiredJobs() {
+               $claimed = $this->getQueueData( 'claimed' );
+               if ( !$claimed ) {
+                       return new ArrayIterator( array() );
+               }
+
+               $that = $this;
+               return new MappedIterator(
+                       $claimed,
+                       function ( $value ) use ( $that ) {
+                               $that->jobFromSpecInternal( $value );
+                       }
+               );
+       }
+
+       /**
+        * @param IJobSpecification $spec
+        *
+        * @return Job
+        */
+       public function jobFromSpecInternal( IJobSpecification $spec ) {
+               return Job::factory( $spec->getType(), $spec->getTitle(), $spec->getParams() );
+       }
+
+       /**
+        * @param string $field
+        * @param mixed $init
+        *
+        * @return mixed
+        */
+       private function &getQueueData( $field, $init = null ) {
+               if ( !isset( self::$data[$this->type][$this->wiki][$field] ) ) {
+                       if ( $init !== null ) {
+                               self::$data[$this->type][$this->wiki][$field] = $init;
+                       } else {
+                               return $init;
+                       }
+               }
+
+               return self::$data[$this->type][$this->wiki][$field];
+       }
+}
index 546093f..eda3e9c 100644 (file)
@@ -186,7 +186,7 @@ class JobQueueRedis extends JobQueue {
 
        /**
         * @see JobQueue::doBatchPush()
-        * @param array $jobs
+        * @param IJobSpecification[] $jobs
         * @param int $flags
         * @return void
         * @throws JobQueueError
index 5666415..4ab9f5a 100644 (file)
@@ -40,6 +40,10 @@ 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 ERROR_BACKOFF_TTL = 1; // seconds to back off a queue due to errors
+
        /**
         * @param callable $debug Optional debug output handler
         */
@@ -98,47 +102,42 @@ class JobRunner implements LoggerAwareInterface {
                $maxTime = isset( $options['maxTime'] ) ? $options['maxTime'] : false;
                $noThrottle = isset( $options['throttle'] ) && !$options['throttle'];
 
+               // Bail if job type is invalid
                if ( $type !== false && !isset( $wgJobClasses[$type] ) ) {
                        $response['reached'] = 'none-possible';
                        return $response;
                }
-
-               // Bail out if in read-only mode
+               // Bail out if DB is in read-only mode
                if ( wfReadOnly() ) {
                        $response['reached'] = 'read-only';
                        return $response;
                }
-
-               // Catch huge single updates that lead to slave lag
-               $trxProfiler = Profiler::instance()->getTransactionProfiler();
-               $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
-               $trxProfiler->setExpectations( $wgTrxProfilerLimits['JobRunner'], __METHOD__ );
-
                // Bail out if there is too much DB lag.
                // This check should not block as we want to try other wiki queues.
-               $maxAllowedLag = 3;
                list( , $maxLag ) = wfGetLB( wfWikiID() )->getMaxLag();
-               if ( $maxLag >= $maxAllowedLag ) {
+               if ( $maxLag >= self::MAX_ALLOWED_LAG ) {
                        $response['reached'] = 'slave-lag-limit';
                        return $response;
                }
 
-               $group = JobQueueGroup::singleton();
-
                // Flush any pending DB writes for sanity
-               wfGetLBFactory()->commitAll();
+               wfGetLBFactory()->commitAll( __METHOD__ );
+
+               // Catch huge single updates that lead to slave lag
+               $trxProfiler = Profiler::instance()->getTransactionProfiler();
+               $trxProfiler->setLogger( LoggerFactory::getInstance( 'DBPerformance' ) );
+               $trxProfiler->setExpectations( $wgTrxProfilerLimits['JobRunner'], __METHOD__ );
 
                // Some jobs types should not run until a certain timestamp
                $backoffs = array(); // map of (type => UNIX expiry)
                $backoffDeltas = array(); // map of (type => seconds)
                $wait = 'wait'; // block to read backoffs the first time
 
+               $group = JobQueueGroup::singleton();
                $stats = RequestContext::getMain()->getStats();
                $jobsPopped = 0;
                $timeMsTotal = 0;
-               $flags = JobQueueGroup::USE_CACHE;
                $startTime = microtime( true ); // time since jobs started running
-               $checkLagPeriod = 1.0; // check slave lag this many seconds
                $lastCheckTime = 1; // timestamp of last slave check
                do {
                        // Sync the persistent backoffs with concurrent runners
@@ -147,7 +146,11 @@ class JobRunner implements LoggerAwareInterface {
                        $wait = 'nowait'; // less important now
 
                        if ( $type === false ) {
-                               $job = $group->pop( JobQueueGroup::TYPE_DEFAULT, $flags, $blacklist );
+                               $job = $group->pop(
+                                       JobQueueGroup::TYPE_DEFAULT,
+                                       JobQueueGroup::USE_CACHE,
+                                       $blacklist
+                               );
                        } elseif ( in_array( $type, $blacklist ) ) {
                                $job = false; // requested queue in backoff state
                        } else {
@@ -155,6 +158,7 @@ class JobRunner implements LoggerAwareInterface {
                        }
 
                        if ( $job ) { // found a job
+                               ++$jobsPopped;
                                $popTime = time();
                                $jType = $job->getType();
 
@@ -169,80 +173,26 @@ class JobRunner implements LoggerAwareInterface {
                                        $backoffs = $this->syncBackoffDeltas( $backoffs, $backoffDeltas, $wait );
                                }
 
-                               $msg = $job->toString() . " STARTING";
-                               $this->logger->debug( $msg );
-                               $this->debugCallback( $msg );
-
-                               // Run the job...
-                               $jobStartTime = microtime( true );
-                               try {
-                                       ++$jobsPopped;
-                                       $status = $job->run();
-                                       $error = $job->getLastError();
-                                       $this->commitMasterChanges( $job );
-
-                                       DeferredUpdates::doUpdates();
-                                       $this->commitMasterChanges( $job );
-                               } catch ( Exception $e ) {
-                                       MWExceptionHandler::rollbackMasterChangesAndLog( $e );
-                                       $status = false;
-                                       $error = get_class( $e ) . ': ' . $e->getMessage();
-                                       MWExceptionHandler::logException( $e );
-                               }
-                               // 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();
-                               // Clear out title cache data from prior snapshots
-                               LinkCache::singleton()->clear();
-                               $timeMs = intval( ( microtime( true ) - $jobStartTime ) * 1000 );
-                               $timeMsTotal += $timeMs;
-
-                               // Record how long jobs wait before getting popped
-                               $readyTs = $job->getReadyTimestamp();
-                               if ( $readyTs ) {
-                                       $pickupDelay = max( 0, $popTime - $readyTs );
-                                       $stats->timing( 'jobqueue.pickup_delay.all', 1000 * $pickupDelay );
-                                       $stats->timing( "jobqueue.pickup_delay.$jType", 1000 * $pickupDelay );
-                               }
-                               // Record root job age for jobs being run
-                               $root = $job->getRootJobParams();
-                               if ( $root['rootJobTimestamp'] ) {
-                                       $age = max( 0, $popTime - wfTimestamp( TS_UNIX, $root['rootJobTimestamp'] ) );
-                                       $stats->timing( "jobqueue.pickup_root_age.$jType", 1000 * $age );
-                               }
-                               // Track the execution time for jobs
-                               $stats->timing( "jobqueue.run.$jType", $timeMs );
-
-                               // Mark the job as done on success or when the job cannot be retried
-                               if ( $status !== false || !$job->allowRetries() ) {
-                                       $group->ack( $job ); // done
+                               $info = $this->executeJob( $job, $stats, $popTime );
+                               if ( $info['status'] !== false || !$job->allowRetries() ) {
+                                       $group->ack( $job ); // succeeded or job cannot be retried
                                }
 
                                // Back off of certain jobs for a while (for throttling and for errors)
-                               if ( $status === false && mt_rand( 0, 49 ) == 0 ) {
-                                       $ttw = max( $ttw, 30 ); // too many errors
+                               if ( $info['status'] === false && mt_rand( 0, 49 ) == 0 ) {
+                                       $ttw = max( $ttw, self::ERROR_BACKOFF_TTL ); // too many errors
                                        $backoffDeltas[$jType] = isset( $backoffDeltas[$jType] )
                                                ? $backoffDeltas[$jType] + $ttw
                                                : $ttw;
                                }
 
-                               if ( $status === false ) {
-                                       $msg = $job->toString() . " t=$timeMs error={$error}";
-                                       $this->logger->error( $msg );
-                                       $this->debugCallback( $msg );
-                               } else {
-                                       $msg = $job->toString() . " t=$timeMs good";
-                                       $this->logger->info( $msg );
-                                       $this->debugCallback( $msg );
-                               }
-
                                $response['jobs'][] = array(
                                        'type'   => $jType,
-                                       'status' => ( $status === false ) ? 'failed' : 'ok',
-                                       'error'  => $error,
-                                       'time'   => $timeMs
+                                       'status' => ( $info['status'] === false ) ? 'failed' : 'ok',
+                                       'error'  => $info['error'],
+                                       'time'   => $info['timeMs']
                                );
+                               $timeMsTotal += $info['timeMs'];
 
                                // Break out if we hit the job count or wall time limits...
                                if ( $maxJobs && $jobsPopped >= $maxJobs ) {
@@ -257,8 +207,8 @@ class JobRunner implements LoggerAwareInterface {
                                // 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 >= $checkLagPeriod || $timePassed < 0 ) {
-                                       if ( !wfWaitForSlaves( $lastCheckTime, false, '*', $maxAllowedLag ) ) {
+                               if ( $timePassed >= self::LAG_CHECK_PERIOD || $timePassed < 0 ) {
+                                       if ( !wfWaitForSlaves( $lastCheckTime, false, '*', self::MAX_ALLOWED_LAG ) ) {
                                                $response['reached'] = 'slave-lag-limit';
                                                break;
                                        }
@@ -288,6 +238,85 @@ class JobRunner implements LoggerAwareInterface {
                return $response;
        }
 
+       /**
+        * @param Job $job
+        * @param BufferingStatsdDataFactory $stats
+        * @param float $popTime
+        * @return array Map of status/error/timeMs
+        */
+       private function executeJob( Job $job, $stats, $popTime ) {
+               $jType = $job->getType();
+               $msg = $job->toString() . " STARTING";
+               $this->logger->debug( $msg );
+               $this->debugCallback( $msg );
+
+               // Run the job...
+               $rssStart = $this->getMaxRssKb();
+               $jobStartTime = microtime( true );
+               try {
+                       $status = $job->run();
+                       $error = $job->getLastError();
+                       $this->commitMasterChanges( $job );
+
+                       DeferredUpdates::doUpdates();
+                       $this->commitMasterChanges( $job );
+               } catch ( Exception $e ) {
+                       MWExceptionHandler::rollbackMasterChangesAndLog( $e );
+                       $status = false;
+                       $error = get_class( $e ) . ': ' . $e->getMessage();
+                       MWExceptionHandler::logException( $e );
+               }
+               // 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__ );
+               // Clear out title cache data from prior snapshots
+               LinkCache::singleton()->clear();
+               $timeMs = intval( ( microtime( true ) - $jobStartTime ) * 1000 );
+               $rssEnd = $this->getMaxRssKb();
+
+               // Record how long jobs wait before getting popped
+               $readyTs = $job->getReadyTimestamp();
+               if ( $readyTs ) {
+                       $pickupDelay = max( 0, $popTime - $readyTs );
+                       $stats->timing( 'jobqueue.pickup_delay.all', 1000 * $pickupDelay );
+                       $stats->timing( "jobqueue.pickup_delay.$jType", 1000 * $pickupDelay );
+               }
+               // Record root job age for jobs being run
+               $root = $job->getRootJobParams();
+               if ( $root['rootJobTimestamp'] ) {
+                       $age = max( 0, $popTime - wfTimestamp( TS_UNIX, $root['rootJobTimestamp'] ) );
+                       $stats->timing( "jobqueue.pickup_root_age.$jType", 1000 * $age );
+               }
+               // Track the execution time for jobs
+               $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 );
+               }
+
+               if ( $status === false ) {
+                       $msg = $job->toString() . " t=$timeMs error={$error}";
+                       $this->logger->error( $msg );
+                       $this->debugCallback( $msg );
+               } else {
+                       $msg = $job->toString() . " t=$timeMs good";
+                       $this->logger->info( $msg );
+                       $this->debugCallback( $msg );
+               }
+
+               return array( 'status' => $status, 'error' => $error, 'timeMs' => $timeMs );
+       }
+
+       /**
+        * @return int|null Max memory RSS in kilobytes
+        */
+       private function getMaxRssKb() {
+               $info = wfGetRusage() ?: array();
+               // see http://linux.die.net/man/2/getrusage
+               return isset( $info['ru_maxrss'] ) ? (int)$info['ru_maxrss'] : null;
+       }
+
        /**
         * @param Job $job
         * @return int Seconds for this runner to avoid doing more jobs of this type
@@ -464,7 +493,7 @@ class JobRunner implements LoggerAwareInterface {
                ) {
                        // Writes are all to foreign DBs, named locks don't form queues,
                        // or $wgJobSerialCommitThreshold is not reached; commit changes now
-                       wfGetLBFactory()->commitMasterChanges();
+                       wfGetLBFactory()->commitMasterChanges( __METHOD__ );
                        return;
                }
 
@@ -496,7 +525,7 @@ class JobRunner implements LoggerAwareInterface {
                } );
 
                // Actually commit the DB master changes
-               wfGetLBFactory()->commitMasterChanges();
+               wfGetLBFactory()->commitMasterChanges( __METHOD__ );
 
                // Release the lock
                $dbwSerial->unlock( 'jobrunner-serial-commit', __METHOD__ );
index ade4810..0491e64 100644 (file)
@@ -93,10 +93,10 @@ class UploadFromUrlJob extends Job {
                                                        $this->params['url'] )->text()
                                        );
                                } else {
-                                       wfSetupSession( $this->params['sessionId'] );
-                                       $this->storeResultInSession( 'Warning',
+                                       $session = MediaWiki\Session\SessionManager::singleton()
+                                               ->getSessionById( $this->params['sessionId'] );
+                                       $this->storeResultInSession( $session, 'Warning',
                                                'warnings', $warnings );
-                                       session_write_close();
                                }
 
                                return true;
@@ -139,15 +139,15 @@ class UploadFromUrlJob extends Job {
                                        )->text() );
                        }
                } else {
-                       wfSetupSession( $this->params['sessionId'] );
+                       $session = MediaWiki\Session\SessionManager::singleton()
+                               ->getSessionById( $this->params['sessionId'] );
                        if ( $status->isOk() ) {
-                               $this->storeResultInSession( 'Success',
+                               $this->storeResultInSession( $session, 'Success',
                                        'filename', $this->upload->getLocalFile()->getName() );
                        } else {
-                               $this->storeResultInSession( 'Failure',
+                               $this->storeResultInSession( $session, 'Failure',
                                        'errors', $status->getErrorsArray() );
                        }
-                       session_write_close();
                }
        }
 
@@ -155,33 +155,59 @@ class UploadFromUrlJob extends Job {
         * Store a result in the session data. Note that the caller is responsible
         * for appropriate session_start and session_write_close calls.
         *
+        * @param MediaWiki\\Session\\Session|null $session Session to store result into
         * @param string $result The result (Success|Warning|Failure)
         * @param string $dataKey The key of the extra data
         * @param mixed $dataValue The extra data itself
         */
-       protected function storeResultInSession( $result, $dataKey, $dataValue ) {
-               $session =& self::getSessionData( $this->params['sessionKey'] );
-               $session['result'] = $result;
-               $session[$dataKey] = $dataValue;
+       protected function storeResultInSession(
+               MediaWiki\Session\Session $session = null, $result, $dataKey, $dataValue
+       ) {
+               if ( $session ) {
+                       $data = self::getSessionData( $session, $this->params['sessionKey'] );
+                       $data['result'] = $result;
+                       $data[$dataKey] = $dataValue;
+                       self::setSessionData( $session, $this->params['sessionKey'], $data );
+               } else {
+                       wfDebug( __METHOD__ . ': Cannot store result in session, session does not exist' );
+               }
        }
 
        /**
         * Initialize the session data. Sets the initial result to queued.
         */
        public function initializeSessionData() {
-               $session =& self::getSessionData( $this->params['sessionKey'] );
-               $session['result'] = 'Queued';
+               $session = MediaWiki\Session\SessionManager::getGlobalSession();
+               $data = self::getSessionData( $session, $this->params['sessionKey'] );
+               $data['result'] = 'Queued';
+               self::setSessionData( $session, $this->params['sessionKey'], $data );
        }
 
        /**
+        * @param MediaWiki\\Session\\Session $session
         * @param string $key
         * @return mixed
         */
-       public static function &getSessionData( $key ) {
-               if ( !isset( $_SESSION[self::SESSION_KEYNAME][$key] ) ) {
-                       $_SESSION[self::SESSION_KEYNAME][$key] = array();
+       public static function getSessionData( MediaWiki\Session\Session $session, $key ) {
+               $data = $session->get( self::SESSION_KEYNAME );
+               if ( !is_array( $data ) || !isset( $data[$key] ) ) {
+                       self::setSessionData( $session, $key, array() );
+                       return array();
                }
+               return $data[$key];
+       }
 
-               return $_SESSION[self::SESSION_KEYNAME][$key];
+       /**
+        * @param MediaWiki\\Session\\Session $session
+        * @param string $key
+        * @param mixed $value
+        */
+       public static function setSessionData( MediaWiki\Session\Session $session, $key, $value ) {
+               $data = $session->get( self::SESSION_KEYNAME, array() );
+               if ( !is_array( $data ) ) {
+                       $data = array();
+               }
+               $data[$key] = $value;
+               $session->set( self::SESSION_KEYNAME, $data );
        }
 }
index 796acb5..bb55ac6 100644 (file)
@@ -27,9 +27,11 @@ class ComposerJson {
         */
        public function getRequiredDependencies() {
                $deps = array();
-               foreach ( $this->contents['require'] as $package => $version ) {
-                       if ( $package !== "php" && strpos( $package, 'ext-' ) !== 0 ) {
-                               $deps[$package] = self::normalizeVersion( $version );
+               if ( isset( $this->contents['require'] ) ) {
+                       foreach ( $this->contents['require'] as $package => $version ) {
+                               if ( $package !== "php" && strpos( $package, 'ext-' ) !== 0 ) {
+                                       $deps[$package] = self::normalizeVersion( $version );
+                               }
                        }
                }
 
index 703c195..b9be43d 100644 (file)
@@ -507,18 +507,27 @@ abstract class BagOStuff implements IExpiringStore, LoggerAwareInterface {
        /**
         * Increase stored value of $key by $value while preserving its TTL
         *
-        * This will create the key with value $init and TTL $ttl if not present
+        * This will create the key with value $init and TTL $ttl instead if not present
         *
         * @param string $key
         * @param int $ttl
         * @param int $value
         * @param int $init
-        * @return bool
+        * @return int|bool New value or false on failure
         * @since 1.24
         */
        public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
-               return $this->incr( $key, $value ) ||
-                       $this->add( $key, (int)$init, $ttl ) || $this->incr( $key, $value );
+               $newValue = $this->incr( $key, $value );
+               if ( $newValue === false ) {
+                       // No key set; initialize
+                       $newValue = $this->add( $key, (int)$init, $ttl ) ? $init : false;
+               }
+               if ( $newValue === false ) {
+                       // Raced out initializing; increment
+                       $newValue = $this->incr( $key, $value );
+               }
+
+               return $newValue;
        }
 
        /**
index 01f8ccc..4aa868e 100644 (file)
@@ -450,7 +450,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *
         * Example usage:
         * @code
-        *     $dbw->begin(); // start of request
+        *     $dbw->begin( __METHOD__ ); // start of request
         *     ... <execute some stuff> ...
         *     // Update the row in the DB
         *     $dbw->update( ... );
@@ -460,7 +460,7 @@ class WANObjectCache implements IExpiringStore, LoggerAwareInterface {
         *         $cache->delete( $key );
         *     } );
         *     ... <execute some stuff> ...
-        *     $dbw->commit(); // end of request
+        *     $dbw->commit( __METHOD__ ); // end of request
         * @endcode
         *
         * The $ttl parameter can be used when purging values that have not actually changed
index 8427adb..db588fd 100644 (file)
 
 /**
  * Interface for log entries. Every log entry has these methods.
+ *
  * @since 1.19
  */
 interface LogEntry {
+
        /**
         * The main log type.
+        *
         * @return string
         */
        public function getType();
 
        /**
         * The log subtype.
+        *
         * @return string
         */
        public function getSubtype();
 
        /**
         * The full logtype in format maintype/subtype.
+        *
         * @return string
         */
        public function getFullType();
 
        /**
         * Get the extra parameters stored for this message.
+        *
         * @return array
         */
        public function getParameters();
 
        /**
         * Get the user for performed this action.
+        *
         * @return User
         */
        public function getPerformer();
 
        /**
         * Get the target page of this action.
+        *
         * @return Title
         */
        public function getTarget();
 
        /**
         * Get the timestamp when the action was executed.
+        *
         * @return string
         */
        public function getTimestamp();
 
        /**
         * Get the user provided comment.
+        *
         * @return string
         */
        public function getComment();
 
        /**
         * Get the access restriction.
+        *
         * @return string
         */
        public function getDeleted();
@@ -96,9 +107,11 @@ interface LogEntry {
 
 /**
  * Extends the LogEntryInterface with some basic functionality
+ *
  * @since 1.19
  */
 abstract class LogEntryBase implements LogEntry {
+
        public function getFullType() {
                return $this->getType() . '/' . $this->getSubtype();
        }
@@ -110,6 +123,7 @@ abstract class LogEntryBase implements LogEntry {
        /**
         * Whether the parameters for this log are stored in new or
         * old format.
+        *
         * @return bool
         */
        public function isLegacy() {
@@ -119,9 +133,9 @@ abstract class LogEntryBase implements LogEntry {
        /**
         * Create a blob from a parameter array
         *
+        * @since 1.26
         * @param array $params
         * @return string
-        * @since 1.26
         */
        public static function makeParamBlob( $params ) {
                return serialize( (array)$params );
@@ -130,9 +144,9 @@ abstract class LogEntryBase implements LogEntry {
        /**
         * Extract a parameter array from a blob
         *
+        * @since 1.26
         * @param string $blob
         * @return array
-        * @since 1.26
         */
        public static function extractParams( $blob ) {
                return unserialize( $blob );
@@ -141,15 +155,16 @@ abstract class LogEntryBase implements LogEntry {
 
 /**
  * This class wraps around database result row.
+ *
  * @since 1.19
  */
 class DatabaseLogEntry extends LogEntryBase {
-       // Static->
 
        /**
         * Returns array of information that is needed for querying
         * log entries. Array contains the following keys:
         * tables, fields, conds, options and join_conds
+        *
         * @return array
         */
        public static function getSelectQueryData() {
@@ -163,7 +178,7 @@ class DatabaseLogEntry extends LogEntryBase {
                );
 
                $joins = array(
-                       // IP's don't have an entry in user table
+                       // IPs don't have an entry in user table
                        'user' => array( 'LEFT JOIN', 'log_user=user_id' ),
                );
 
@@ -179,6 +194,7 @@ class DatabaseLogEntry extends LogEntryBase {
        /**
         * Constructs new LogEntry from database result row.
         * Supports rows from both logging and recentchanges table.
+        *
         * @param stdClass|array $row
         * @return DatabaseLogEntry
         */
@@ -191,17 +207,19 @@ class DatabaseLogEntry extends LogEntryBase {
                }
        }
 
-       // Non-static->
-
        /** @var stdClass Database result row. */
        protected $row;
 
        /** @var User */
        protected $performer;
 
-       /** @var bool Whether the parameters for this log entry are stored in new
-        *    or old format.
-        */
+       /** @var array Parameters for log entry */
+       protected $params;
+
+       /** @var int A rev id associated to the log entry */
+       protected $revId = null;
+
+       /** @var bool Whether the parameters for this log entry are stored in new or old format. */
        protected $legacy;
 
        protected function __construct( $row ) {
@@ -210,6 +228,7 @@ class DatabaseLogEntry extends LogEntryBase {
 
        /**
         * Returns the unique database id.
+        *
         * @return int
         */
        public function getId() {
@@ -218,23 +237,19 @@ class DatabaseLogEntry extends LogEntryBase {
 
        /**
         * Returns whatever is stored in the database field.
+        *
         * @return string
         */
        protected function getRawParameters() {
                return $this->row->log_params;
        }
 
-       // LogEntryBase->
-
        public function isLegacy() {
-               // This does the check
+               // This extracts the property
                $this->getParameters();
-
                return $this->legacy;
        }
 
-       // LogEntry->
-
        public function getType() {
                return $this->row->log_type;
        }
@@ -256,21 +271,34 @@ class DatabaseLogEntry extends LogEntryBase {
                                $this->params = LogPage::extractParams( $blob );
                                $this->legacy = true;
                        }
+
+                       if ( isset( $this->params['associated_rev_id'] ) ) {
+                               $this->revId = $this->params['associated_rev_id'];
+                               unset( $this->params['associated_rev_id'] );
+                       }
                }
 
                return $this->params;
        }
 
+       public function getAssociatedRevId() {
+               // This extracts the property
+               $this->getParameters();
+               return $this->revId;
+       }
+
        public function getPerformer() {
                if ( !$this->performer ) {
                        $userId = (int)$this->row->log_user;
-                       if ( $userId !== 0 ) { // logged-in users
+                       if ( $userId !== 0 ) {
+                               // logged-in users
                                if ( isset( $this->row->user_name ) ) {
                                        $this->performer = User::newFromRow( $this->row );
                                } else {
                                        $this->performer = User::newFromId( $userId );
                                }
-                       } else { // IP users
+                       } else {
+                               // IP users
                                $userText = $this->row->log_user_text;
                                $this->performer = User::newFromName( $userText, false );
                        }
@@ -310,7 +338,9 @@ class RCDatabaseLogEntry extends DatabaseLogEntry {
                return $this->row->rc_params;
        }
 
-       // LogEntry->
+       public function getAssociatedRevId() {
+               return $this->row->rc_this_oldid;
+       }
 
        public function getType() {
                return $this->row->rc_log_type;
@@ -357,8 +387,8 @@ class RCDatabaseLogEntry extends DatabaseLogEntry {
 }
 
 /**
- * Class for creating log entries manually, for
- * example to inject them into the database.
+ * Class for creating log entries manually, to inject them into the database.
+ *
  * @since 1.19
  */
 class ManualLogEntry extends LogEntryBase {
@@ -386,6 +416,9 @@ class ManualLogEntry extends LogEntryBase {
        /** @var string Comment for the log entry */
        protected $comment = '';
 
+       /** @var int A rev id associated to the log entry */
+       protected $revId = 0;
+
        /** @var int Deletion state of the log entry */
        protected $deleted;
 
@@ -399,7 +432,6 @@ class ManualLogEntry extends LogEntryBase {
         * Constructor.
         *
         * @since 1.19
-        *
         * @param string $type
         * @param string $subtype
         */
@@ -414,15 +446,19 @@ class ManualLogEntry extends LogEntryBase {
         * You can pass params to the log action message by prefixing the keys with
         * a number and optional type, using colons to separate the fields. The
         * numbering should start with number 4, the first three parameters are
-        * hardcoded for every message. Example:
-        * $entry->setParameters(
-        *   '4::color' => 'blue',
-        *   '5:number:count' => 3000,
-        *   'animal' => 'dog'
-        * );
+        * hardcoded for every message.
         *
-        * @since 1.19
+        * If you want to store stuff that should not be available in messages, don't
+        * prefix the array key with a number and don't use the colons.
+        *
+        * Example:
+        *   $entry->setParameters(
+        *     '4::color' => 'blue',
+        *     '5:number:count' => 3000,
+        *     'animal' => 'dog'
+        *   );
         *
+        * @since 1.19
         * @param array $parameters Associative array
         */
        public function setParameters( $parameters ) {
@@ -444,7 +480,6 @@ class ManualLogEntry extends LogEntryBase {
         * Set the user that performed the action being logged.
         *
         * @since 1.19
-        *
         * @param User $performer
         */
        public function setPerformer( User $performer ) {
@@ -455,7 +490,6 @@ class ManualLogEntry extends LogEntryBase {
         * Set the title of the object changed.
         *
         * @since 1.19
-        *
         * @param Title $target
         */
        public function setTarget( Title $target ) {
@@ -466,7 +500,6 @@ class ManualLogEntry extends LogEntryBase {
         * Set the timestamp of when the logged action took place.
         *
         * @since 1.19
-        *
         * @param string $timestamp
         */
        public function setTimestamp( $timestamp ) {
@@ -477,13 +510,25 @@ class ManualLogEntry extends LogEntryBase {
         * Set a comment associated with the action being logged.
         *
         * @since 1.19
-        *
         * @param string $comment
         */
        public function setComment( $comment ) {
                $this->comment = $comment;
        }
 
+       /**
+        * Set an associated revision id.
+        *
+        * For example, the ID of the revision that was inserted to mark a page move
+        * or protection, file upload, etc.
+        *
+        * @since 1.27
+        * @param int $revId
+        */
+       public function setAssociatedRevId( $revId ) {
+               $this->revId = $revId;
+       }
+
        /**
         * Set the 'legacy' flag
         *
@@ -495,18 +540,18 @@ class ManualLogEntry extends LogEntryBase {
        }
 
        /**
-        * TODO: document
+        * Set the 'deleted' flag.
         *
         * @since 1.19
-        *
-        * @param int $deleted
+        * @param int $deleted One of LogPage::DELETED_* bitfield constants
         */
        public function setDeleted( $deleted ) {
                $this->deleted = $deleted;
        }
 
        /**
-        * Inserts the entry into the logging table.
+        * Insert the entry into the `logging` table.
+        *
         * @param IDatabase $dbw
         * @return int ID of the log entry
         * @throws MWException
@@ -521,12 +566,22 @@ class ManualLogEntry extends LogEntryBase {
                        $this->timestamp = wfTimestampNow();
                }
 
-               # Trim spaces on user supplied text
+               // Trim spaces on user supplied text
                $comment = trim( $this->getComment() );
 
-               # Truncate for whole multibyte characters.
+               // Truncate for whole multibyte characters.
                $comment = $wgContLang->truncate( $comment, 255 );
 
+               $params = $this->getParameters();
+               $relations = $this->relations;
+
+               // Additional fields for which there's no space in the database table schema
+               $revId = $this->getAssociatedRevId();
+               if ( $revId ) {
+                       $params['associated_rev_id'] = $revId;
+                       $relations['associated_rev_id'] = $revId;
+               }
+
                $data = array(
                        'log_id' => $id,
                        'log_type' => $this->getType(),
@@ -538,7 +593,7 @@ class ManualLogEntry extends LogEntryBase {
                        'log_title' => $this->getTarget()->getDBkey(),
                        'log_page' => $this->getTarget()->getArticleID(),
                        'log_comment' => $comment,
-                       'log_params' => LogEntryBase::makeParamBlob( $this->getParameters() ),
+                       'log_params' => LogEntryBase::makeParamBlob( $params ),
                );
                if ( isset( $this->deleted ) ) {
                        $data['log_deleted'] = $this->deleted;
@@ -548,7 +603,7 @@ class ManualLogEntry extends LogEntryBase {
                $this->id = !is_null( $id ) ? $id : $dbw->insertId();
 
                $rows = array();
-               foreach ( $this->relations as $tag => $values ) {
+               foreach ( $relations as $tag => $values ) {
                        if ( !strlen( $tag ) ) {
                                throw new MWException( "Got empty log search tag." );
                        }
@@ -574,6 +629,7 @@ class ManualLogEntry extends LogEntryBase {
 
        /**
         * Get a RecentChanges object for the log entry
+        *
         * @param int $newId
         * @return RecentChange
         * @since 1.23
@@ -587,10 +643,8 @@ class ManualLogEntry extends LogEntryBase {
                $user = $this->getPerformer();
                $ip = "";
                if ( $user->isAnon() ) {
-                       /*
-                        * "MediaWiki default" and friends may have
-                        * no IP address in their name
-                        */
+                       // "MediaWiki default" and friends may have
+                       // no IP address in their name
                        if ( IP::isIPAddress( $user->getName() ) ) {
                                $ip = $user->getName();
                        }
@@ -608,12 +662,14 @@ class ManualLogEntry extends LogEntryBase {
                        $this->getComment(),
                        LogEntryBase::makeParamBlob( $this->getParameters() ),
                        $newId,
-                       $formatter->getIRCActionComment() // Used for IRC feeds
+                       $formatter->getIRCActionComment(), // Used for IRC feeds
+                       $this->getAssociatedRevId() // Used for e.g. moves and uploads
                );
        }
 
        /**
-        * Publishes the log entry.
+        * Publish the log entry.
+        *
         * @param int $newId Id of the log entry.
         * @param string $to One of: rcandudp (default), rc, udp
         */
@@ -632,9 +688,13 @@ class ManualLogEntry extends LogEntryBase {
                if ( $to === 'udp' || $to === 'rcandudp' ) {
                        $rc->notifyRCFeeds();
                }
-       }
 
-       // LogEntry->
+               // Log the autopatrol if an associated rev id was passed
+               if ( $this->getAssociatedRevId() > 0 &&
+                       $rc->getAttribute( 'rc_patrolled' ) === 1 ) {
+                       PatrolLog::record( $rc, true, $this->getPerformer() );
+               }
+       }
 
        public function getType() {
                return $this->type;
@@ -672,6 +732,14 @@ class ManualLogEntry extends LogEntryBase {
                return $this->comment;
        }
 
+       /**
+        * @since 1.27
+        * @return int
+        */
+       public function getAssociatedRevId() {
+               return $this->revId;
+       }
+
        /**
         * @since 1.25
         * @return bool
index 2e28917..6b70cec 100644 (file)
@@ -211,42 +211,6 @@ class LogPage {
                return in_array( $type, LogPage::validTypes() );
        }
 
-       /**
-        * Get the name for the given log type
-        *
-        * @param string $type Log type
-        * @return string Log name
-        * @deprecated since 1.19, warnings in 1.21. Use getName()
-        */
-       public static function logName( $type ) {
-               global $wgLogNames;
-
-               wfDeprecated( __METHOD__, '1.21' );
-
-               if ( isset( $wgLogNames[$type] ) ) {
-                       return str_replace( '_', ' ', wfMessage( $wgLogNames[$type] )->text() );
-               } else {
-                       // Bogus log types? Perhaps an extension was removed.
-                       return $type;
-               }
-       }
-
-       /**
-        * Get the log header for the given log type
-        *
-        * @todo handle missing log types
-        * @param string $type Logtype
-        * @return string Header text of this logtype
-        * @deprecated since 1.19, warnings in 1.21. Use getDescription()
-        */
-       public static function logHeader( $type ) {
-               global $wgLogHeaders;
-
-               wfDeprecated( __METHOD__, '1.21' );
-
-               return wfMessage( $wgLogHeaders[$type] )->parse();
-       }
-
        /**
         * Generate text for a log entry.
         * Only LogFormatter should call this function.
diff --git a/includes/objectcache/ObjectCacheSessionHandler.php b/includes/objectcache/ObjectCacheSessionHandler.php
deleted file mode 100644 (file)
index cc85074..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-/**
- * Session storage in object cache.
- *
- * 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
- */
-
-use MediaWiki\Logger\LoggerFactory;
-
-/**
- * Session storage in object cache.
- * Used if $wgSessionsInObjectCache is true.
- *
- * @ingroup Cache
- */
-class ObjectCacheSessionHandler {
-       /** @var array Map of (session ID => SHA-1 of the data) */
-       protected static $hashCache = array();
-
-       /**
-        * Install a session handler for the current web request
-        */
-       static function install() {
-               session_set_save_handler(
-                       array( __CLASS__, 'open' ),
-                       array( __CLASS__, 'close' ),
-                       array( __CLASS__, 'read' ),
-                       array( __CLASS__, 'write' ),
-                       array( __CLASS__, 'destroy' ),
-                       array( __CLASS__, 'gc' ) );
-
-               // It's necessary to register a shutdown function to call session_write_close(),
-               // because by the time the request shutdown function for the session module is
-               // called, the BagOStuff has already been destroyed. Shutdown functions registered
-               // this way are called before object destruction.
-               register_shutdown_function( array( __CLASS__, 'handleShutdown' ) );
-       }
-
-       /**
-        * Get the cache storage object to use for session storage
-        * @return BagOStuff
-        */
-       protected static function getCache() {
-               global $wgSessionCacheType;
-
-               return ObjectCache::getInstance( $wgSessionCacheType );
-       }
-
-       /**
-        * Get a cache key for the given session id.
-        *
-        * @param string $id Session id
-        * @return string Cache key
-        */
-       protected static function getKey( $id ) {
-               return wfMemcKey( 'session', $id );
-       }
-
-       /**
-        * @param mixed $data
-        * @return string
-        */
-       protected static function getHash( $data ) {
-               return sha1( serialize( $data ) );
-       }
-
-       /**
-        * Callback when opening a session.
-        *
-        * @param string $save_path Path used to store session files, unused
-        * @param string $session_name Session name
-        * @return bool Success
-        */
-       static function open( $save_path, $session_name ) {
-               return true;
-       }
-
-       /**
-        * Callback when closing a session.
-        * NOP.
-        *
-        * @return bool Success
-        */
-       static function close() {
-               return true;
-       }
-
-       /**
-        * Callback when reading session data.
-        *
-        * @param string $id Session id
-        * @return mixed Session data
-        */
-       static function read( $id ) {
-               $stime = microtime( true );
-               $data = self::getCache()->get( self::getKey( $id ) );
-               $real = microtime( true ) - $stime;
-
-               RequestContext::getMain()->getStats()->timing( "session.read", 1000 * $real );
-
-               self::$hashCache = array( $id => self::getHash( $data ) );
-
-               return ( $data === false ) ? '' : $data;
-       }
-
-       /**
-        * Callback when writing session data.
-        *
-        * @param string $id Session id
-        * @param string $data Session data
-        * @return bool Success
-        */
-       static function write( $id, $data ) {
-               global $wgObjectCacheSessionExpiry;
-
-               // Only issue a write if anything changed (PHP 5.6 already does this)
-               if ( !isset( self::$hashCache[$id] )
-                       || self::getHash( $data ) !== self::$hashCache[$id]
-               ) {
-                       $stime = microtime( true );
-                       self::getCache()->set( self::getKey( $id ), $data, $wgObjectCacheSessionExpiry );
-                       $real = microtime( true ) - $stime;
-
-                       RequestContext::getMain()->getStats()->timing( "session.write", 1000 * $real );
-               }
-
-               return true;
-       }
-
-       /**
-        * Callback to destroy a session when calling session_destroy().
-        *
-        * @param string $id Session id
-        * @return bool Success
-        */
-       static function destroy( $id ) {
-               $stime = microtime( true );
-               self::getCache()->delete( self::getKey( $id ) );
-               $real = microtime( true ) - $stime;
-
-               RequestContext::getMain()->getStats()->timing( "session.destroy", 1000 * $real );
-
-               return true;
-       }
-
-       /**
-        * Callback to execute garbage collection.
-        * NOP: Object caches perform garbage collection implicitly
-        *
-        * @param int $maxlifetime Maximum session life time
-        * @return bool Success
-        */
-       static function gc( $maxlifetime ) {
-               return true;
-       }
-
-       /**
-        * Shutdown function.
-        * See the comment inside ObjectCacheSessionHandler::install for rationale.
-        */
-       static function handleShutdown() {
-               session_write_close();
-       }
-
-       /**
-        * Pre-emptive session renewal function
-        */
-       static function renewCurrentSession() {
-               global $wgObjectCacheSessionExpiry;
-
-               // Once a session is at half TTL, renew it
-               $window = $wgObjectCacheSessionExpiry / 2;
-               $logger = LoggerFactory::getInstance( 'SessionHandler' );
-
-               $now = microtime( true );
-               // Session are only written in object stores when $_SESSION changes,
-               // which also renews the TTL ($wgObjectCacheSessionExpiry). If a user
-               // is active but not causing session data changes, it may suddenly
-               // expire as they view a form, blocking the first submission.
-               // Make a dummy change every so often to avoid this.
-               if ( !isset( $_SESSION['wsExpiresUnix'] ) ) {
-                       $_SESSION['wsExpiresUnix'] = $now + $wgObjectCacheSessionExpiry;
-
-                       $logger->info( "Set expiry for session " . session_id(), array() );
-               } elseif ( ( $now + $window ) > $_SESSION['wsExpiresUnix'] ) {
-                       $_SESSION['wsExpiresUnix'] = $now + $wgObjectCacheSessionExpiry;
-
-                       $logger->info( "Renewed session " . session_id(), array() );
-               }
-       }
-}
index af1f00b..f16158b 100644 (file)
@@ -1061,25 +1061,19 @@ class Article implements Page {
         * @return bool
         */
        public function showPatrolFooter() {
-               global $wgUseNPPatrol, $wgUseRCPatrol, $wgEnableAPI, $wgEnableWriteAPI;
+               global $wgUseNPPatrol, $wgUseRCPatrol, $wgUseFilePatrol, $wgEnableAPI, $wgEnableWriteAPI;
 
                $outputPage = $this->getContext()->getOutput();
                $user = $this->getContext()->getUser();
                $rc = false;
 
                if ( !$this->getTitle()->quickUserCan( 'patrol', $user )
-                       || !( $wgUseRCPatrol || $wgUseNPPatrol )
+                       || !( $wgUseRCPatrol || $wgUseNPPatrol || $wgUseFilePatrol )
                ) {
                        // Patrolling is disabled or the user isn't allowed to
                        return false;
                }
 
-               // New page patrol: Get the timestamp of the oldest revison which
-               // the revision table holds for the given page. Then we look
-               // whether it's within the RC lifespan and if it is, we try
-               // to get the recentchanges row belonging to that entry
-               // (with rc_new = 1).
-
                if ( $this->mRevision
                        && !RecentChange::isInRCLifespan( $this->mRevision->getTimestamp(), 21600 )
                ) {
@@ -1103,10 +1097,17 @@ class Article implements Page {
                        __METHOD__
                );
 
+               // New page patrol: Get the timestamp of the oldest revison which
+               // the revision table holds for the given page. Then we look
+               // whether it's within the RC lifespan and if it is, we try
+               // to get the recentchanges row belonging to that entry
+               // (with rc_new = 1).
+               $recentPageCreation = false;
                if ( $oldestRevisionTimestamp
                        && RecentChange::isInRCLifespan( $oldestRevisionTimestamp, 21600 )
                ) {
                        // 6h tolerance because the RC might not be cleaned out regularly
+                       $recentPageCreation = true;
                        $rc = RecentChange::newFromConds(
                                array(
                                        'rc_new' => 1,
@@ -1116,16 +1117,63 @@ class Article implements Page {
                                ),
                                __METHOD__
                        );
-               } else {
-                       // Cache the information we gathered above in case we can't patrol
-                       // Don't cache in case we can patrol as this could change
+                       if ( $rc ) {
+                               // Use generic patrol message for new pages
+                               $markPatrolledMsg = wfMessage( 'markaspatrolledtext' );
+                       }
+               }
+
+               // File patrol: Get the timestamp of the latest upload for this page,
+               // check whether it is within the RC lifespan and if it is, we try
+               // to get the recentchanges row belonging to that entry
+               // (with rc_type = RC_LOG, rc_log_type = upload).
+               $recentFileUpload = false;
+               if ( ( !$rc || $rc->getAttribute( 'rc_patrolled' ) ) && $wgUseFilePatrol
+                       && $this->getTitle()->getNamespace() === NS_FILE ) {
+                       // Retrieve timestamp of most recent upload
+                       $newestUploadTimestamp = $dbr->selectField(
+                               'image',
+                               'MAX( img_timestamp )',
+                               array( 'img_name' => $this->getTitle()->getDBkey() ),
+                               __METHOD__
+                       );
+                       if ( $newestUploadTimestamp
+                               && RecentChange::isInRCLifespan( $newestUploadTimestamp, 21600 )
+                       ) {
+                               // 6h tolerance because the RC might not be cleaned out regularly
+                               $recentFileUpload = true;
+                               $rc = RecentChange::newFromConds(
+                                       array(
+                                               'rc_type' => RC_LOG,
+                                               'rc_log_type' => 'upload',
+                                               'rc_timestamp' => $newestUploadTimestamp,
+                                               'rc_namespace' => NS_FILE,
+                                               'rc_cur_id' => $this->getTitle()->getArticleID()
+                                       ),
+                                       __METHOD__,
+                                       array( 'USE INDEX' => 'rc_timestamp' )
+                               );
+                               if ( $rc ) {
+                                       // Use patrol message specific to files
+                                       $markPatrolledMsg = wfMessage( 'markaspatrolledtext-file' );
+                               }
+                       }
+               }
+
+               if ( !$recentPageCreation && !$recentFileUpload ) {
+                       // Page creation and latest upload (for files) is too old to be in RC
+
+                       // We definitely can't patrol so cache the information
+                       // When a new file version is uploaded, the cache is cleared
                        $cache->set( $key, '1' );
+
+                       return false;
                }
 
                if ( !$rc ) {
                        // Don't cache: This can be hit if the page gets accessed very fast after
-                       // its creation or in case we have high slave lag. In case the revision is
-                       // too old, we will already return above.
+                       // its creation / latest upload or in case we have high slave lag. In case
+                       // the revision is too old, we will already return above.
                        return false;
                }
 
@@ -1140,7 +1188,7 @@ class Article implements Page {
                }
 
                if ( $rc->getPerformer()->equals( $user ) ) {
-                       // Don't show a patrol link for own creations. If the user could
+                       // Don't show a patrol link for own creations/uploads. If the user could
                        // patrol them, they already would be patrolled
                        return false;
                }
@@ -1156,7 +1204,7 @@ class Article implements Page {
 
                $link = Linker::linkKnown(
                        $this->getTitle(),
-                       wfMessage( 'markaspatrolledtext' )->escaped(),
+                       $markPatrolledMsg->escaped(),
                        array(),
                        array(
                                'action' => 'markpatrolled',
@@ -1174,6 +1222,17 @@ class Article implements Page {
                return true;
        }
 
+       /**
+        * Purge the cache used to check if it is worth showing the patrol footer
+        * For example, it is done during re-uploads when file patrol is used.
+        * @param int $articleID ID of the article to purge
+        * @since 1.27
+        */
+       public static function purgePatrolFooterCache( $articleID ) {
+               $cache = ObjectCache::getMainWANInstance();
+               $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
+       }
+
        /**
         * Show the error text for a missing article. For articles in the MediaWiki
         * namespace, show the default message text. To be called from Article::view().
index 8fb760d..6f4c296 100644 (file)
@@ -687,18 +687,6 @@ class WikiPage implements Page, IDBAccessObject {
                return false;
        }
 
-       /**
-        * Get the text of the current revision. No side-effects...
-        *
-        * @return string|bool The text of the current revision. False on failure
-        * @deprecated since 1.21, getContent() should be used instead.
-        */
-       public function getRawText() {
-               ContentHandler::deprecated( __METHOD__, '1.21' );
-
-               return $this->getText( Revision::RAW );
-       }
-
        /**
         * @return string MW timestamp of last article revision
         */
@@ -1158,6 +1146,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                return true;
        }
+
        /**
         * Insert a new empty page record for this article.
         * This *must* be followed up by creating a revision
@@ -1166,13 +1155,16 @@ class WikiPage implements Page, IDBAccessObject {
         * Best if all done inside a transaction.
         *
         * @param IDatabase $dbw
-        * @return int|bool The newly created page_id key; false if the title already existed
+        * @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
         */
-       public function insertOn( $dbw ) {
+       public function insertOn( $dbw, $pageId = null ) {
+               $pageIdForInsert = $pageId ?: $dbw->nextSequenceValue( 'page_page_id_seq' );
                $dbw->insert(
                        'page',
                        array(
-                               'page_id'           => $dbw->nextSequenceValue( 'page_page_id_seq' ),
+                               'page_id'           => $pageIdForInsert,
                                'page_namespace'    => $this->mTitle->getNamespace(),
                                'page_title'        => $this->mTitle->getDBkey(),
                                'page_restrictions' => '',
@@ -1188,7 +1180,7 @@ class WikiPage implements Page, IDBAccessObject {
                );
 
                if ( $dbw->affectedRows() > 0 ) {
-                       $newid = $dbw->insertId();
+                       $newid = $pageId ?: $dbw->insertId();
                        $this->mId = $newid;
                        $this->mTitle->resetArticleID( $newid );
 
@@ -3559,64 +3551,6 @@ class WikiPage implements Page, IDBAccessObject {
                }
        }
 
-       /**
-        * Return a list of templates used by this article.
-        * Uses the templatelinks table
-        *
-        * @deprecated since 1.19; use Title::getTemplateLinksFrom()
-        * @return array Array of Title objects
-        */
-       public function getUsedTemplates() {
-               return $this->mTitle->getTemplateLinksFrom();
-       }
-
-       /**
-        * This function is called right before saving the wikitext,
-        * so we can do things like signatures and links-in-context.
-        *
-        * @deprecated since 1.19; use Parser::preSaveTransform() instead
-        * @param string $text Article contents
-        * @param User $user User doing the edit
-        * @param ParserOptions $popts Parser options, default options for
-        *   the user loaded if null given
-        * @return string Article contents with altered wikitext markup (signatures
-        *      converted, {{subst:}}, templates, etc.)
-        */
-       public function preSaveTransform( $text, User $user = null, ParserOptions $popts = null ) {
-               global $wgParser, $wgUser;
-
-               wfDeprecated( __METHOD__, '1.19' );
-
-               $user = is_null( $user ) ? $wgUser : $user;
-
-               if ( $popts === null ) {
-                       $popts = ParserOptions::newFromUser( $user );
-               }
-
-               return $wgParser->preSaveTransform( $text, $this->mTitle, $user, $popts );
-       }
-
-       /**
-        * Update the article's restriction field, and leave a log entry.
-        *
-        * @deprecated since 1.19
-        * @param array $limit Set of restriction keys
-        * @param string $reason
-        * @param int &$cascade Set to false if cascading protection isn't allowed.
-        * @param array $expiry Per restriction type expiration
-        * @param User $user The user updating the restrictions
-        * @return bool True on success
-        */
-       public function updateRestrictions(
-               $limit = array(), $reason = '', &$cascade = 0, $expiry = array(), User $user = null
-       ) {
-               global $wgUser;
-
-               $user = is_null( $user ) ? $wgUser : $user;
-
-               return $this->doUpdateRestrictions( $limit, $expiry, $cascade, $reason, $user )->isOK();
-       }
-
        /**
         * Returns a list of updates to be performed when this page is deleted. The
         * updates should remove any information about this page from secondary data
index d25d11a..91b6080 100644 (file)
@@ -813,7 +813,7 @@ class CoreParserFunctions {
                        $titleObject = $parser->mTitle;
                }
                if ( $titleObject->areRestrictionsLoaded() || $parser->incrementExpensiveFunctionCount() ) {
-                       $expiry = $parser->mTitle->getRestrictionExpiry( strtolower( $type ) );
+                       $expiry = $titleObject->getRestrictionExpiry( strtolower( $type ) );
                        // getRestrictionExpiry() returns false on invalid type; trying to
                        // match protectionlevel() function that returns empty string instead
                        if ( $expiry === false ) {
index cfbf0b4..7b4a650 100644 (file)
@@ -3752,7 +3752,6 @@ class Parser {
        public function callParserFunction( $frame, $function, array $args = array() ) {
                global $wgContLang;
 
-
                # Case sensitive functions
                if ( isset( $this->mFunctionSynonyms[1][$function] ) ) {
                        $function = $this->mFunctionSynonyms[1][$function];
@@ -5277,9 +5276,8 @@ class Parser {
        }
 
        /**
-        * @todo FIXME: Update documentation. makeLinkObj() is deprecated.
         * Replace "<!--LINK-->" link placeholders with actual links, in the buffer
-        * Placeholders created in Skin::makeLinkObj()
+        * Placeholders created in Linker::link()
         *
         * @param string $text
         * @param int $options
index e4c287a..72668bf 100644 (file)
@@ -855,64 +855,6 @@ class ParserOutput extends CacheTime {
                $this->mAccessedOptions[$option] = true;
        }
 
-       /**
-        * @deprecated since 1.25. Instead, store any relevant data using setExtensionData,
-        *    and implement Content::getSecondaryDataUpdates() if possible, or use the
-        *    'SecondaryDataUpdates' hook to construct the necessary update objects.
-        *
-        * @note Hard deprecation and removal without long deprecation period, since there are no
-        *       known users, but known conceptual issues.
-        *
-        * @todo remove in 1.26
-        *
-        * @param DataUpdate $update
-        *
-        * @throws MWException
-        */
-       public function addSecondaryDataUpdate( DataUpdate $update ) {
-               wfDeprecated( __METHOD__, '1.25' );
-               throw new MWException(
-                       'ParserOutput::addSecondaryDataUpdate() is no longer supported. ' .
-                               'Override Content::getSecondaryDataUpdates() ' .
-                               'or use the SecondaryDataUpdates hook instead.'
-               );
-       }
-
-       /**
-        * @deprecated since 1.25.
-        *
-        * @note Hard deprecation and removal without long deprecation period, since there are no
-        *       known users, but known conceptual issues.
-        *
-        * @todo remove in 1.26
-        *
-        * @return bool false (since 1.25)
-        */
-       public function hasCustomDataUpdates() {
-               wfDeprecated( __METHOD__, '1.25' );
-               return false;
-       }
-
-       /**
-        * @deprecated since 1.25. Instead, store any relevant data using setExtensionData,
-        *    and implement Content::getSecondaryDataUpdates() if possible, or use the
-        *    'SecondaryDataUpdates' hook to construct the necessary update objects.
-        *
-        * @note Hard deprecation and removal without long deprecation period, since there are no
-        *       known users, but known conceptual issues.
-        *
-        * @todo remove in 1.26
-        *
-        * @param Title $title
-        * @param bool $recursive
-        *
-        * @return array An array of instances of DataUpdate
-        */
-       public function getSecondaryDataUpdates( Title $title = null, $recursive = true ) {
-               wfDeprecated( __METHOD__, '1.25' );
-               return array();
-       }
-
        /**
         * Attaches arbitrary data to this ParserObject. This can be used to store some information in
         * the ParserOutput object for later use during page output. The data will be cached along with
index 4ca3a87..817f153 100644 (file)
@@ -237,6 +237,8 @@ class Preprocessor_DOM extends Preprocessor {
                $inHeading = false;
                // True if there are no more greater-than (>) signs right of $i
                $noMoreGT = false;
+               // Map of tag name => true if there are no more closing tags of given type right of $i
+               $noMoreClosingTag = array();
                // True to ignore all input up to the next <onlyinclude>
                $findOnlyinclude = $enableOnlyinclude;
                // Do a line-start run without outputting an LF character
@@ -457,17 +459,21 @@ class Preprocessor_DOM extends Preprocessor {
                                } else {
                                        $attrEnd = $tagEndPos;
                                        // Find closing tag
-                                       if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
+                                       if (
+                                               !isset( $noMoreClosingTag[$name] ) &&
+                                               preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
                                                        $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
                                        ) {
                                                $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
                                                $i = $matches[0][1] + strlen( $matches[0][0] );
                                                $close = '<close>' . htmlspecialchars( $matches[0][0] ) . '</close>';
                                        } else {
-                                               // No end tag -- let it run out to the end of the text.
-                                               $inner = substr( $text, $tagEndPos + 1 );
-                                               $i = $lengthText;
-                                               $close = '';
+                                               // No end tag -- don't match the tag, treat opening tag as literal and resume parsing.
+                                               $i = $tagEndPos + 1;
+                                               $accum .= htmlspecialchars( 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;
                                        }
                                }
                                // <includeonly> and <noinclude> just become <ignore> tags
index 14292a5..28c49fd 100644 (file)
@@ -88,7 +88,6 @@ class Preprocessor_Hash extends Preprocessor {
                return $node;
        }
 
-
        /**
         * Preprocess some wikitext and return the document tree.
         * This is the ghost of Parser::replace_variables().
@@ -161,6 +160,8 @@ class Preprocessor_Hash extends Preprocessor {
                $inHeading = false;
                // True if there are no more greater-than (>) signs right of $i
                $noMoreGT = false;
+               // Map of tag name => true if there are no more closing tags of given type right of $i
+               $noMoreClosingTag = array();
                // True to ignore all input up to the next <onlyinclude>
                $findOnlyinclude = $enableOnlyinclude;
                // Do a line-start run without outputting an LF character
@@ -381,17 +382,21 @@ class Preprocessor_Hash extends Preprocessor {
                                } else {
                                        $attrEnd = $tagEndPos;
                                        // Find closing tag
-                                       if ( preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
+                                       if (
+                                               !isset( $noMoreClosingTag[$name] ) &&
+                                               preg_match( "/<\/" . preg_quote( $name, '/' ) . "\s*>/i",
                                                        $text, $matches, PREG_OFFSET_CAPTURE, $tagEndPos + 1 )
                                        ) {
                                                $inner = substr( $text, $tagEndPos + 1, $matches[0][1] - $tagEndPos - 1 );
                                                $i = $matches[0][1] + strlen( $matches[0][0] );
                                                $close = $matches[0][0];
                                        } else {
-                                               // No end tag -- let it run out to the end of the text.
-                                               $inner = substr( $text, $tagEndPos + 1 );
-                                               $i = $lengthText;
-                                               $close = null;
+                                               // No end tag -- don't match the tag, treat opening tag as literal and resume parsing.
+                                               $i = $tagEndPos + 1;
+                                               $accum->addLiteral( 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;
                                        }
                                }
                                // <includeonly> and <noinclude> just become <ignore> tags
index 30be343..0efcebf 100644 (file)
@@ -123,10 +123,10 @@ class IRCColourfulRCFeedFormatter implements RCFeedFormatter {
         * @return string
         */
        public static function cleanupForIRC( $text ) {
-               return Sanitizer::decodeCharReferences( str_replace(
+               return str_replace(
                        array( "\n", "\r" ),
                        array( " ", "" ),
-                       $text
-               ) );
+                       Sanitizer::decodeCharReferences( $text )
+               );
        }
 }
index 84e873d..6ac25e8 100644 (file)
@@ -23,6 +23,7 @@ class ExtensionProcessor implements Processor {
                'AvailableRights',
                'ContentHandlers',
                'ConfigRegistry',
+               'CentralIdLookupProviders',
                'RateLimits',
                'RecentChangesFlags',
                'MediaHandlers',
@@ -44,6 +45,7 @@ class ExtensionProcessor implements Processor {
                'APIPropModules',
                'APIListModules',
                'ValidSkinNames',
+               'FeedClasses',
        );
 
        /**
@@ -57,13 +59,13 @@ class ExtensionProcessor implements Processor {
                'wgGroupPermissions' => 'array_plus_2d',
                'wgRevokePermissions' => 'array_plus_2d',
                'wgHooks' => 'array_merge_recursive',
-               // credits are handled in the ExtensionRegistry
-               // 'wgExtensionCredits' => 'array_merge_recursive',
+               'wgExtensionCredits' => 'array_merge_recursive',
                'wgExtraGenderNamespaces' => 'array_plus',
                'wgNamespacesWithSubpages' => 'array_plus',
                'wgNamespaceContentModels' => 'array_plus',
                'wgNamespaceProtection' => 'array_plus',
                'wgCapitalLinkOverrides' => 'array_plus',
+               'wgRateLimits' => 'array_plus_2d',
        );
 
        /**
@@ -102,6 +104,7 @@ class ExtensionProcessor implements Processor {
                'ParserTestFiles',
                'AutoloadClasses',
                'manifest_version',
+               'load_composer_autoloader',
        );
 
        /**
@@ -293,6 +296,11 @@ class ExtensionProcessor implements Processor {
                }
        }
 
+       /**
+        * @param string $path
+        * @param array $info
+        * @throws Exception
+        */
        protected function extractCredits( $path, array $info ) {
                $credits = array(
                        'path' => $path,
@@ -304,7 +312,18 @@ class ExtensionProcessor implements Processor {
                        }
                }
 
-               $this->credits[$credits['name']] = $credits;
+               $name = $credits['name'];
+
+               // If someone is loading the same thing twice, throw
+               // a nice error (T121493)
+               if ( isset( $this->credits[$name] ) ) {
+                       $firstPath = $this->credits[$name]['path'];
+                       $secondPath = $credits['path'];
+                       throw new Exception( "It was attempted to load $name twice, from $firstPath and $secondPath." );
+               }
+
+               $this->credits[$name] = $credits;
+               $this->globals['wgExtensionCredits'][$credits['type']][] = $credits;
        }
 
        /**
@@ -353,4 +372,15 @@ class ExtensionProcessor implements Processor {
                        $array[$name] = $value;
                }
        }
+
+       public function getExtraAutoloaderPaths( $dir, array $info ) {
+               $paths = array();
+               if ( isset( $info['load_composer_autoloader'] ) && $info['load_composer_autoloader'] === true ) {
+                       $path = "$dir/vendor/autoload.php";
+                       if ( file_exists( $path ) ) {
+                               $paths[] = $path;
+                       }
+               }
+               return $paths;
+       }
 }
index 732b4a0..86be86b 100644 (file)
@@ -29,7 +29,7 @@ class ExtensionRegistry {
        /**
         * Bump whenever the registration cache needs resetting
         */
-       const CACHE_VERSION = 1;
+       const CACHE_VERSION = 3;
 
        /**
         * Special key that defines the merge strategy
@@ -173,6 +173,7 @@ class ExtensionRegistry {
        public function readFromQueue( array $queue ) {
                global $wgVersion;
                $autoloadClasses = array();
+               $autoloaderPaths = array();
                $processor = new ExtensionProcessor();
                $incompatible = array();
                $coreVersionParser = new CoreVersionChecker( $wgVersion );
@@ -208,6 +209,9 @@ class ExtensionRegistry {
                                        . '.';
                                continue;
                        }
+                       // Get extra paths for later inclusion
+                       $autoloaderPaths = array_merge( $autoloaderPaths,
+                               $processor->getExtraAutoloaderPaths( dirname( $path ), $info ) );
                        // Compatible, read and extract info
                        $processor->extractInfo( $path, $info, $version );
                }
@@ -221,11 +225,8 @@ class ExtensionRegistry {
                $data = $processor->getExtractedInfo();
                // Need to set this so we can += to it later
                $data['globals']['wgAutoloadClasses'] = array();
-               foreach ( $data['credits'] as $credit ) {
-                       $data['globals']['wgExtensionCredits'][$credit['type']][] = $credit;
-               }
-               $data['globals']['wgExtensionCredits'][self::MERGE_STRATEGY] = 'array_merge_recursive';
                $data['autoload'] = $autoloadClasses;
+               $data['autoloaderPaths'] = $autoloaderPaths;
                return $data;
        }
 
@@ -279,8 +280,11 @@ class ExtensionRegistry {
                        call_user_func( $cb );
                }
 
-               $this->loaded += $info['credits'];
+               foreach ( $info['autoloaderPaths'] as $path ) {
+                       require_once $path;
+               }
 
+               $this->loaded += $info['credits'];
                if ( $info['attributes'] ) {
                        if ( !$this->attributes ) {
                                $this->attributes = $info['attributes'];
index e5669d2..a4100bb 100644 (file)
@@ -40,4 +40,14 @@ interface Processor {
         *              like 'MediaWiki'. Values are a constraint string like "1.26.1".
         */
        public function getRequirements( array $info );
+
+       /**
+        * Get the path for additional autoloaders, e.g. the one of Composer.
+        *
+        * @param string $dir
+        * @param array $info
+        * @return array Containing the paths for autoloader file(s).
+        * @since 1.27
+        */
+       public function getExtraAutoloaderPaths( $dir, array $info );
 }
index 1f3085a..74ee6ab 100644 (file)
@@ -41,7 +41,7 @@ class ResourceLoader implements LoggerAwareInterface {
        protected static $debugMode = null;
 
        /** @var array */
-       private static $lessVars = null;
+       private $lessVars = null;
 
        /**
         * Module name/ResourceLoaderModule object pairs
@@ -1589,15 +1589,13 @@ MESSAGE;
        /**
         * Returns LESS compiler set up for use with MediaWiki
         *
-        * @since 1.22
-        * @since 1.27 added $extraVars parameter
-        * @param Config $config
+        * @since 1.27
         * @param array $extraVars Associative array of extra (i.e., other than the
         *   globally-configured ones) that should be used for compilation.
         * @throws MWException
         * @return Less_Parser
         */
-       public static function getLessCompiler( Config $config, $extraVars = array() ) {
+       public function getLessCompiler( $extraVars = array() ) {
                // When called from the installer, it is possible that a required PHP extension
                // is missing (at least for now; see bug 47564). If this is the case, throw an
                // exception (caught by the installer) to prevent a fatal error later on.
@@ -1606,10 +1604,12 @@ MESSAGE;
                }
 
                $parser = new Less_Parser;
-               $parser->ModifyVars( array_merge( self::getLessVars( $config ), $extraVars ) );
-               $parser->SetImportDirs( array_fill_keys( $config->get( 'ResourceLoaderLESSImportPaths' ), '' ) );
+               $parser->ModifyVars( array_merge( $this->getLessVars(), $extraVars ) );
+               $parser->SetImportDirs(
+                       array_fill_keys( $this->config->get( 'ResourceLoaderLESSImportPaths' ), '' )
+               );
                $parser->SetOption( 'relativeUrls', false );
-               $parser->SetCacheDir( $config->get( 'CacheDirectory' ) ?: wfTempDir() );
+               $parser->SetCacheDir( $this->config->get( 'CacheDirectory' ) ?: wfTempDir() );
 
                return $parser;
        }
@@ -1617,16 +1617,15 @@ MESSAGE;
        /**
         * Get global LESS variables.
         *
-        * @param Config $config
-        * @since 1.22
+        * @since 1.27
         * @return array Map of variable names to string CSS values.
         */
-       public static function getLessVars( Config $config ) {
-               if ( !self::$lessVars ) {
-                       $lessVars = $config->get( 'ResourceLoaderLESSVars' );
+       public function getLessVars() {
+               if ( !$this->lessVars ) {
+                       $lessVars = $this->config->get( 'ResourceLoaderLESSVars' );
                        Hooks::run( 'ResourceLoaderGetLessVars', array( &$lessVars ) );
-                       self::$lessVars = $lessVars;
+                       $this->lessVars = $lessVars;
                }
-               return self::$lessVars;
+               return $this->lessVars;
        }
 }
index f5b3bad..98269ae 100644 (file)
@@ -957,7 +957,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                        }
                }
 
-               $compiler = ResourceLoader::getLessCompiler( $this->getConfig(), $vars );
+               $compiler = $context->getResourceLoader()->getLessCompiler( $vars );
                $css = $compiler->parseFile( $fileName )->getCss();
                $files = $compiler->AllParsedFiles();
                $this->localFileRefs = array_merge( $this->localFileRefs, $files );
index fc128fb..4a68f8e 100644 (file)
@@ -178,7 +178,6 @@ class ResourceLoaderStartUpModule extends ResourceLoaderModule {
                }
        }
 
-
        /**
         * Get registration code for all modules.
         *
index 6705336..04b0434 100644 (file)
@@ -50,14 +50,10 @@ class ResourceLoaderUserCSSPrefsModule extends ResourceLoaderModule {
                // Build CSS rules
                $rules = array();
 
-               // Underline: 2 = browser default, 1 = always, 0 = never
+               // Underline: 2 = skin default, 1 = always, 0 = never
                if ( $options['underline'] < 2 ) {
                        $rules[] = "a { text-decoration: " .
                                ( $options['underline'] ? 'underline' : 'none' ) . "; }";
-               } else {
-                       # The scripts of these languages are very hard to read with underlines
-                       $rules[] = 'a:lang(ar), a:lang(kk-arab), a:lang(mzn), ' .
-                       'a:lang(ps), a:lang(ur) { text-decoration: none; }';
                }
                if ( $options['editfont'] !== 'default' ) {
                        // Double-check that $options['editfont'] consists of safe characters only
@@ -72,6 +68,15 @@ class ResourceLoaderUserCSSPrefsModule extends ResourceLoaderModule {
                return array( 'all' => $style );
        }
 
+       /**
+        * @param ResourceLoaderContext $context
+        * @return bool
+        */
+       public function isKnownEmpty( ResourceLoaderContext $context ) {
+               $styles = $this->getStyles( $context );
+               return isset( $styles['all'] ) && $styles['all'] === '';
+       }
+
        /**
         * @return string
         */
index e5ed23f..3c8d56e 100644 (file)
@@ -164,10 +164,10 @@ class SearchEngine {
                $allSearchTerms = array( $searchterm );
 
                if ( $wgContLang->hasVariants() ) {
-                       $allSearchTerms = array_merge(
+                       $allSearchTerms = array_unique( array_merge(
                                $allSearchTerms,
                                $wgContLang->autoConvertToAllVariants( $searchterm )
-                       );
+                       ) );
                }
 
                $titleResult = null;
diff --git a/includes/search/SearchNearMatchResultSet.php b/includes/search/SearchNearMatchResultSet.php
new file mode 100644 (file)
index 0000000..cb4f81d
--- /dev/null
@@ -0,0 +1,26 @@
+<?php
+/**
+ * A SearchResultSet wrapper for SearchEngine::getNearMatch
+ */
+class SearchNearMatchResultSet extends SearchResultSet {
+       private $fetched = false;
+
+       /**
+        * @param Title|null $match Title if matched, else null
+        */
+       public function __construct( $match ) {
+               $this->result = $match;
+       }
+
+       public function numRows() {
+               return $this->result ? 1 : 0;
+       }
+
+       public function next() {
+               if ( $this->fetched || !$this->result ) {
+                       return false;
+               }
+               $this->fetched = true;
+               return SearchResult::newFromTitle( $this->result );
+       }
+}
index 8fb04e4..eccd36e 100644 (file)
@@ -171,89 +171,3 @@ class SearchResultSet {
                return $this->containedSyntax;
        }
 }
-
-/**
- * This class is used for different SQL-based search engines shipped with MediaWiki
- * @ingroup Search
- */
-class SqlSearchResultSet extends SearchResultSet {
-       protected $resultSet;
-       protected $terms;
-       protected $totalHits;
-
-       function __construct( $resultSet, $terms, $total = null ) {
-               $this->resultSet = $resultSet;
-               $this->terms = $terms;
-               $this->totalHits = $total;
-       }
-
-       function termMatches() {
-               return $this->terms;
-       }
-
-       function numRows() {
-               if ( $this->resultSet === false ) {
-                       return false;
-               }
-
-               return $this->resultSet->numRows();
-       }
-
-       function next() {
-               if ( $this->resultSet === false ) {
-                       return false;
-               }
-
-               $row = $this->resultSet->fetchObject();
-               if ( $row === false ) {
-                       return false;
-               }
-
-               return SearchResult::newFromTitle(
-                       Title::makeTitle( $row->page_namespace, $row->page_title )
-               );
-       }
-
-       function free() {
-               if ( $this->resultSet === false ) {
-                       return false;
-               }
-
-               $this->resultSet->free();
-       }
-
-       function getTotalHits() {
-               if ( !is_null( $this->totalHits ) ) {
-                       return $this->totalHits;
-               } else {
-                       // Special:Search expects a number here.
-                       return $this->numRows();
-               }
-       }
-}
-
-/**
- * A SearchResultSet wrapper for SearchEngine::getNearMatch
- */
-class SearchNearMatchResultSet extends SearchResultSet {
-       private $fetched = false;
-
-       /**
-        * @param Title|null $match Title if matched, else null
-        */
-       public function __construct( $match ) {
-               $this->result = $match;
-       }
-
-       public function numRows() {
-               return $this->result ? 1 : 0;
-       }
-
-       public function next() {
-               if ( $this->fetched || !$this->result ) {
-                       return false;
-               }
-               $this->fetched = true;
-               return SearchResult::newFromTitle( $this->result );
-       }
-}
diff --git a/includes/search/SqlSearchResultSet.php b/includes/search/SqlSearchResultSet.php
new file mode 100644 (file)
index 0000000..7a6aaf7
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+/**
+ * This class is used for different SQL-based search engines shipped with MediaWiki
+ * @ingroup Search
+ */
+class SqlSearchResultSet extends SearchResultSet {
+       protected $resultSet;
+       protected $terms;
+       protected $totalHits;
+
+       function __construct( $resultSet, $terms, $total = null ) {
+               $this->resultSet = $resultSet;
+               $this->terms = $terms;
+               $this->totalHits = $total;
+       }
+
+       function termMatches() {
+               return $this->terms;
+       }
+
+       function numRows() {
+               if ( $this->resultSet === false ) {
+                       return false;
+               }
+
+               return $this->resultSet->numRows();
+       }
+
+       function next() {
+               if ( $this->resultSet === false ) {
+                       return false;
+               }
+
+               $row = $this->resultSet->fetchObject();
+               if ( $row === false ) {
+                       return false;
+               }
+
+               return SearchResult::newFromTitle(
+                       Title::makeTitle( $row->page_namespace, $row->page_title )
+               );
+       }
+
+       function free() {
+               if ( $this->resultSet === false ) {
+                       return false;
+               }
+
+               $this->resultSet->free();
+       }
+
+       function getTotalHits() {
+               if ( !is_null( $this->totalHits ) ) {
+                       return $this->totalHits;
+               } else {
+                       // Special:Search expects a number here.
+                       return $this->numRows();
+               }
+       }
+}
diff --git a/includes/session/BotPasswordSessionProvider.php b/includes/session/BotPasswordSessionProvider.php
new file mode 100644 (file)
index 0000000..d9c60c7
--- /dev/null
@@ -0,0 +1,167 @@
+<?php
+/**
+ * Session provider for bot passwords
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use BotPassword;
+use User;
+use WebRequest;
+
+/**
+ * Session provider for bot passwords
+ * @since 1.27
+ */
+class BotPasswordSessionProvider extends ImmutableSessionProviderWithCookie {
+
+       /**
+        * @param array $params Keys include:
+        *  - priority: (required) Set the priority
+        *  - sessionCookieName: Session cookie name. Default is '_BPsession'.
+        *  - sessionCookieOptions: Options to pass to WebResponse::setCookie().
+        */
+       public function __construct( array $params = array() ) {
+               if ( !isset( $params['sessionCookieName'] ) ) {
+                       $params['sessionCookieName'] = '_BPsession';
+               }
+               parent::__construct( $params );
+
+               if ( !isset( $params['priority'] ) ) {
+                       throw new \InvalidArgumentException( __METHOD__ . ': priority must be specified' );
+               }
+               if ( $params['priority'] < SessionInfo::MIN_PRIORITY ||
+                       $params['priority'] > SessionInfo::MAX_PRIORITY
+               ) {
+                       throw new \InvalidArgumentException( __METHOD__ . ': Invalid priority' );
+               }
+
+               $this->priority = $params['priority'];
+       }
+
+       public function provideSessionInfo( WebRequest $request ) {
+               // Only relevant for the API
+               if ( !defined( 'MW_API' ) ) {
+                       return null;
+               }
+
+               // Enabled?
+               if ( !$this->config->get( 'EnableBotPasswords' ) ) {
+                       return null;
+               }
+
+               // Have a session ID?
+               $id = $this->getSessionIdFromCookie( $request );
+               if ( $id === null ) {
+                       return null;
+               }
+
+               return new SessionInfo( $this->priority, array(
+                       'provider' => $this,
+                       'id' => $id,
+                       'persisted' => true
+               ) );
+       }
+
+       public function newSessionInfo( $id = null ) {
+               // We don't activate by default
+               return null;
+       }
+
+       /**
+        * Create a new session for a request
+        * @param User $user
+        * @param BotPassword $bp
+        * @param WebRequest $request
+        * @return Session
+        */
+       public function newSessionForRequest( User $user, BotPassword $bp, WebRequest $request ) {
+               $id = $this->getSessionIdFromCookie( $request );
+               $info = new SessionInfo( SessionInfo::MAX_PRIORITY, array(
+                       'provider' => $this,
+                       'id' => $id,
+                       'userInfo' => UserInfo::newFromUser( $user, true ),
+                       'persisted' => $id !== null,
+                       'metadata' => array(
+                               'centralId' => $bp->getUserCentralId(),
+                               'appId' => $bp->getAppId(),
+                               'token' => $bp->getToken(),
+                               'rights' => \MWGrants::getGrantRights( $bp->getGrants() ),
+                       ),
+               ) );
+               $session = $this->getManager()->getSessionFromInfo( $info, $request );
+               $session->persist();
+               return $session;
+       }
+
+       public function refreshSessionInfo( SessionInfo $info, WebRequest $request, &$metadata ) {
+               $missingKeys = array_diff(
+                       array( 'centralId', 'appId', 'token' ),
+                       array_keys( $metadata )
+               );
+               if ( $missingKeys ) {
+                       $this->logger->info( "Session $info: Missing metadata: " . join( ', ', $missingKeys ) );
+                       return false;
+               }
+
+               $bp = BotPassword::newFromCentralId( $metadata['centralId'], $metadata['appId'] );
+               if ( !$bp ) {
+                       $this->logger->info(
+                               "Session $info: No BotPassword for {$metadata['centralId']} {$metadata['appId']}"
+                       );
+                       return false;
+               }
+
+               if ( !hash_equals( $metadata['token'], $bp->getToken() ) ) {
+                       $this->logger->info( "Session $info: BotPassword token check failed" );
+                       return false;
+               }
+
+               $status = $bp->getRestrictions()->check( $request );
+               if ( !$status->isOk() ) {
+                       $this->logger->info( "Session $info: Restrictions check failed", $status->getValue() );
+                       return false;
+               }
+
+               // Update saved rights
+               $metadata['rights'] = \MWGrants::getGrantRights( $bp->getGrants() );
+
+               return true;
+       }
+
+       public function preventSessionsForUser( $username ) {
+               BotPassword::removeAllPasswordsForUser( $username );
+       }
+
+       public function getAllowedUserRights( SessionBackend $backend ) {
+               if ( $backend->getProvider() !== $this ) {
+                       throw new InvalidArgumentException( 'Backend\'s provider isn\'t $this' );
+               }
+               $data = $backend->getProviderMetadata();
+               if ( $data ) {
+                       return $data['rights'];
+               }
+
+               // Should never happen
+               $this->logger->debug( __METHOD__ . ': No provider metadata, returning no rights allowed' );
+               return array();
+       }
+}
diff --git a/includes/session/CookieSessionProvider.php b/includes/session/CookieSessionProvider.php
new file mode 100644 (file)
index 0000000..f92a519
--- /dev/null
@@ -0,0 +1,324 @@
+<?php
+/**
+ * MediaWiki cookie-based session provider interface
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use Config;
+use User;
+use WebRequest;
+
+/**
+ * A CookieSessionProvider persists sessions using cookies
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+class CookieSessionProvider extends SessionProvider {
+
+       protected $params = array();
+       protected $cookieOptions = array();
+
+       /**
+        * @param array $params Keys include:
+        *  - priority: (required) Priority of the returned sessions
+        *  - callUserSetCookiesHook: Whether to call the deprecated hook
+        *  - sessionName: Session cookie name. Doesn't honor 'prefix'. Defaults to
+        *    $wgSessionName, or $wgCookiePrefix . '_session' if that is unset.
+        *  - cookieOptions: Options to pass to WebRequest::setCookie():
+        *    - prefix: Cookie prefix, defaults to $wgCookiePrefix
+        *    - path: Cookie path, defaults to $wgCookiePath
+        *    - domain: Cookie domain, defaults to $wgCookieDomain
+        *    - secure: Cookie secure flag, defaults to $wgCookieSecure
+        *    - httpOnly: Cookie httpOnly flag, defaults to $wgCookieHttpOnly
+        */
+       public function __construct( $params = array() ) {
+               parent::__construct();
+
+               $params += array(
+                       'cookieOptions' => array(),
+                       // @codeCoverageIgnoreStart
+               );
+               // @codeCoverageIgnoreEnd
+
+               if ( !isset( $params['priority'] ) ) {
+                       throw new \InvalidArgumentException( __METHOD__ . ': priority must be specified' );
+               }
+               if ( $params['priority'] < SessionInfo::MIN_PRIORITY ||
+                       $params['priority'] > SessionInfo::MAX_PRIORITY
+               ) {
+                       throw new \InvalidArgumentException( __METHOD__ . ': Invalid priority' );
+               }
+
+               if ( !is_array( $params['cookieOptions'] ) ) {
+                       throw new \InvalidArgumentException( __METHOD__ . ': cookieOptions must be an array' );
+               }
+
+               $this->priority = $params['priority'];
+               $this->cookieOptions = $params['cookieOptions'];
+               $this->params = $params;
+               unset( $this->params['priority'] );
+               unset( $this->params['cookieOptions'] );
+       }
+
+       public function setConfig( Config $config ) {
+               parent::setConfig( $config );
+
+               // @codeCoverageIgnoreStart
+               $this->params += array(
+                       // @codeCoverageIgnoreEnd
+                       'callUserSetCookiesHook' => false,
+                       'sessionName' =>
+                               $config->get( 'SessionName' ) ?: $config->get( 'CookiePrefix' ) . '_session',
+               );
+
+               // @codeCoverageIgnoreStart
+               $this->cookieOptions += array(
+                       // @codeCoverageIgnoreEnd
+                       'prefix' => $config->get( 'CookiePrefix' ),
+                       'path' => $config->get( 'CookiePath' ),
+                       'domain' => $config->get( 'CookieDomain' ),
+                       'secure' => $config->get( 'CookieSecure' ),
+                       'httpOnly' => $config->get( 'CookieHttpOnly' ),
+               );
+       }
+
+       public function provideSessionInfo( WebRequest $request ) {
+               $info = array(
+                       'id' => $request->getCookie( $this->params['sessionName'], '' )
+               );
+               if ( !SessionManager::validateSessionId( $info['id'] ) ) {
+                       unset( $info['id'] );
+               }
+
+               list( $userId, $userName, $token ) = $this->getUserInfoFromCookies( $request );
+               if ( $userId !== null ) {
+                       try {
+                               $userInfo = UserInfo::newFromId( $userId );
+                       } catch ( \InvalidArgumentException $ex ) {
+                               return null;
+                       }
+
+                       // Sanity check
+                       if ( $userName !== null && $userInfo->getName() !== $userName ) {
+                               return null;
+                       }
+
+                       if ( $token !== null ) {
+                               if ( !hash_equals( $userInfo->getToken(), $token ) ) {
+                                       return null;
+                               }
+                               $info['userInfo'] = $userInfo->verified();
+                       } elseif ( isset( $info['id'] ) ) { // No point if no session ID
+                               $info['userInfo'] = $userInfo;
+                       }
+               }
+
+               if ( !$info ) {
+                       return null;
+               }
+
+               $info += array(
+                       'provider' => $this,
+                       'persisted' => isset( $info['id'] ),
+                       'forceHTTPS' => $request->getCookie( 'forceHTTPS', '', false )
+               );
+
+               return new SessionInfo( $this->priority, $info );
+       }
+
+       public function persistsSessionId() {
+               return true;
+       }
+
+       public function canChangeUser() {
+               return true;
+       }
+
+       public function persistSession( SessionBackend $session, WebRequest $request ) {
+               $response = $request->response();
+               if ( $response->headersSent() ) {
+                       // Can't do anything now
+                       $this->logger->debug( __METHOD__ . ': Headers already sent' );
+                       return;
+               }
+
+               $user = $session->getUser();
+
+               $cookies = $this->cookieDataToExport( $user, $session->shouldRememberUser() );
+               $sessionData = $this->sessionDataToExport( $user );
+
+               // Legacy hook
+               if ( $this->params['callUserSetCookiesHook'] && !$user->isAnon() ) {
+                       \Hooks::run( 'UserSetCookies', array( $user, &$sessionData, &$cookies ) );
+               }
+
+               $options = $this->cookieOptions;
+               if ( $session->shouldForceHTTPS() || $user->requiresHTTPS() ) {
+                       $response->setCookie( 'forceHTTPS', 'true', $session->shouldRememberUser() ? 0 : null,
+                               array( 'prefix' => '', 'secure' => false ) + $options );
+                       $options['secure'] = true;
+               }
+
+               $response->setCookie( $this->params['sessionName'], $session->getId(), null,
+                       array( 'prefix' => '' ) + $options
+               );
+
+               $extendedCookies = $this->config->get( 'ExtendedLoginCookies' );
+               $extendedExpiry = $this->config->get( 'ExtendedLoginCookieExpiration' );
+
+               foreach ( $cookies as $key => $value ) {
+                       if ( $value === false ) {
+                               $response->clearCookie( $key, $options );
+                       } else {
+                               if ( $extendedExpiry !== null && in_array( $key, $extendedCookies ) ) {
+                                       $expiry = time() + (int)$extendedExpiry;
+                               } else {
+                                       $expiry = 0; // Default cookie expiration
+                               }
+                               $response->setCookie( $key, (string)$value, $expiry, $options );
+                       }
+               }
+
+               $this->setLoggedOutCookie( $session->getLoggedOutTimestamp(), $request );
+
+               if ( $sessionData ) {
+                       $session->addData( $sessionData );
+               }
+       }
+
+       public function unpersistSession( WebRequest $request ) {
+               $response = $request->response();
+               if ( $response->headersSent() ) {
+                       // Can't do anything now
+                       $this->logger->debug( __METHOD__ . ': Headers already sent' );
+                       return;
+               }
+
+               $cookies = array(
+                       'UserID' => false,
+                       'Token' => false,
+               );
+
+               $response->clearCookie(
+                       $this->params['sessionName'], array( 'prefix' => '' ) + $this->cookieOptions
+               );
+
+               foreach ( $cookies as $key => $value ) {
+                       $response->clearCookie( $key, $this->cookieOptions );
+               }
+
+               $response->clearCookie( 'forceHTTPS',
+                       array( 'prefix' => '', 'secure' => false ) + $this->cookieOptions );
+       }
+
+       /**
+        * Set the "logged out" cookie
+        * @param int $loggedOut timestamp
+        * @param WebRequest $request
+        */
+       protected function setLoggedOutCookie( $loggedOut, WebRequest $request ) {
+               if ( $loggedOut + 86400 > time() &&
+                       $loggedOut !== (int)$request->getCookie( 'LoggedOut', $this->cookieOptions['prefix'] )
+               ) {
+                       $request->response()->setCookie( 'LoggedOut', $loggedOut, $loggedOut + 86400,
+                               $this->cookieOptions );
+               }
+       }
+
+       public function getVaryCookies() {
+               return array(
+                       // Vary on token and session because those are the real authn
+                       // determiners. UserID and UserName don't matter without those.
+                       $this->cookieOptions['prefix'] . 'Token',
+                       $this->cookieOptions['prefix'] . 'LoggedOut',
+                       $this->params['sessionName'],
+                       'forceHTTPS',
+               );
+       }
+
+       public function suggestLoginUsername( WebRequest $request ) {
+                $name = $request->getCookie( 'UserName', $this->cookieOptions['prefix'] );
+                if ( $name !== null ) {
+                        $name = User::getCanonicalName( $name, 'usable' );
+                }
+                return $name === false ? null : $name;
+       }
+
+       /**
+        * Fetch the user identity from cookies
+        * @return array (int|null $id, string|null $token)
+        */
+       protected function getUserInfoFromCookies( $request ) {
+               $prefix = $this->cookieOptions['prefix'];
+               return array(
+                       $request->getCookie( 'UserID', $prefix ),
+                       $request->getCookie( 'UserName', $prefix ),
+                       $request->getCookie( 'Token', $prefix ),
+               );
+       }
+
+       /**
+        * Return the data to store in cookies
+        * @param User $user
+        * @param bool $remember
+        * @return array $cookies Set value false to unset the cookie
+        */
+       protected function cookieDataToExport( $user, $remember ) {
+               if ( $user->isAnon() ) {
+                       return array(
+                               'UserID' => false,
+                               'Token' => false,
+                       );
+               } else {
+                       return array(
+                               'UserID' => $user->getId(),
+                               'UserName' => $user->getName(),
+                               'Token' => $remember ? (string)$user->getToken() : false,
+                       );
+               }
+       }
+
+       /**
+        * Return extra data to store in the session
+        * @param User $user
+        * @return array $session
+        */
+       protected function sessionDataToExport( $user ) {
+               // If we're calling the legacy hook, we should populate $session
+               // like User::setCookies() did.
+               if ( !$user->isAnon() && $this->params['callUserSetCookiesHook'] ) {
+                       return array(
+                               'wsUserID' => $user->getId(),
+                               'wsToken' => $user->getToken(),
+                               'wsUserName' => $user->getName(),
+                       );
+               }
+
+               return array();
+       }
+
+       public function whyNoSession() {
+               return wfMessage( 'sessionprovider-nocookies' );
+       }
+
+}
diff --git a/includes/session/ImmutableSessionProviderWithCookie.php b/includes/session/ImmutableSessionProviderWithCookie.php
new file mode 100644 (file)
index 0000000..98f7e5c
--- /dev/null
@@ -0,0 +1,153 @@
+<?php
+/**
+ * MediaWiki session provider base class
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Session
+ */
+
+namespace MediaWiki\Session;
+
+use WebRequest;
+
+/**
+ * An ImmutableSessionProviderWithCookie doesn't persist the user, but
+ * optionally can use a cookie to support multiple IDs per session.
+ *
+ * As mentioned in the documentation for SessionProvider, many methods that are
+ * technically "cannot persist ID" could be turned into "can persist ID but
+ * not changing User" using a session cookie. This class implements such an
+ * optional session cookie.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+abstract class ImmutableSessionProviderWithCookie extends SessionProvider {
+
+       /** @var string|null */
+       protected $sessionCookieName = null;
+       protected $sessionCookieOptions = array();
+
+       /**
+        * @param array $params Keys include:
+        *  - sessionCookieName: Session cookie name, if multiple sessions per
+        *    client are to be supported.
+        *  - sessionCookieOptions: Options to pass to WebResponse::setCookie().
+        */
+       public function __construct( $params = array() ) {
+               parent::__construct();
+
+               if ( isset( $params['sessionCookieName'] ) ) {
+                       if ( !is_string( $params['sessionCookieName'] ) ) {
+                               throw new \InvalidArgumentException( 'sessionCookieName must be a string' );
+                       }
+                       $this->sessionCookieName = $params['sessionCookieName'];
+               }
+               if ( isset( $params['sessionCookieOptions'] ) ) {
+                       if ( !is_array( $params['sessionCookieOptions'] ) ) {
+                               throw new \InvalidArgumentException( 'sessionCookieOptions must be an array' );
+                       }
+                       $this->sessionCookieOptions = $params['sessionCookieOptions'];
+               }
+       }
+
+       /**
+        * Get the session ID from the cookie, if any.
+        *
+        * Only call this if $this->sessionCookieName !== null. If
+        * sessionCookieName is null, do some logic (probably involving a call to
+        * $this->hashToSessionId()) to create the single session ID corresponding
+        * to this WebRequest instead of calling this method.
+        *
+        * @param WebRequest $request
+        * @return string|null
+        */
+       protected function getSessionIdFromCookie( WebRequest $request ) {
+               if ( $this->sessionCookieName === null ) {
+                       throw new \BadMethodCallException(
+                               __METHOD__ . ' may not be called when $this->sessionCookieName === null'
+                       );
+               }
+
+               $prefix = isset( $this->sessionCookieOptions['prefix'] )
+                       ? $this->sessionCookieOptions['prefix']
+                       : $this->config->get( 'CookiePrefix' );
+               $id = $request->getCookie( $this->sessionCookieName, $prefix );
+               return SessionManager::validateSessionId( $id ) ? $id : null;
+       }
+
+       public function persistsSessionId() {
+               return $this->sessionCookieName !== null;
+       }
+
+       public function canChangeUser() {
+               return false;
+       }
+
+       public function persistSession( SessionBackend $session, WebRequest $request ) {
+               if ( $this->sessionCookieName === null ) {
+                       return;
+               }
+
+               $response = $request->response();
+               if ( $response->headersSent() ) {
+                       // Can't do anything now
+                       $this->logger->debug( __METHOD__ . ': Headers already sent' );
+                       return;
+               }
+
+               $options = $this->sessionCookieOptions;
+               if ( $session->shouldForceHTTPS() || $session->getUser()->requiresHTTPS() ) {
+                       $response->setCookie( 'forceHTTPS', 'true', $session->shouldRememberUser() ? 0 : null,
+                               array( 'prefix' => '', 'secure' => false ) + $options );
+                       $options['secure'] = true;
+               }
+
+               $response->setCookie( $this->sessionCookieName, $session->getId(), null, $options );
+       }
+
+       public function unpersistSession( WebRequest $request ) {
+               if ( $this->sessionCookieName === null ) {
+                       return;
+               }
+
+               $response = $request->response();
+               if ( $response->headersSent() ) {
+                       // Can't do anything now
+                       $this->logger->debug( __METHOD__ . ': Headers already sent' );
+                       return;
+               }
+
+               $response->clearCookie( $this->sessionCookieName, $this->sessionCookieOptions );
+       }
+
+       public function getVaryCookies() {
+               if ( $this->sessionCookieName === null ) {
+                       return array();
+               }
+
+               $prefix = isset( $this->sessionCookieOptions['prefix'] )
+                       ? $this->sessionCookieOptions['prefix']
+                       : $this->config->get( 'CookiePrefix' );
+               return array( $prefix . $this->sessionCookieName );
+       }
+
+       public function whyNoSession() {
+               return wfMessage( 'sessionprovider-nocookies' );
+       }
+}
diff --git a/includes/session/PHPSessionHandler.php b/includes/session/PHPSessionHandler.php
new file mode 100644 (file)
index 0000000..5344321
--- /dev/null
@@ -0,0 +1,377 @@
+<?php
+/**
+ * Session storage in object cache.
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LoggerInterface;
+use BagOStuff;
+
+/**
+ * Adapter for PHP's session handling
+ * @todo Once we drop support for PHP < 5.4, use SessionHandlerInterface
+ *  (should just be a matter of adding "implements SessionHandlerInterface" and
+ *  changing the session_set_save_handler() call).
+ * @ingroup Session
+ * @since 1.27
+ */
+class PHPSessionHandler {
+       /** @var PHPSessionHandler */
+       protected static $instance = null;
+
+       /** @var bool Whether PHP session handling is enabled */
+       protected $enable = false;
+       protected $warn = true;
+
+       /** @var SessionManager|null */
+       protected $manager;
+
+       /** @var BagOStuff|null */
+       protected $store;
+
+       /** @var LoggerInterface */
+       protected $logger;
+
+       /** @var array Track original session fields for later modification check */
+       protected $sessionFieldCache = array();
+
+       protected function __construct( SessionManager $manager ) {
+               $this->setEnableFlags(
+                       \RequestContext::getMain()->getConfig()->get( 'PHPSessionHandling' )
+               );
+               $manager->setupPHPSessionHandler( $this );
+       }
+
+       /**
+        * Set $this->enable and $this->warn
+        *
+        * Separate just because there doesn't seem to be a good way to test it
+        * otherwise.
+        *
+        * @param string $PHPSessionHandling See $wgPHPSessionHandling
+        */
+       private function setEnableFlags( $PHPSessionHandling ) {
+               switch ( $PHPSessionHandling ) {
+                       case 'enable':
+                               $this->enable = true;
+                               $this->warn = false;
+                               break;
+
+                       case 'warn':
+                               $this->enable = true;
+                               $this->warn = true;
+                               break;
+
+                       case 'disable':
+                               $this->enable = false;
+                               $this->warn = false;
+                               break;
+               }
+       }
+
+       /**
+        * Test whether the handler is installed
+        * @return bool
+        */
+       public static function isInstalled() {
+               return (bool)self::$instance;
+       }
+
+       /**
+        * Test whether the handler is installed and enabled
+        * @return bool
+        */
+       public static function isEnabled() {
+               return self::$instance && self::$instance->enable;
+       }
+
+       /**
+        * Install a session handler for the current web request
+        * @param SessionManager $manager
+        */
+       public static function install( SessionManager $manager ) {
+               if ( self::$instance ) {
+                       $manager->setupPHPSessionHandler( self::$instance );
+                       return;
+               }
+
+               self::$instance = new self( $manager );
+
+               // Close any auto-started session, before we replace it
+               session_write_close();
+
+               // Tell PHP not to mess with cookies itself
+               ini_set( 'session.use_cookies', 0 );
+               ini_set( 'session.use_trans_sid', 0 );
+
+               // Also set a sane serialization handler
+               \Wikimedia\PhpSessionSerializer::setSerializeHandler();
+
+               session_set_save_handler(
+                       array( self::$instance, 'open' ),
+                       array( self::$instance, 'close' ),
+                       array( self::$instance, 'read' ),
+                       array( self::$instance, 'write' ),
+                       array( self::$instance, 'destroy' ),
+                       array( self::$instance, 'gc' )
+               );
+
+               // It's necessary to register a shutdown function to call session_write_close(),
+               // because by the time the request shutdown function for the session module is
+               // called, other needed objects may have already been destroyed. Shutdown functions
+               // registered this way are called before object destruction.
+               register_shutdown_function( array( self::$instance, 'handleShutdown' ) );
+       }
+
+       /**
+        * Set the manager, store, and logger
+        * @private Use self::install().
+        * @param SessionManager $manager
+        * @param BagOStuff $store
+        * @param LoggerInterface $store
+        */
+       public function setManager(
+               SessionManager $manager, BagOStuff $store, LoggerInterface $logger
+       ) {
+               if ( $this->manager !== $manager ) {
+                       // Close any existing session before we change stores
+                       if ( $this->manager ) {
+                               session_write_close();
+                       }
+                       $this->manager = $manager;
+                       $this->store = $store;
+                       $this->logger = $logger;
+                       \Wikimedia\PhpSessionSerializer::setLogger( $this->logger );
+               }
+       }
+
+       /**
+        * Initialize the session (handler)
+        * @private For internal use only
+        * @param string $save_path Path used to store session files (ignored)
+        * @param string $session_name Session name (ignored)
+        * @return bool Success
+        */
+       public function open( $save_path, $session_name ) {
+               if ( self::$instance !== $this ) {
+                       throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
+               }
+               if ( !$this->enable ) {
+                       throw new \BadMethodCallException( 'Attempt to use PHP session management' );
+               }
+               return true;
+       }
+
+       /**
+        * Close the session (handler)
+        * @private For internal use only
+        * @return bool Success
+        */
+       public function close() {
+               if ( self::$instance !== $this ) {
+                       throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
+               }
+               $this->sessionFieldCache = array();
+               return true;
+       }
+
+       /**
+        * Read session data
+        * @private For internal use only
+        * @param string $id Session id
+        * @return string Session data
+        */
+       public function read( $id ) {
+               if ( self::$instance !== $this ) {
+                       throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
+               }
+               if ( !$this->enable ) {
+                       throw new \BadMethodCallException( 'Attempt to use PHP session management' );
+               }
+
+               $session = $this->manager->getSessionById( $id, false );
+               if ( !$session ) {
+                       return '';
+               }
+               $session->persist();
+
+               $data = iterator_to_array( $session );
+               $this->sessionFieldCache[$id] = $data;
+               return (string)\Wikimedia\PhpSessionSerializer::encode( $data );
+       }
+
+       /**
+        * Write session data
+        * @private For internal use only
+        * @param string $id Session id
+        * @param string $dataStr Session data. Not that you should ever call this
+        *   directly, but note that this has the same issues with code injection
+        *   via user-controlled data as does PHP's unserialize function.
+        * @return bool Success
+        */
+       public function write( $id, $dataStr ) {
+               if ( self::$instance !== $this ) {
+                       throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
+               }
+               if ( !$this->enable ) {
+                       throw new \BadMethodCallException( 'Attempt to use PHP session management' );
+               }
+
+               $session = $this->manager->getSessionById( $id, true );
+               if ( !$session ) {
+                       // This can happen under normal circumstances, if the session exists but is
+                       // invalid. Let's emit a log warning instead of a PHP warning.
+                       $this->logger->warning(
+                               __METHOD__ . ": Session \"$id\" cannot be loaded, skipping write."
+                       );
+                       return true;
+               }
+
+               // First, decode the string PHP handed us
+               $data = \Wikimedia\PhpSessionSerializer::decode( $dataStr );
+               if ( $data === null ) {
+                       // @codeCoverageIgnoreStart
+                       return false;
+                       // @codeCoverageIgnoreEnd
+               }
+
+               // Now merge the data into the Session object.
+               $changed = false;
+               $cache = isset( $this->sessionFieldCache[$id] ) ? $this->sessionFieldCache[$id] : array();
+               foreach ( $data as $key => $value ) {
+                       if ( !isset( $cache[$key] ) ) {
+                               if ( $session->exists( $key ) ) {
+                                       // New in both, so ignore and log
+                                       $this->logger->warning(
+                                               __METHOD__ . ": Key \"$key\" added in both Session and \$_SESSION!"
+                                       );
+                               } else {
+                                       // New in $_SESSION, keep it
+                                       $session->set( $key, $value );
+                                       $changed = true;
+                               }
+                       } elseif ( $cache[$key] === $value ) {
+                               // Unchanged in $_SESSION, so ignore it
+                       } elseif ( !$session->exists( $key ) ) {
+                               // Deleted in Session, keep but log
+                               $this->logger->warning(
+                                       __METHOD__ . ": Key \"$key\" deleted in Session and changed in \$_SESSION!"
+                               );
+                               $session->set( $key, $value );
+                               $changed = true;
+                       } elseif ( $cache[$key] === $session->get( $key ) ) {
+                               // Unchanged in Session, so keep it
+                               $session->set( $key, $value );
+                               $changed = true;
+                       } else {
+                               // Changed in both, so ignore and log
+                               $this->logger->warning(
+                                       __METHOD__ . ": Key \"$key\" changed in both Session and \$_SESSION!"
+                               );
+                       }
+               }
+               // Anything deleted in $_SESSION and unchanged in Session should be deleted too
+               // (but not if $_SESSION can't represent it at all)
+               \Wikimedia\PhpSessionSerializer::setLogger( new \Psr\Log\NullLogger() );
+               foreach ( $cache as $key => $value ) {
+                       if ( !isset( $data[$key] ) && $session->exists( $key ) &&
+                               \Wikimedia\PhpSessionSerializer::encode( array( $key => true ) )
+                       ) {
+                               if ( $cache[$key] === $session->get( $key ) ) {
+                                       // Unchanged in Session, delete it
+                                       $session->remove( $key );
+                                       $changed = true;
+                               } else {
+                                       // Changed in Session, ignore deletion and log
+                                       $this->logger->warning(
+                                               __METHOD__ . ": Key \"$key\" changed in Session and deleted in \$_SESSION!"
+                                       );
+                               }
+                       }
+               }
+               \Wikimedia\PhpSessionSerializer::setLogger( $this->logger );
+
+               // Save and update cache if anything changed
+               if ( $changed ) {
+                       if ( $this->warn ) {
+                               wfDeprecated( '$_SESSION', '1.27' );
+                               $this->logger->warning( 'Something wrote to $_SESSION!' );
+                       }
+
+                       $session->save();
+                       $this->sessionFieldCache[$id] = iterator_to_array( $session );
+               }
+
+               $session->persist();
+
+               return true;
+       }
+
+       /**
+        * Destroy a session
+        * @private For internal use only
+        * @param string $id Session id
+        * @return bool Success
+        */
+       public function destroy( $id ) {
+               if ( self::$instance !== $this ) {
+                       throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
+               }
+               if ( !$this->enable ) {
+                       throw new \BadMethodCallException( 'Attempt to use PHP session management' );
+               }
+               $session = $this->manager->getSessionById( $id, false );
+               if ( $session ) {
+                       $session->clear();
+               }
+               return true;
+       }
+
+       /**
+        * Execute garbage collection.
+        * @private For internal use only
+        * @param int $maxlifetime Maximum session life time (ignored)
+        * @return bool Success
+        */
+       public function gc( $maxlifetime ) {
+               if ( self::$instance !== $this ) {
+                       throw new \UnexpectedValueException( __METHOD__ . ': Wrong instance called!' );
+               }
+               $before = date( 'YmdHis', time() );
+               $this->store->deleteObjectsExpiringBefore( $before );
+               return true;
+       }
+
+       /**
+        * Shutdown function.
+        *
+        * See the comment inside self::install for rationale.
+        * @codeCoverageIgnore
+        * @private For internal use only
+        */
+       public function handleShutdown() {
+               if ( $this->enable ) {
+                       session_write_close();
+               }
+       }
+
+}
diff --git a/includes/session/Session.php b/includes/session/Session.php
new file mode 100644 (file)
index 0000000..840baa7
--- /dev/null
@@ -0,0 +1,372 @@
+<?php
+/**
+ * MediaWiki session
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use User;
+use WebRequest;
+
+/**
+ * Manages data for an an authenticated session
+ *
+ * A Session represents the fact that the current HTTP request is part of a
+ * session. There are two broad types of Sessions, based on whether they
+ * return true or false from self::canSetUser():
+ * * When true (mutable), the Session identifies multiple requests as part of
+ *   a session generically, with no tie to a particular user.
+ * * When false (immutable), the Session identifies multiple requests as part
+ *   of a session by identifying and authenticating the request itself as
+ *   belonging to a particular user.
+ *
+ * The Session object also serves as a replacement for PHP's $_SESSION,
+ * managing access to per-session data.
+ *
+ * @todo Once we drop support for PHP 5.3.3, implementing ArrayAccess would be nice.
+ * @ingroup Session
+ * @since 1.27
+ */
+final class Session implements \Countable, \Iterator {
+       /** @var SessionBackend Session backend */
+       private $backend;
+
+       /** @var int Session index */
+       private $index;
+
+       /**
+        * @param SessionBackend $backend
+        * @param int $index
+        */
+       public function __construct( SessionBackend $backend, $index ) {
+               $this->backend = $backend;
+               $this->index = $index;
+       }
+
+       public function __destruct() {
+               $this->backend->deregisterSession( $this->index );
+       }
+
+       /**
+        * Returns the session ID
+        * @return string
+        */
+       public function getId() {
+               return $this->backend->getId();
+       }
+
+       /**
+        * Returns the SessionId object
+        * @private For internal use by WebRequest
+        * @return SessionId
+        */
+       public function getSessionId() {
+               return $this->backend->getSessionId();
+       }
+
+       /**
+        * Changes the session ID
+        * @return string New ID (might be the same as the old)
+        */
+       public function resetId() {
+               return $this->backend->resetId();
+       }
+
+       /**
+        * Fetch the SessionProvider for this session
+        * @return SessionProviderInterface
+        */
+       public function getProvider() {
+               return $this->backend->getProvider();
+       }
+
+       /**
+        * Indicate whether this session is persisted across requests
+        *
+        * For example, if cookies are set.
+        *
+        * @return bool
+        */
+       public function isPersistent() {
+               return $this->backend->isPersistent();
+       }
+
+       /**
+        * Make this session persisted across requests
+        *
+        * If the session is already persistent, equivalent to calling
+        * $this->renew().
+        */
+       public function persist() {
+               $this->backend->persist();
+       }
+
+       /**
+        * Indicate whether the user should be remembered independently of the
+        * session ID.
+        * @return bool
+        */
+       public function shouldRememberUser() {
+               return $this->backend->shouldRememberUser();
+       }
+
+       /**
+        * Set whether the user should be remembered independently of the session
+        * ID.
+        * @param bool $remember
+        */
+       public function setRememberUser( $remember ) {
+               $this->backend->setRememberUser( $remember );
+       }
+
+       /**
+        * Returns the request associated with this session
+        * @return WebRequest
+        */
+       public function getRequest() {
+               return $this->backend->getRequest( $this->index );
+       }
+
+       /**
+        * Returns the authenticated user for this session
+        * @return User
+        */
+       public function getUser() {
+               return $this->backend->getUser();
+       }
+
+       /**
+        * Fetch the rights allowed the user when this session is active.
+        * @return null|string[] Allowed user rights, or null to allow all.
+        */
+       public function getAllowedUserRights() {
+               return $this->backend->getAllowedUserRights();
+       }
+
+       /**
+        * Indicate whether the session user info can be changed
+        * @return bool
+        */
+       public function canSetUser() {
+               return $this->backend->canSetUser();
+       }
+
+       /**
+        * Set a new user for this session
+        * @note This should only be called when the user has been authenticated
+        * @param User $user User to set on the session.
+        *   This may become a "UserValue" in the future, or User may be refactored
+        *   into such.
+        */
+       public function setUser( $user ) {
+               $this->backend->setUser( $user );
+       }
+
+       /**
+        * Get a suggested username for the login form
+        * @return string|null
+        */
+       public function suggestLoginUsername() {
+               return $this->backend->suggestLoginUsername( $this->index );
+       }
+
+       /**
+        * Whether HTTPS should be forced
+        * @return bool
+        */
+       public function shouldForceHTTPS() {
+               return $this->backend->shouldForceHTTPS();
+       }
+
+       /**
+        * Set whether HTTPS should be forced
+        * @param bool $force
+        */
+       public function setForceHTTPS( $force ) {
+               $this->backend->setForceHTTPS( $force );
+       }
+
+       /**
+        * Fetch the "logged out" timestamp
+        * @return int
+        */
+       public function getLoggedOutTimestamp() {
+               return $this->backend->getLoggedOutTimestamp();
+       }
+
+       /**
+        * Set the "logged out" timestamp
+        * @param int $ts
+        */
+       public function setLoggedOutTimestamp( $ts ) {
+               $this->backend->setLoggedOutTimestamp( $ts );
+       }
+
+       /**
+        * Fetch provider metadata
+        * @protected For use by SessionProvider subclasses only
+        * @return mixed
+        */
+       public function getProviderMetadata() {
+               return $this->backend->getProviderMetadata();
+       }
+
+       /**
+        * Delete all session data and clear the user (if possible)
+        */
+       public function clear() {
+               $data = &$this->backend->getData();
+               if ( $data ) {
+                       $data = array();
+                       $this->backend->dirty();
+               }
+               if ( $this->backend->canSetUser() ) {
+                       $this->backend->setUser( new User );
+               }
+               $this->backend->save();
+       }
+
+       /**
+        * Renew the session
+        *
+        * Resets the TTL in the backend store if the session is near expiring, and
+        * re-persists the session to any active WebRequests if persistent.
+        */
+       public function renew() {
+               $this->backend->renew();
+       }
+
+       /**
+        * Fetch a copy of this session attached to an alternative WebRequest
+        *
+        * Actions on the copy will affect this session too, and vice versa.
+        *
+        * @param WebRequest $request Any existing session associated with this
+        *  WebRequest object will be overwritten.
+        * @return Session
+        */
+       public function sessionWithRequest( WebRequest $request ) {
+               $request->setSessionId( $this->backend->getSessionId() );
+               return $this->backend->getSession( $request );
+       }
+
+       /**
+        * Fetch a value from the session
+        * @param string|int $key
+        * @param mixed $default
+        * @return mixed
+        */
+       public function get( $key, $default = null ) {
+               $data = &$this->backend->getData();
+               return array_key_exists( $key, $data ) ? $data[$key] : $default;
+       }
+
+       /**
+        * Test if a value exists in the session
+        * @param string|int $key
+        * @return bool
+        */
+       public function exists( $key ) {
+               $data = &$this->backend->getData();
+               return array_key_exists( $key, $data );
+       }
+
+       /**
+        * Set a value in the session
+        * @param string|int $key
+        * @param mixed $value
+        */
+       public function set( $key, $value ) {
+               $data = &$this->backend->getData();
+               if ( !array_key_exists( $key, $data ) || $data[$key] !== $value ) {
+                       $data[$key] = $value;
+                       $this->backend->dirty();
+               }
+       }
+
+       /**
+        * Remove a value from the session
+        * @param string|int $key
+        */
+       public function remove( $key ) {
+               $data = &$this->backend->getData();
+               if ( array_key_exists( $key, $data ) ) {
+                       unset( $data[$key] );
+                       $this->backend->dirty();
+               }
+       }
+
+       /**
+        * Delay automatic saving while multiple updates are being made
+        *
+        * Calls to save() or clear() will not be delayed.
+        *
+        * @return \ScopedCallback When this goes out of scope, a save will be triggered
+        */
+       public function delaySave() {
+               return $this->backend->delaySave();
+       }
+
+       /**
+        * Save the session
+        */
+       public function save() {
+               $this->backend->save();
+       }
+
+       /**
+        * @name Interface methods
+        * @{
+        */
+
+       public function count() {
+               $data = &$this->backend->getData();
+               return count( $data );
+       }
+
+       public function current() {
+               $data = &$this->backend->getData();
+               return current( $data );
+       }
+
+       public function key() {
+               $data = &$this->backend->getData();
+               return key( $data );
+       }
+
+       public function next() {
+               $data = &$this->backend->getData();
+               next( $data );
+       }
+
+       public function rewind() {
+               $data = &$this->backend->getData();
+               reset( $data );
+       }
+
+       public function valid() {
+               $data = &$this->backend->getData();
+               return key( $data ) !== null;
+       }
+
+       /**@}*/
+
+}
diff --git a/includes/session/SessionBackend.php b/includes/session/SessionBackend.php
new file mode 100644 (file)
index 0000000..3c0f692
--- /dev/null
@@ -0,0 +1,651 @@
+<?php
+/**
+ * MediaWiki session backend
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use BagOStuff;
+use Psr\Log\LoggerInterface;
+use User;
+use WebRequest;
+
+/**
+ * This is the actual workhorse for Session.
+ *
+ * Most code does not need to use this class, you want \\MediaWiki\\Session\\Session.
+ * The exceptions are SessionProviders and SessionMetadata hook functions,
+ * which get an instance of this class rather than Session.
+ *
+ * The reasons for this split are:
+ * 1. A session can be attached to multiple requests, but we want the Session
+ *    object to have some features that correspond to just one of those
+ *    requests.
+ * 2. We want reasonable garbage collection behavior, but we also want the
+ *    SessionManager to hold a reference to every active session so it can be
+ *    saved when the request ends.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+final class SessionBackend {
+       /** @var SessionId */
+       private $id;
+
+       private $persist = false;
+       private $remember = false;
+       private $forceHTTPS = false;
+
+       /** @var array|null */
+       private $data = null;
+
+       private $forcePersist = false;
+       private $metaDirty = false;
+       private $dataDirty = false;
+
+       /** @var string Used to detect subarray modifications */
+       private $dataHash = null;
+
+       /** @var BagOStuff */
+       private $store;
+
+       /** @var LoggerInterface */
+       private $logger;
+
+       /** @var int */
+       private $lifetime;
+
+       /** @var User */
+       private $user;
+
+       private $curIndex = 0;
+
+       /** @var WebRequest[] Session requests */
+       private $requests = array();
+
+       /** @var SessionProvider provider */
+       private $provider;
+
+       /** @var array|null provider-specified metadata */
+       private $providerMetadata = null;
+
+       private $expires = 0;
+       private $loggedOut = 0;
+       private $delaySave = 0;
+
+       private $usePhpSessionHandling = true;
+       private $checkPHPSessionRecursionGuard = false;
+
+       /**
+        * @param SessionId $id Session ID object
+        * @param SessionInfo $info Session info to populate from
+        * @param BagOStuff $store Backend data store
+        * @param LoggerInterface $logger
+        * @param int $lifetime Session data lifetime in seconds
+        */
+       public function __construct(
+               SessionId $id, SessionInfo $info, BagOStuff $store, LoggerInterface $logger, $lifetime
+       ) {
+               $phpSessionHandling = \RequestContext::getMain()->getConfig()->get( 'PHPSessionHandling' );
+               $this->usePhpSessionHandling = $phpSessionHandling !== 'disable';
+
+               if ( $info->getUserInfo() && !$info->getUserInfo()->isVerified() ) {
+                       throw new \InvalidArgumentException(
+                               "Refusing to create session for unverified user {$info->getUserInfo()}"
+                       );
+               }
+               if ( $info->getProvider() === null ) {
+                       throw new \InvalidArgumentException( 'Cannot create session without a provider' );
+               }
+               if ( $info->getId() !== $id->getId() ) {
+                       throw new \InvalidArgumentException( 'SessionId and SessionInfo don\'t match' );
+               }
+
+               $this->id = $id;
+               $this->user = $info->getUserInfo() ? $info->getUserInfo()->getUser() : new User;
+               $this->store = $store;
+               $this->logger = $logger;
+               $this->lifetime = $lifetime;
+               $this->provider = $info->getProvider();
+               $this->persist = $info->wasPersisted();
+               $this->remember = $info->wasRemembered();
+               $this->forceHTTPS = $info->forceHTTPS();
+               $this->providerMetadata = $info->getProviderMetadata();
+
+               $blob = $store->get( wfMemcKey( 'MWSession', (string)$this->id ) );
+               if ( !is_array( $blob ) ||
+                       !isset( $blob['metadata'] ) || !is_array( $blob['metadata'] ) ||
+                       !isset( $blob['data'] ) || !is_array( $blob['data'] )
+               ) {
+                       $this->data = array();
+                       $this->dataDirty = true;
+                       $this->metaDirty = true;
+                       $this->logger->debug( "SessionBackend $this->id is unsaved, marking dirty in constructor" );
+               } else {
+                       $this->data = $blob['data'];
+                       if ( isset( $blob['metadata']['loggedOut'] ) ) {
+                               $this->loggedOut = (int)$blob['metadata']['loggedOut'];
+                       }
+                       if ( isset( $blob['metadata']['expires'] ) ) {
+                               $this->expires = (int)$blob['metadata']['expires'];
+                       } else {
+                               $this->metaDirty = true;
+                               $this->logger->debug(
+                                       "SessionBackend $this->id metadata dirty due to missing expiration timestamp"
+                               );
+                       }
+               }
+               $this->dataHash = md5( serialize( $this->data ) );
+       }
+
+       /**
+        * Return a new Session for this backend
+        * @param WebRequest $request
+        * @return Session
+        */
+       public function getSession( WebRequest $request ) {
+               $index = ++$this->curIndex;
+               $this->requests[$index] = $request;
+               $session = new Session( $this, $index );
+               return $session;
+       }
+
+       /**
+        * Deregister a Session
+        * @private For use by \\MediaWiki\\Session\\Session::__destruct() only
+        * @param int $index
+        */
+       public function deregisterSession( $index ) {
+               unset( $this->requests[$index] );
+               if ( !count( $this->requests ) ) {
+                       $this->save( true );
+                       $this->provider->getManager()->deregisterSessionBackend( $this );
+               }
+       }
+
+       /**
+        * Returns the session ID.
+        * @return string
+        */
+       public function getId() {
+               return (string)$this->id;
+       }
+
+       /**
+        * Fetch the SessionId object
+        * @private For internal use by WebRequest
+        * @return SessionId
+        */
+       public function getSessionId() {
+               return $this->id;
+       }
+
+       /**
+        * Changes the session ID
+        * @return string New ID (might be the same as the old)
+        */
+       public function resetId() {
+               if ( $this->provider->persistsSessionId() ) {
+                       $oldId = (string)$this->id;
+                       $restart = $this->usePhpSessionHandling && $oldId === session_id() &&
+                               PHPSessionHandler::isEnabled();
+
+                       if ( $restart ) {
+                               // If this session is the one behind PHP's $_SESSION, we need
+                               // to close then reopen it.
+                               session_write_close();
+                       }
+
+                       $this->provider->getManager()->changeBackendId( $this );
+                       $this->provider->sessionIdWasReset( $this, $oldId );
+                       $this->metaDirty = true;
+                       $this->logger->debug(
+                               "SessionBackend $this->id metadata dirty due to ID reset (formerly $oldId)"
+                       );
+
+                       if ( $restart ) {
+                               session_id( (string)$this->id );
+                               \MediaWiki\quietCall( 'session_start' );
+                       }
+
+                       $this->autosave();
+
+                       // Delete the data for the old session ID now
+                       $this->store->delete( wfMemcKey( 'MWSession', $oldId ) );
+               }
+       }
+
+       /**
+        * Fetch the SessionProvider for this session
+        * @return SessionProviderInterface
+        */
+       public function getProvider() {
+               return $this->provider;
+       }
+
+       /**
+        * Indicate whether this session is persisted across requests
+        *
+        * For example, if cookies are set.
+        *
+        * @return bool
+        */
+       public function isPersistent() {
+               return $this->persist;
+       }
+
+       /**
+        * Make this session persisted across requests
+        *
+        * If the session is already persistent, equivalent to calling
+        * $this->renew().
+        */
+       public function persist() {
+               if ( !$this->persist ) {
+                       $this->persist = true;
+                       $this->forcePersist = true;
+                       $this->logger->debug( "SessionBackend $this->id force-persist due to persist()" );
+                       $this->autosave();
+               } else {
+                       $this->renew();
+               }
+       }
+
+       /**
+        * Indicate whether the user should be remembered independently of the
+        * session ID.
+        * @return bool
+        */
+       public function shouldRememberUser() {
+               return $this->remember;
+       }
+
+       /**
+        * Set whether the user should be remembered independently of the session
+        * ID.
+        * @param bool $remember
+        */
+       public function setRememberUser( $remember ) {
+               if ( $this->remember !== (bool)$remember ) {
+                       $this->remember = (bool)$remember;
+                       $this->metaDirty = true;
+                       $this->logger->debug( "SessionBackend $this->id metadata dirty due to remember-user change" );
+                       $this->autosave();
+               }
+       }
+
+       /**
+        * Returns the request associated with a Session
+        * @param int $index Session index
+        * @return WebRequest
+        */
+       public function getRequest( $index ) {
+               if ( !isset( $this->requests[$index] ) ) {
+                       throw new \InvalidArgumentException( 'Invalid session index' );
+               }
+               return $this->requests[$index];
+       }
+
+       /**
+        * Returns the authenticated user for this session
+        * @return User
+        */
+       public function getUser() {
+               return $this->user;
+       }
+
+       /**
+        * Fetch the rights allowed the user when this session is active.
+        * @return null|string[] Allowed user rights, or null to allow all.
+        */
+       public function getAllowedUserRights() {
+               return $this->provider->getAllowedUserRights( $this );
+       }
+
+       /**
+        * Indicate whether the session user info can be changed
+        * @return bool
+        */
+       public function canSetUser() {
+               return $this->provider->canChangeUser();
+       }
+
+       /**
+        * Set a new user for this session
+        * @note This should only be called when the user has been authenticated via a login process
+        * @param User $user User to set on the session.
+        *   This may become a "UserValue" in the future, or User may be refactored
+        *   into such.
+        */
+       public function setUser( $user ) {
+               if ( !$this->canSetUser() ) {
+                       throw new \BadMethodCallException(
+                               'Cannot set user on this session; check $session->canSetUser() first'
+                       );
+               }
+
+               $this->user = $user;
+               $this->metaDirty = true;
+               $this->logger->debug( "SessionBackend $this->id metadata dirty due to user change" );
+               $this->autosave();
+       }
+
+       /**
+        * Get a suggested username for the login form
+        * @param int $index Session index
+        * @return string|null
+        */
+       public function suggestLoginUsername( $index ) {
+               if ( !isset( $this->requests[$index] ) ) {
+                       throw new \InvalidArgumentException( 'Invalid session index' );
+               }
+               return $this->provider->suggestLoginUsername( $this->requests[$index] );
+       }
+
+       /**
+        * Whether HTTPS should be forced
+        * @return bool
+        */
+       public function shouldForceHTTPS() {
+               return $this->forceHTTPS;
+       }
+
+       /**
+        * Set whether HTTPS should be forced
+        * @param bool $force
+        */
+       public function setForceHTTPS( $force ) {
+               if ( $this->forceHTTPS !== (bool)$force ) {
+                       $this->forceHTTPS = (bool)$force;
+                       $this->metaDirty = true;
+                       $this->logger->debug( "SessionBackend $this->id metadata dirty due to force-HTTPS change" );
+                       $this->autosave();
+               }
+       }
+
+       /**
+        * Fetch the "logged out" timestamp
+        * @return int
+        */
+       public function getLoggedOutTimestamp() {
+               return $this->loggedOut;
+       }
+
+       /**
+        * Set the "logged out" timestamp
+        * @param int $ts
+        */
+       public function setLoggedOutTimestamp( $ts = null ) {
+               $ts = (int)$ts;
+               if ( $this->loggedOut !== $ts ) {
+                       $this->loggedOut = $ts;
+                       $this->metaDirty = true;
+                       $this->logger->debug(
+                               "SessionBackend $this->id metadata dirty due to logged-out-timestamp change"
+                       );
+                       $this->autosave();
+               }
+       }
+
+       /**
+        * Fetch provider metadata
+        * @protected For use by SessionProvider subclasses only
+        * @return array|null
+        */
+       public function getProviderMetadata() {
+               return $this->providerMetadata;
+       }
+
+       /**
+        * Set provider metadata
+        * @protected For use by SessionProvider subclasses only
+        * @param array|null $metadata
+        */
+       public function setProviderMetadata( $metadata ) {
+               if ( $metadata !== null && !is_array( $metadata ) ) {
+                       throw new \InvalidArgumentException( '$metadata must be an array or null' );
+               }
+               if ( $this->providerMetadata !== $metadata ) {
+                       $this->providerMetadata = $metadata;
+                       $this->metaDirty = true;
+                       $this->logger->debug(
+                               "SessionBackend $this->id metadata dirty due to provider metadata change"
+                       );
+                       $this->autosave();
+               }
+       }
+
+       /**
+        * Fetch the session data array
+        *
+        * Note the caller is responsible for calling $this->dirty() if anything in
+        * the array is changed.
+        *
+        * @private For use by \\MediaWiki\\Session\\Session only.
+        * @return array
+        */
+       public function &getData() {
+               return $this->data;
+       }
+
+       /**
+        * Add data to the session.
+        *
+        * Overwrites any existing data under the same keys.
+        *
+        * @param array $newData Key-value pairs to add to the session
+        */
+       public function addData( array $newData ) {
+               $data = &$this->getData();
+               foreach ( $newData as $key => $value ) {
+                       if ( !array_key_exists( $key, $data ) || $data[$key] !== $value ) {
+                               $data[$key] = $value;
+                               $this->dataDirty = true;
+                               $this->logger->debug(
+                                       "SessionBackend $this->id data dirty due to addData(): " . wfGetAllCallers( 5 )
+                               );
+                       }
+               }
+       }
+
+       /**
+        * Mark data as dirty
+        * @private For use by \\MediaWiki\\Session\\Session only.
+        */
+       public function dirty() {
+               $this->dataDirty = true;
+               $this->logger->debug(
+                       "SessionBackend $this->id data dirty due to dirty(): " . wfGetAllCallers( 5 )
+               );
+       }
+
+       /**
+        * Renew the session by resaving everything
+        *
+        * Resets the TTL in the backend store if the session is near expiring, and
+        * re-persists the session to any active WebRequests if persistent.
+        */
+       public function renew() {
+               if ( time() + $this->lifetime / 2 > $this->expires ) {
+                       $this->metaDirty = true;
+                       $this->logger->debug(
+                               "SessionBackend $this->id metadata dirty for renew(): " . wfGetAllCallers( 5 )
+                       );
+                       if ( $this->persist ) {
+                               $this->forcePersist = true;
+                               $this->logger->debug(
+                                       "SessionBackend $this->id force-persist for renew(): " . wfGetAllCallers( 5 )
+                               );
+                       }
+               }
+               $this->autosave();
+       }
+
+       /**
+        * Delay automatic saving while multiple updates are being made
+        *
+        * Calls to save() will not be delayed.
+        *
+        * @return \ScopedCallback When this goes out of scope, a save will be triggered
+        */
+       public function delaySave() {
+               $that = $this;
+               $this->delaySave++;
+               $ref = &$this->delaySave;
+               return new \ScopedCallback( function () use ( $that, &$ref ) {
+                       if ( --$ref <= 0 ) {
+                               $ref = 0;
+                               $that->save();
+                       }
+               } );
+       }
+
+       /**
+        * Save and persist session data, unless delayed
+        */
+       private function autosave() {
+               if ( $this->delaySave <= 0 ) {
+                       $this->save();
+               }
+       }
+
+       /**
+        * Save and persist session data
+        * @param bool $closing Whether the session is being closed
+        */
+       public function save( $closing = false ) {
+               if ( $this->provider->getManager()->isUserSessionPrevented( $this->user->getName() ) ) {
+                       $this->logger->debug(
+                               "SessionBackend $this->id not saving, " .
+                                       "user {$this->user} was passed to SessionManager::preventSessionsForUser"
+                       );
+                       return;
+               }
+
+               // Ensure the user has a token
+               // @codeCoverageIgnoreStart
+               $anon = $this->user->isAnon();
+               if ( !$anon && !$this->user->getToken() ) {
+                       $this->logger->debug(
+                               "SessionBackend $this->id creating token for user {$this->user} on save"
+                       );
+                       $this->user->setToken();
+                       if ( !wfReadOnly() ) {
+                               $this->user->saveSettings();
+                       }
+                       $this->metaDirty = true;
+               }
+               // @codeCoverageIgnoreEnd
+
+               if ( !$this->metaDirty && !$this->dataDirty &&
+                       $this->dataHash !== md5( serialize( $this->data ) )
+               ) {
+                       $this->logger->debug( "SessionBackend $this->id data dirty due to hash mismatch, " .
+                               "$this->dataHash !== " . md5( serialize( $this->data ) ) );
+                       $this->dataDirty = true;
+               }
+
+               if ( !$this->metaDirty && !$this->dataDirty && !$this->forcePersist ) {
+                       return;
+               }
+
+               $this->logger->debug( "SessionBackend $this->id save: " .
+                       'dataDirty=' . (int)$this->dataDirty . ' ' .
+                       'metaDirty=' . (int)$this->metaDirty . ' ' .
+                       'forcePersist=' . (int)$this->forcePersist
+               );
+
+               // Persist to the provider, if flagged
+               if ( $this->persist && ( $this->metaDirty || $this->forcePersist ) ) {
+                       foreach ( $this->requests as $request ) {
+                               $request->setSessionId( $this->getSessionId() );
+                               $this->provider->persistSession( $this, $request );
+                       }
+                       if ( !$closing ) {
+                               $this->checkPHPSession();
+                       }
+               }
+
+               $this->forcePersist = false;
+
+               if ( !$this->metaDirty && !$this->dataDirty ) {
+                       return;
+               }
+
+               // Save session data to store, if necessary
+               $metadata = $origMetadata = array(
+                       'provider' => (string)$this->provider,
+                       'providerMetadata' => $this->providerMetadata,
+                       'userId' => $anon ? 0 : $this->user->getId(),
+                       'userName' => $anon ? null : $this->user->getName(),
+                       'userToken' => $anon ? null : $this->user->getToken(),
+                       'remember' => !$anon && $this->remember,
+                       'forceHTTPS' => $this->forceHTTPS,
+                       'expires' => time() + $this->lifetime,
+                       'loggedOut' => $this->loggedOut,
+               );
+
+               \Hooks::run( 'SessionMetadata', array( $this, &$metadata, $this->requests ) );
+
+               foreach ( $origMetadata as $k => $v ) {
+                       if ( $metadata[$k] !== $v ) {
+                               throw new \UnexpectedValueException( "SessionMetadata hook changed metadata key \"$k\"" );
+                       }
+               }
+
+               $this->store->set(
+                       wfMemcKey( 'MWSession', (string)$this->id ),
+                       array(
+                               'data' => $this->data,
+                               'metadata' => $metadata,
+                       ),
+                       $metadata['expires']
+               );
+
+               $this->metaDirty = false;
+               $this->dataDirty = false;
+               $this->dataHash = md5( serialize( $this->data ) );
+               $this->expires = $metadata['expires'];
+       }
+
+       /**
+        * For backwards compatibility, open the PHP session when the global
+        * session is persisted
+        */
+       private function checkPHPSession() {
+               if ( !$this->checkPHPSessionRecursionGuard ) {
+                       $this->checkPHPSessionRecursionGuard = true;
+                       $ref = &$this->checkPHPSessionRecursionGuard;
+                       $reset = new \ScopedCallback( function () use ( &$ref ) {
+                               $ref = false;
+                       } );
+
+                       if ( $this->usePhpSessionHandling && session_id() === '' && PHPSessionHandler::isEnabled() &&
+                               SessionManager::getGlobalSession()->getId() === (string)$this->id
+                       ) {
+                               $this->logger->debug( "SessionBackend $this->id: Taking over PHP session" );
+                               session_id( (string)$this->id );
+                               \MediaWiki\quietCall( 'session_start' );
+                       }
+               }
+       }
+
+}
diff --git a/includes/session/SessionId.php b/includes/session/SessionId.php
new file mode 100644 (file)
index 0000000..0669100
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * MediaWiki session ID holder
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+/**
+ * Value object holding the session ID in a manner that can be globally
+ * updated.
+ *
+ * This class exists because we want WebRequest to refer to the session, but it
+ * can't hold the Session itself due to issues with circular references and it
+ * can't just hold the ID as a string because we need to be able to update the
+ * ID when SessionBackend::resetId() is called.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+final class SessionId {
+       /** @var string */
+       private $id;
+
+       /**
+        * @param string $id
+        */
+       public function __construct( $id ) {
+               $this->id = $id;
+       }
+
+       /**
+        * Get the ID
+        * @return string
+        */
+       public function getId() {
+               return $this->id;
+       }
+
+       /**
+        * Set the ID
+        * @private For use by \\MediaWiki\\Session\\SessionManager only
+        * @param string $id
+        */
+       public function setId( $id ) {
+               $this->id = $id;
+       }
+
+       public function __toString() {
+               return $this->id;
+       }
+
+}
diff --git a/includes/session/SessionInfo.php b/includes/session/SessionInfo.php
new file mode 100644 (file)
index 0000000..9fe2cdf
--- /dev/null
@@ -0,0 +1,270 @@
+<?php
+/**
+ * MediaWiki session info
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LoggerInterface;
+use BagOStuff;
+use WebRequest;
+
+/**
+ * Value object returned by SessionProvider
+ *
+ * This holds the data necessary to construct a Session.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+class SessionInfo {
+       /** Minimum allowed priority */
+       const MIN_PRIORITY = 1;
+
+       /** Maximum allowed priority */
+       const MAX_PRIORITY = 100;
+
+       /** @var SessionProvider|null */
+       private $provider;
+
+       /** @var string */
+       private $id;
+
+       /** @var int */
+       private $priority;
+
+       /** @var UserInfo|null */
+       private $userInfo = null;
+
+       private $persisted = false;
+       private $remembered = false;
+       private $forceHTTPS = false;
+       private $idIsSafe = false;
+
+       /** @var array|null */
+       private $providerMetadata = null;
+
+       /**
+        * @param int $priority Session priority
+        * @param array $data
+        *  - provider: (SessionProvider|null) If not given, the provider will be
+        *    determined from the saved session data.
+        *  - id: (string|null) Session ID
+        *  - userInfo: (UserInfo|null) User known from the request. If
+        *    $provider->canChangeUser() is false, a verified user
+        *    must be provided.
+        *  - persisted: (bool) Whether this session was persisted
+        *  - remembered: (bool) Whether the verified user was remembered.
+        *    Defaults to true.
+        *  - forceHTTPS: (bool) Whether to force HTTPS for this session
+        *  - metadata: (array) Provider metadata, to be returned by
+        *    Session::getProviderMetadata().
+        *  - 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.
+        *  - copyFrom: (SessionInfo) SessionInfo to copy other data items from.
+        */
+       public function __construct( $priority, array $data ) {
+               if ( $priority < self::MIN_PRIORITY || $priority > self::MAX_PRIORITY ) {
+                       throw new \InvalidArgumentException( 'Invalid priority' );
+               }
+
+               if ( isset( $data['copyFrom'] ) ) {
+                       $from = $data['copyFrom'];
+                       if ( !$from instanceof SessionInfo ) {
+                               throw new \InvalidArgumentException( 'Invalid copyFrom' );
+                       }
+                       $data += array(
+                               'provider' => $from->provider,
+                               'id' => $from->id,
+                               'userInfo' => $from->userInfo,
+                               'persisted' => $from->persisted,
+                               'remembered' => $from->remembered,
+                               'forceHTTPS' => $from->forceHTTPS,
+                               'metadata' => $from->providerMetadata,
+                               'idIsSafe' => $from->idIsSafe,
+                               // @codeCoverageIgnoreStart
+                       );
+                       // @codeCoverageIgnoreEnd
+               } else {
+                       $data += array(
+                               'provider' => null,
+                               'id' => null,
+                               'userInfo' => null,
+                               'persisted' => false,
+                               'remembered' => true,
+                               'forceHTTPS' => false,
+                               'metadata' => null,
+                               'idIsSafe' => false,
+                               // @codeCoverageIgnoreStart
+                       );
+                       // @codeCoverageIgnoreEnd
+               }
+
+               if ( $data['id'] !== null && !SessionManager::validateSessionId( $data['id'] ) ) {
+                       throw new \InvalidArgumentException( 'Invalid session ID' );
+               }
+
+               if ( $data['userInfo'] !== null && !$data['userInfo'] instanceof UserInfo ) {
+                       throw new \InvalidArgumentException( 'Invalid userInfo' );
+               }
+
+               if ( !$data['provider'] && $data['id'] === null ) {
+                       throw new \InvalidArgumentException(
+                               'Must supply an ID when no provider is given'
+                       );
+               }
+
+               if ( $data['metadata'] !== null && !is_array( $data['metadata'] ) ) {
+                       throw new \InvalidArgumentException( 'Invalid metadata' );
+               }
+
+               $this->provider = $data['provider'];
+               if ( $data['id'] !== null ) {
+                       $this->id = $data['id'];
+                       $this->idIsSafe = $data['idIsSafe'];
+               } else {
+                       $this->id = $this->provider->getManager()->generateSessionId();
+                       $this->idIsSafe = true;
+               }
+               $this->priority = (int)$priority;
+               $this->userInfo = $data['userInfo'];
+               $this->persisted = (bool)$data['persisted'];
+               if ( $data['provider'] !== null ) {
+                       if ( $this->userInfo !== null && !$this->userInfo->isAnon() && $this->userInfo->isVerified() ) {
+                               $this->remembered = (bool)$data['remembered'];
+                       }
+                       $this->providerMetadata = $data['metadata'];
+               }
+               $this->forceHTTPS = (bool)$data['forceHTTPS'];
+       }
+
+       /**
+        * Return the provider
+        * @return SessionProvider|null
+        */
+       final public function getProvider() {
+               return $this->provider;
+       }
+
+       /**
+        * Return the session ID
+        * @return string
+        */
+       final public function getId() {
+               return $this->id;
+       }
+
+       /**
+        * Indicate whether the ID is "safe"
+        *
+        * The ID is safe in the following cases:
+        * - The ID was randomly generated by the constructor.
+        * - The ID was found in the backend data store.
+        * - $this->getProvider()->persistsSessionId() is false.
+        * - The constructor was explicitly told it's safe using the 'idIsSafe'
+        *   parameter.
+        *
+        * @return bool
+        */
+       final public function isIdSafe() {
+               return $this->idIsSafe;
+       }
+
+       /**
+        * Return the priority
+        * @return int
+        */
+       final public function getPriority() {
+               return $this->priority;
+       }
+
+       /**
+        * Return the user
+        * @return UserInfo|null
+        */
+       final public function getUserInfo() {
+               return $this->userInfo;
+       }
+
+       /**
+        * Return whether the session is persisted
+        *
+        * i.e. a session ID was given to the constuctor
+        *
+        * @return bool
+        */
+       final public function wasPersisted() {
+               return $this->persisted;
+       }
+
+       /**
+        * Return provider metadata
+        * @return array|null
+        */
+       final public function getProviderMetadata() {
+               return $this->providerMetadata;
+       }
+
+       /**
+        * Return whether the user was remembered
+        *
+        * For providers that can persist the user separately from the session,
+        * the human using it may not actually *want* that to be done. For example,
+        * a cookie-based provider can set cookies that are longer-lived than the
+        * backend session data, but on a public terminal the human likely doesn't
+        * want those cookies set.
+        *
+        * This is false unless a non-anonymous verified user was passed to
+        * the SessionInfo constructor by the provider, and the provider didn't
+        * pass false for the 'remembered' data item.
+        *
+        * @return bool
+        */
+       final public function wasRemembered() {
+               return $this->remembered;
+       }
+
+       /**
+        * Whether this session should only be used over HTTPS
+        * @return bool
+        */
+       final public function forceHTTPS() {
+               return $this->forceHTTPS;
+       }
+
+       public function __toString() {
+               return '[' . $this->getPriority() . ']' .
+                       ( $this->getProvider() ?: 'null' ) .
+                       ( $this->userInfo ?: '<null>' ) . $this->getId();
+       }
+
+       /**
+        * Compare two SessionInfo objects by priority
+        * @param SessionInfo $a
+        * @param SessionInfo $b
+        * @return int Negative if $a < $b, positive if $a > $b, zero if equal
+        */
+       public static function compare( $a, $b ) {
+               return $a->getPriority() - $b->getPriority();
+       }
+
+}
diff --git a/includes/session/SessionManager.php b/includes/session/SessionManager.php
new file mode 100644 (file)
index 0000000..c4f33d0
--- /dev/null
@@ -0,0 +1,1008 @@
+<?php
+/**
+ * MediaWiki\Session entry point
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LoggerInterface;
+use BagOStuff;
+use Config;
+use FauxRequest;
+use Language;
+use Message;
+use User;
+use WebRequest;
+
+/**
+ * This serves as the entry point to the MediaWiki session handling system.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+final class SessionManager implements SessionManagerInterface {
+       /** @var SessionManager|null */
+       private static $instance = null;
+
+       /** @var Session|null */
+       private static $globalSession = null;
+
+       /** @var WebRequest|null */
+       private static $globalSessionRequest = null;
+
+       /** @var LoggerInterface */
+       private $logger;
+
+       /** @var Config */
+       private $config;
+
+       /** @var BagOStuff|null */
+       private $store;
+
+       /** @var SessionProvider[] */
+       private $sessionProviders = null;
+
+       /** @var string[] */
+       private $varyCookies = null;
+
+       /** @var array */
+       private $varyHeaders = null;
+
+       /** @var SessionBackend[] */
+       private $allSessionBackends = array();
+
+       /** @var SessionId[] */
+       private $allSessionIds = array();
+
+       /** @var string[] */
+       private $preventUsers = array();
+
+       /**
+        * Get the global SessionManager
+        * @return SessionManagerInterface
+        *  (really a SessionManager, but this is to make IDEs less confused)
+        */
+       public static function singleton() {
+               if ( self::$instance === null ) {
+                       self::$instance = new self();
+               }
+               return self::$instance;
+       }
+
+       /**
+        * Get the "global" session
+        *
+        * If PHP's session_id() has been set, returns that session. Otherwise
+        * returns the session for RequestContext::getMain()->getRequest().
+        *
+        * @return Session
+        */
+       public static function getGlobalSession() {
+               if ( !PHPSessionHandler::isEnabled() ) {
+                       $id = '';
+               } else {
+                       $id = session_id();
+               }
+
+               $request = \RequestContext::getMain()->getRequest();
+               if (
+                       !self::$globalSession // No global session is set up yet
+                       || self::$globalSessionRequest !== $request // The global WebRequest changed
+                       || $id !== '' && self::$globalSession->getId() !== $id // Someone messed with session_id()
+               ) {
+                       self::$globalSessionRequest = $request;
+                       if ( $id === '' ) {
+                               // session_id() wasn't used, so fetch the Session from the WebRequest.
+                               // We use $request->getSession() instead of $singleton->getSessionForRequest()
+                               // because doing the latter would require a public
+                               // "$request->getSessionId()" method that would confuse end
+                               // users by returning SessionId|null where they'd expect it to
+                               // be short for $request->getSession()->getId(), and would
+                               // wind up being a duplicate of the code in
+                               // $request->getSession() anyway.
+                               self::$globalSession = $request->getSession();
+                       } else {
+                               // Someone used session_id(), so we need to follow suit.
+                               // Note this overwrites whatever session might already be
+                               // associated with $request with the one for $id.
+                               self::$globalSession = self::singleton()->getSessionById( $id, true, $request )
+                                       ?: $request->getSession();
+                       }
+               }
+               return self::$globalSession;
+       }
+
+       /**
+        * @param array $options
+        *  - config: Config to fetch configuration from. Defaults to the default 'main' config.
+        *  - logger: LoggerInterface to use for logging. Defaults to the 'session' channel.
+        *  - store: BagOStuff to store session data in.
+        */
+       public function __construct( $options = array() ) {
+               if ( isset( $options['config'] ) ) {
+                       $this->config = $options['config'];
+                       if ( !$this->config instanceof Config ) {
+                               throw new \InvalidArgumentException(
+                                       '$options[\'config\'] must be an instance of Config'
+                               );
+                       }
+               } else {
+                       $this->config = \ConfigFactory::getDefaultInstance()->makeConfig( 'main' );
+               }
+
+               if ( isset( $options['logger'] ) ) {
+                       if ( !$options['logger'] instanceof LoggerInterface ) {
+                               throw new \InvalidArgumentException(
+                                       '$options[\'logger\'] must be an instance of LoggerInterface'
+                               );
+                       }
+                       $this->setLogger( $options['logger'] );
+               } else {
+                       $this->setLogger( \MediaWiki\Logger\LoggerFactory::getInstance( 'session' ) );
+               }
+
+               if ( isset( $options['store'] ) ) {
+                       if ( !$options['store'] instanceof BagOStuff ) {
+                               throw new \InvalidArgumentException(
+                                       '$options[\'store\'] must be an instance of BagOStuff'
+                               );
+                       }
+                       $this->store = $options['store'];
+               } else {
+                       $this->store = \ObjectCache::getInstance( $this->config->get( 'SessionCacheType' ) );
+                       $this->store->setLogger( $this->logger );
+               }
+
+               register_shutdown_function( array( $this, 'shutdown' ) );
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
+       }
+
+       public function getPersistedSessionId( WebRequest $request ) {
+               $info = $this->getSessionInfoForRequest( $request );
+               if ( $info && $info->wasPersisted() ) {
+                       return $info->getId();
+               } else {
+                       return null;
+               }
+       }
+
+       public function getSessionForRequest( WebRequest $request ) {
+               $info = $this->getSessionInfoForRequest( $request );
+
+               if ( !$info ) {
+                       $session = $this->getEmptySession( $request );
+               } else {
+                       $session = $this->getSessionFromInfo( $info, $request );
+               }
+               return $session;
+       }
+
+       public function getSessionById( $id, $create = false, WebRequest $request = null ) {
+               if ( !self::validateSessionId( $id ) ) {
+                       throw new \InvalidArgumentException( 'Invalid session ID' );
+               }
+               if ( !$request ) {
+                       $request = new FauxRequest;
+               }
+
+               $session = null;
+
+               // Test this here to provide a better log message for the common case
+               // of "no such ID"
+               $key = wfMemcKey( 'MWSession', $id );
+               if ( is_array( $this->store->get( $key ) ) ) {
+                       $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array( 'id' => $id, 'idIsSafe' => true ) );
+                       if ( $this->loadSessionInfoFromStore( $info, $request ) ) {
+                               $session = $this->getSessionFromInfo( $info, $request );
+                       }
+               }
+
+               if ( $create && $session === null ) {
+                       $ex = null;
+                       try {
+                               $session = $this->getEmptySessionInternal( $request, $id );
+                       } catch ( \Exception $ex ) {
+                               $this->logger->error( __METHOD__ . ': failed to create empty session: ' .
+                                       $ex->getMessage() );
+                               $session = null;
+                       }
+               }
+
+               return $session;
+       }
+
+       public function getEmptySession( WebRequest $request = null ) {
+               return $this->getEmptySessionInternal( $request );
+       }
+
+       /**
+        * @see SessionManagerInterface::getEmptySession
+        * @param WebRequest|null $request
+        * @param string|null $id ID to force on the new session
+        * @return Session
+        */
+       private function getEmptySessionInternal( WebRequest $request = null, $id = null ) {
+               if ( $id !== null ) {
+                       if ( !self::validateSessionId( $id ) ) {
+                               throw new \InvalidArgumentException( 'Invalid session ID' );
+                       }
+
+                       $key = wfMemcKey( 'MWSession', $id );
+                       if ( is_array( $this->store->get( $key ) ) ) {
+                               throw new \InvalidArgumentException( 'Session ID already exists' );
+                       }
+               }
+               if ( !$request ) {
+                       $request = new FauxRequest;
+               }
+
+               $infos = array();
+               foreach ( $this->getProviders() as $provider ) {
+                       $info = $provider->newSessionInfo( $id );
+                       if ( !$info ) {
+                               continue;
+                       }
+                       if ( $info->getProvider() !== $provider ) {
+                               throw new \UnexpectedValueException(
+                                       "$provider returned an empty session info for a different provider: $info"
+                               );
+                       }
+                       if ( $id !== null && $info->getId() !== $id ) {
+                               throw new \UnexpectedValueException(
+                                       "$provider returned empty session info with a wrong id: " .
+                                               $info->getId() . ' != ' . $id
+                               );
+                       }
+                       if ( !$info->isIdSafe() ) {
+                               throw new \UnexpectedValueException(
+                                       "$provider returned empty session info with id flagged unsafe"
+                               );
+                       }
+                       $compare = $infos ? SessionInfo::compare( $infos[0], $info ) : -1;
+                       if ( $compare > 0 ) {
+                               continue;
+                       }
+                       if ( $compare === 0 ) {
+                               $infos[] = $info;
+                       } else {
+                               $infos = array( $info );
+                       }
+               }
+
+               // Make sure there's exactly one
+               if ( count( $infos ) > 1 ) {
+                       throw new \UnexpectedValueException(
+                               'Multiple empty sessions tied for top priority: ' . join( ', ', $infos )
+                       );
+               } elseif ( count( $infos ) < 1 ) {
+                       throw new \UnexpectedValueException( 'No provider could provide an empty session!' );
+               }
+
+               return $this->getSessionFromInfo( $infos[0], $request );
+       }
+
+       public function getVaryHeaders() {
+               if ( $this->varyHeaders === null ) {
+                       $headers = array();
+                       foreach ( $this->getProviders() as $provider ) {
+                               foreach ( $provider->getVaryHeaders() as $header => $options ) {
+                                       if ( !isset( $headers[$header] ) ) {
+                                               $headers[$header] = array();
+                                       }
+                                       if ( is_array( $options ) ) {
+                                               $headers[$header] = array_unique( array_merge( $headers[$header], $options ) );
+                                       }
+                               }
+                       }
+                       $this->varyHeaders = $headers;
+               }
+               return $this->varyHeaders;
+       }
+
+       public function getVaryCookies() {
+               if ( $this->varyCookies === null ) {
+                       $cookies = array();
+                       foreach ( $this->getProviders() as $provider ) {
+                               $cookies = array_merge( $cookies, $provider->getVaryCookies() );
+                       }
+                       $this->varyCookies = array_values( array_unique( $cookies ) );
+               }
+               return $this->varyCookies;
+       }
+
+       /**
+        * Validate a session ID
+        * @param string $id
+        * @return bool
+        */
+       public static function validateSessionId( $id ) {
+               return is_string( $id ) && preg_match( '/^[a-zA-Z0-9_-]{32,}$/', $id );
+       }
+
+       /**
+        * @name Internal methods
+        * @{
+        */
+
+       /**
+        * Auto-create the given user, if necessary
+        * @private Don't call this yourself. Let Setup.php do it for you at the right time.
+        * @note This more properly belongs in AuthManager, but we need it now.
+        *  When AuthManager comes, this will be deprecated and will pass-through
+        *  to the corresponding AuthManager method.
+        * @param User $user User to auto-create
+        * @return bool Success
+        */
+       public static function autoCreateUser( User $user ) {
+               global $wgAuth;
+
+               $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() );
+
+               // 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 );
+               }
+               // @codeCoverageIgnoreEnd
+
+               if ( $localId ) {
+                       // User exists after all.
+                       $user->setId( $localId );
+                       $user->loadFromId();
+                       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', array( $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: $from" );
+
+               try {
+                       // Insert the user into the local DB master
+                       $status = $user->addToDatabase();
+                       if ( !$status->isOK() ) {
+                               // @codeCoverageIgnoreStart
+                               $logger->error( __METHOD__ . ': failed with message ' . $status->getWikiText() );
+                               $user->setId( 0 );
+                               $user->loadFromId();
+                               return false;
+                               // @codeCoverageIgnoreEnd
+                       }
+               } catch ( \Exception $ex ) {
+                       // @codeCoverageIgnoreStart
+                       $logger->error( __METHOD__ . ': failed with exception ' . $ex->getMessage() );
+                       // 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 hooks (e.g. Newuserlog)
+               \Hooks::run( 'AuthPluginAutoCreate', array( $user ) );
+               \Hooks::run( 'LocalUserCreated', array( $user, true ) );
+
+               # Notify AuthPlugin too
+               $tmpUser = $user;
+               $wgAuth->initUser( $tmpUser, true );
+               if ( $tmpUser !== $user ) {
+                       $logger->warning( __METHOD__ . ': ' .
+                               get_class( $wgAuth ) . '::initUser() replaced the user object' );
+               }
+
+               $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(), \WatchedItem::IGNORE_USER_RIGHTS );
+
+               return true;
+       }
+
+       /**
+        * Prevent future sessions for the user
+        *
+        * The intention is that the named account will never again be usable for
+        * normal login (i.e. there is no way to undo the prevention of access).
+        *
+        * @private For use from \\User::newSystemUser only
+        * @param string $username
+        */
+       public function preventSessionsForUser( $username ) {
+               $this->preventUsers[$username] = true;
+
+               // Reset the user's token to kill existing sessions
+               $user = User::newFromName( $username );
+               if ( $user && $user->getToken() ) {
+                       $user->setToken( true );
+                       $user->saveSettings();
+               }
+
+               // Instruct the session providers to kill any other sessions too.
+               foreach ( $this->getProviders() as $provider ) {
+                       $provider->preventSessionsForUser( $username );
+               }
+       }
+
+       /**
+        * Test if a user is prevented
+        * @private For use from SessionBackend only
+        * @param string $username
+        * @return bool
+        */
+       public function isUserSessionPrevented( $username ) {
+               return !empty( $this->preventUsers[$username] );
+       }
+
+       /**
+        * Get the available SessionProviders
+        * @return SessionProvider[]
+        */
+       protected function getProviders() {
+               if ( $this->sessionProviders === null ) {
+                       $this->sessionProviders = array();
+                       foreach ( $this->config->get( 'SessionProviders' ) as $spec ) {
+                               $provider = \ObjectFactory::getObjectFromSpec( $spec );
+                               $provider->setLogger( $this->logger );
+                               $provider->setConfig( $this->config );
+                               $provider->setManager( $this );
+                               if ( isset( $this->sessionProviders[(string)$provider] ) ) {
+                                       throw new \UnexpectedValueException( "Duplicate provider name \"$provider\"" );
+                               }
+                               $this->sessionProviders[(string)$provider] = $provider;
+                       }
+               }
+               return $this->sessionProviders;
+       }
+
+       /**
+        * Get a session provider by name
+        *
+        * Generally, this will only be used by internal implementation of some
+        * special session-providing mechanism. General purpose code, if it needs
+        * to access a SessionProvider at all, will use Session::getProvider().
+        *
+        * @param string $name
+        * @return SessionProvider|null
+        */
+       public function getProvider( $name ) {
+               $providers = $this->getProviders();
+               return isset( $providers[$name] ) ? $providers[$name] : null;
+       }
+
+       /**
+        * Save all active sessions on shutdown
+        * @private For internal use with register_shutdown_function()
+        */
+       public function shutdown() {
+               if ( $this->allSessionBackends ) {
+                       $this->logger->debug( 'Saving all sessions on shutdown' );
+                       if ( session_id() !== '' ) {
+                               // @codeCoverageIgnoreStart
+                               session_write_close();
+                       }
+                       // @codeCoverageIgnoreEnd
+                       foreach ( $this->allSessionBackends as $backend ) {
+                               $backend->save( true );
+                       }
+               }
+       }
+
+       /**
+        * Fetch the SessionInfo(s) for a request
+        * @param WebRequest $request
+        * @return SessionInfo|null
+        */
+       private function getSessionInfoForRequest( WebRequest $request ) {
+               // Call all providers to fetch "the" session
+               $infos = array();
+               foreach ( $this->getProviders() as $provider ) {
+                       $info = $provider->provideSessionInfo( $request );
+                       if ( !$info ) {
+                               continue;
+                       }
+                       if ( $info->getProvider() !== $provider ) {
+                               throw new \UnexpectedValueException(
+                                       "$provider returned session info for a different provider: $info"
+                               );
+                       }
+                       $infos[] = $info;
+               }
+
+               // Sort the SessionInfos. Then find the first one that can be
+               // successfully loaded, and then all the ones after it with the same
+               // priority.
+               usort( $infos, 'MediaWiki\\Session\\SessionInfo::compare' );
+               $retInfos = array();
+               while ( $infos ) {
+                       $info = array_pop( $infos );
+                       if ( $this->loadSessionInfoFromStore( $info, $request ) ) {
+                               $retInfos[] = $info;
+                               while ( $infos ) {
+                                       $info = array_pop( $infos );
+                                       if ( SessionInfo::compare( $retInfos[0], $info ) ) {
+                                               // We hit a lower priority, stop checking.
+                                               break;
+                                       }
+                                       if ( $this->loadSessionInfoFromStore( $info, $request ) ) {
+                                               // This is going to error out below, but we want to
+                                               // provide a complete list.
+                                               $retInfos[] = $info;
+                                       }
+                               }
+                       }
+               }
+
+               if ( count( $retInfos ) > 1 ) {
+                       $ex = new \OverflowException(
+                               'Multiple sessions for this request tied for top priority: ' . join( ', ', $retInfos )
+                       );
+                       $ex->sessionInfos = $retInfos;
+                       throw $ex;
+               }
+
+               return $retInfos ? $retInfos[0] : null;
+       }
+
+       /**
+        * Load and verify the session info against the store
+        *
+        * @param SessionInfo &$info Will likely be replaced with an updated SessionInfo instance
+        * @param WebRequest $request
+        * @return bool Whether the session info matches the stored data (if any)
+        */
+       private function loadSessionInfoFromStore( SessionInfo &$info, WebRequest $request ) {
+               $key = wfMemcKey( 'MWSession', $info->getId() );
+               $blob = $this->store->get( $key );
+
+               $newParams = array();
+
+               if ( $blob !== false ) {
+                       // Sanity check: blob must be an array, if it's saved at all
+                       if ( !is_array( $blob ) ) {
+                               $this->logger->warning( "Session $info: Bad data" );
+                               $this->store->delete( $key );
+                               return false;
+                       }
+
+                       // Sanity check: blob has data and metadata arrays
+                       if ( !isset( $blob['data'] ) || !is_array( $blob['data'] ) ||
+                               !isset( $blob['metadata'] ) || !is_array( $blob['metadata'] )
+                       ) {
+                               $this->logger->warning( "Session $info: Bad data structure" );
+                               $this->store->delete( $key );
+                               return false;
+                       }
+
+                       $data = $blob['data'];
+                       $metadata = $blob['metadata'];
+
+                       // Sanity check: metadata must be an array and must contain certain
+                       // keys, if it's saved at all
+                       if ( !array_key_exists( 'userId', $metadata ) ||
+                               !array_key_exists( 'userName', $metadata ) ||
+                               !array_key_exists( 'userToken', $metadata ) ||
+                               !array_key_exists( 'provider', $metadata )
+                       ) {
+                               $this->logger->warning( "Session $info: Bad metadata" );
+                               $this->store->delete( $key );
+                               return false;
+                       }
+
+                       // First, load the provider from metadata, or validate it against the metadata.
+                       $provider = $info->getProvider();
+                       if ( $provider === null ) {
+                               $newParams['provider'] = $provider = $this->getProvider( $metadata['provider'] );
+                               if ( !$provider ) {
+                                       $this->logger->warning( "Session $info: Unknown provider, " . $metadata['provider'] );
+                                       $this->store->delete( $key );
+                                       return false;
+                               }
+                       } elseif ( $metadata['provider'] !== (string)$provider ) {
+                               $this->logger->warning( "Session $info: Wrong provider, " .
+                                       $metadata['provider'] . ' !== ' . $provider );
+                               return false;
+                       }
+
+                       // Load provider metadata from metadata, or validate it against the metadata
+                       $providerMetadata = $info->getProviderMetadata();
+                       if ( isset( $metadata['providerMetadata'] ) ) {
+                               if ( $providerMetadata === null ) {
+                                       $newParams['metadata'] = $metadata['providerMetadata'];
+                               } else {
+                                       try {
+                                               $newProviderMetadata = $provider->mergeMetadata(
+                                                       $metadata['providerMetadata'], $providerMetadata
+                                               );
+                                               if ( $newProviderMetadata !== $providerMetadata ) {
+                                                       $newParams['metadata'] = $newProviderMetadata;
+                                               }
+                                       } catch ( \UnexpectedValueException $ex ) {
+                                               $this->logger->warning( "Session $info: Metadata merge failed: " . $ex->getMessage() );
+                                               return false;
+                                       }
+                               }
+                       }
+
+                       // Next, load the user from metadata, or validate it against the metadata.
+                       $userInfo = $info->getUserInfo();
+                       if ( !$userInfo ) {
+                               // For loading, id is preferred to name.
+                               try {
+                                       if ( $metadata['userId'] ) {
+                                               $userInfo = UserInfo::newFromId( $metadata['userId'] );
+                                       } elseif ( $metadata['userName'] !== null ) { // Shouldn't happen, but just in case
+                                               $userInfo = UserInfo::newFromName( $metadata['userName'] );
+                                       } else {
+                                               $userInfo = UserInfo::newAnonymous();
+                                       }
+                               } catch ( \InvalidArgumentException $ex ) {
+                                       $this->logger->error( "Session $info: " . $ex->getMessage() );
+                                       return false;
+                               }
+                               $newParams['userInfo'] = $userInfo;
+                       } else {
+                               // User validation passes if user ID matches, or if there
+                               // is no saved ID and the names match.
+                               if ( $metadata['userId'] ) {
+                                       if ( $metadata['userId'] !== $userInfo->getId() ) {
+                                               $this->logger->warning( "Session $info: User ID mismatch, " .
+                                                       $metadata['userId'] . ' !== ' . $userInfo->getId() );
+                                               return false;
+                                       }
+
+                                       // If the user was renamed, probably best to fail here.
+                                       if ( $metadata['userName'] !== null &&
+                                               $userInfo->getName() !== $metadata['userName']
+                                       ) {
+                                               $this->logger->warning( "Session $info: User ID matched but name didn't (rename?), " .
+                                                       $metadata['userName'] . ' !== ' . $userInfo->getName() );
+                                               return false;
+                                       }
+
+                               } elseif ( $metadata['userName'] !== null ) { // Shouldn't happen, but just in case
+                                       if ( $metadata['userName'] !== $userInfo->getName() ) {
+                                               $this->logger->warning( "Session $info: User name mismatch, " .
+                                                       $metadata['userName'] . ' !== ' . $userInfo->getName() );
+                                               return false;
+                                       }
+                               } elseif ( !$userInfo->isAnon() ) {
+                                       // Metadata specifies an anonymous user, but the passed-in
+                                       // user isn't anonymous.
+                                       $this->logger->warning(
+                                               "Session $info: Metadata has an anonymous user, " .
+                                                       'but a non-anon user was provided'
+                                       );
+                                       return false;
+                               }
+                       }
+
+                       // And if we have a token in the metadata, it must match the loaded/provided user.
+                       if ( $metadata['userToken'] !== null &&
+                               $userInfo->getToken() !== $metadata['userToken']
+                       ) {
+                               $this->logger->warning( "Session $info: User token mismatch" );
+                               return false;
+                       }
+                       if ( !$userInfo->isVerified() ) {
+                               $newParams['userInfo'] = $userInfo->verified();
+                       }
+
+                       if ( !empty( $metadata['remember'] ) && !$info->wasRemembered() ) {
+                               $newParams['remembered'] = true;
+                       }
+                       if ( !empty( $metadata['forceHTTPS'] ) && !$info->forceHTTPS() ) {
+                               $newParams['forceHTTPS'] = true;
+                       }
+
+                       if ( !$info->isIdSafe() ) {
+                               $newParams['idIsSafe'] = true;
+                       }
+               } else {
+                       // No metadata, so we can't load the provider if one wasn't given.
+                       if ( $info->getProvider() === null ) {
+                               $this->logger->warning( "Session $info: Null provider and no metadata" );
+                               return false;
+                       }
+
+                       // If no user was provided and no metadata, it must be anon.
+                       if ( !$info->getUserInfo() ) {
+                               if ( $info->getProvider()->canChangeUser() ) {
+                                       $newParams['userInfo'] = UserInfo::newAnonymous();
+                               } else {
+                                       $this->logger->info(
+                                               "Session $info: No user provided and provider cannot set user"
+                                       );
+                                       return false;
+                               }
+                       } elseif ( !$info->getUserInfo()->isVerified() ) {
+                               $this->logger->warning(
+                                       "Session $info: Unverified user provided and no metadata to auth it"
+                               );
+                               return false;
+                       }
+
+                       $data = false;
+                       $metadata = false;
+
+                       if ( !$info->getProvider()->persistsSessionId() && !$info->isIdSafe() ) {
+                               // The ID doesn't come from the user, so it should be safe
+                               // (and if not, nothing we can do about it anyway)
+                               $newParams['idIsSafe'] = true;
+                       }
+               }
+
+               // Construct the replacement SessionInfo, if necessary
+               if ( $newParams ) {
+                       $newParams['copyFrom'] = $info;
+                       $info = new SessionInfo( $info->getPriority(), $newParams );
+               }
+
+               // Allow the provider to check the loaded SessionInfo
+               $providerMetadata = $info->getProviderMetadata();
+               if ( !$info->getProvider()->refreshSessionInfo( $info, $request, $providerMetadata ) ) {
+                       return false;
+               }
+               if ( $providerMetadata !== $info->getProviderMetadata() ) {
+                       $info = new SessionInfo( $info->getPriority(), array(
+                               'metadata' => $providerMetadata,
+                               'copyFrom' => $info,
+                       ) );
+               }
+
+               // Give hooks a chance to abort. Combined with the SessionMetadata
+               // hook, this can allow for tying a session to an IP address or the
+               // like.
+               $reason = 'Hook aborted';
+               if ( !\Hooks::run(
+                       'SessionCheckInfo',
+                       array( &$reason, $info, $request, $metadata, $data )
+               ) ) {
+                       $this->logger->warning( "Session $info: $reason" );
+                       return false;
+               }
+
+               return true;
+       }
+
+       /**
+        * Create a session corresponding to the passed SessionInfo
+        * @private For use by a SessionProvider that needs to specially create its
+        *  own session.
+        * @param SessionInfo $info
+        * @param WebRequest $request
+        * @return Session
+        */
+       public function getSessionFromInfo( SessionInfo $info, WebRequest $request ) {
+               $id = $info->getId();
+
+               if ( !isset( $this->allSessionBackends[$id] ) ) {
+                       if ( !isset( $this->allSessionIds[$id] ) ) {
+                               $this->allSessionIds[$id] = new SessionId( $id );
+                       }
+                       $backend = new SessionBackend(
+                               $this->allSessionIds[$id],
+                               $info,
+                               $this->store,
+                               $this->logger,
+                               $this->config->get( 'ObjectCacheSessionExpiry' )
+                       );
+                       $this->allSessionBackends[$id] = $backend;
+                       $delay = $backend->delaySave();
+               } else {
+                       $backend = $this->allSessionBackends[$id];
+                       $delay = $backend->delaySave();
+                       if ( $info->wasPersisted() ) {
+                               $backend->persist();
+                       }
+                       if ( $info->wasRemembered() ) {
+                               $backend->setRememberUser( true );
+                       }
+               }
+
+               $request->setSessionId( $backend->getSessionId() );
+               $session = $backend->getSession( $request );
+
+               if ( !$info->isIdSafe() ) {
+                       $session->resetId();
+               }
+
+               \ScopedCallback::consume( $delay );
+               return $session;
+       }
+
+       /**
+        * Deregister a SessionBackend
+        * @private For use from \\MediaWiki\\Session\\SessionBackend only
+        * @param SessionBackend $backend
+        */
+       public function deregisterSessionBackend( SessionBackend $backend ) {
+               $id = $backend->getId();
+               if ( !isset( $this->allSessionBackends[$id] ) || !isset( $this->allSessionIds[$id] ) ||
+                       $this->allSessionBackends[$id] !== $backend ||
+                       $this->allSessionIds[$id] !== $backend->getSessionId()
+               ) {
+                       throw new \InvalidArgumentException( 'Backend was not registered with this SessionManager' );
+               }
+
+               unset( $this->allSessionBackends[$id] );
+               // Explicitly do not unset $this->allSessionIds[$id]
+       }
+
+       /**
+        * Change a SessionBackend's ID
+        * @private For use from \\MediaWiki\\Session\\SessionBackend only
+        * @param SessionBackend $backend
+        */
+       public function changeBackendId( SessionBackend $backend ) {
+               $sessionId = $backend->getSessionId();
+               $oldId = (string)$sessionId;
+               if ( !isset( $this->allSessionBackends[$oldId] ) || !isset( $this->allSessionIds[$oldId] ) ||
+                       $this->allSessionBackends[$oldId] !== $backend ||
+                       $this->allSessionIds[$oldId] !== $sessionId
+               ) {
+                       throw new \InvalidArgumentException( 'Backend was not registered with this SessionManager' );
+               }
+
+               $newId = $this->generateSessionId();
+
+               unset( $this->allSessionBackends[$oldId], $this->allSessionIds[$oldId] );
+               $sessionId->setId( $newId );
+               $this->allSessionBackends[$newId] = $backend;
+               $this->allSessionIds[$newId] = $sessionId;
+       }
+
+       /**
+        * Generate a new random session ID
+        * @return string
+        */
+       public function generateSessionId() {
+               do {
+                       $id = wfBaseConvert( \MWCryptRand::generateHex( 40 ), 16, 32, 32 );
+                       $key = wfMemcKey( 'MWSession', $id );
+               } while ( isset( $this->allSessionIds[$id] ) || is_array( $this->store->get( $key ) ) );
+               return $id;
+       }
+
+       /**
+        * Call setters on a PHPSessionHandler
+        * @private Use PhpSessionHandler::install()
+        * @param PHPSessionHandler $handler
+        */
+       public function setupPHPSessionHandler( PHPSessionHandler $handler ) {
+               $handler->setManager( $this, $this->store, $this->logger );
+       }
+
+       /**
+        * Reset the internal caching for unit testing
+        */
+       public static function resetCache() {
+               if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+                       // @codeCoverageIgnoreStart
+                       throw new MWException( __METHOD__ . ' may only be called from unit tests!' );
+                       // @codeCoverageIgnoreEnd
+               }
+
+               self::$globalSession = null;
+               self::$globalSessionRequest = null;
+       }
+
+       /**@}*/
+
+}
diff --git a/includes/session/SessionManagerInterface.php b/includes/session/SessionManagerInterface.php
new file mode 100644 (file)
index 0000000..d58d3b9
--- /dev/null
@@ -0,0 +1,110 @@
+<?php
+/**
+ * MediaWiki\Session entry point interface
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LoggerAwareInterface;
+use WebRequest;
+
+/**
+ * This exists to make IDEs happy, so they don't see the
+ * internal-but-required-to-be-public methods on SessionManager.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+interface SessionManagerInterface extends LoggerAwareInterface {
+       /**
+        * Fetch the persisted session ID in a request.
+        *
+        * Note this is not the same thing as whether the session associated with
+        * the request is currently persistent, as the session might have been
+        * first made persistent during this request.
+        *
+        * @param WebRequest $request
+        * @return string|null
+        * @throws \\OverflowException if there are multiple sessions tied for top
+        *  priority in the request. Exception has a property "sessionInfos"
+        *  holding the SessionInfo objects for the sessions involved.
+        */
+       public function getPersistedSessionId( WebRequest $request );
+
+       /**
+        * Fetch the session for a request
+        *
+        * @note You probably want to use $request->getSession() instead. It's more
+        *  efficient and doesn't break FauxRequests or sessions that were changed
+        *  by $this->getSessionById() or $this->getEmptySession().
+        * @param WebRequest $request Any existing associated session will be reset
+        *  to the session corresponding to the data in the request itself.
+        * @return Session
+        * @throws \\OverflowException if there are multiple sessions tied for top
+        *  priority in the request. Exception has a property "sessionInfos"
+        *  holding the SessionInfo objects for the sessions involved.
+        */
+       public function getSessionForRequest( WebRequest $request );
+
+       /**
+        * 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.
+        * @param WebRequest|null $request Corresponding request. Any existing
+        *  session associated with this WebRequest object will be overwritten.
+        * @return Session|null
+        */
+       public function getSessionById( $id, $create = false, WebRequest $request = null );
+
+       /**
+        * Fetch a new, empty session
+        *
+        * The first provider configured that is able to provide an empty session
+        * will be used.
+        *
+        * @param WebRequest|null $request Corresponding request. Any existing
+        *  session associated with this WebRequest object will be overwritten.
+        * @return Session
+        */
+       public function getEmptySession( WebRequest $request = null );
+
+       /**
+        * Return the HTTP headers that need varying on.
+        *
+        * The return value is such that someone could theoretically do this:
+        * @code
+        *  foreach ( $provider->getVaryHeaders() as $header => $options ) {
+        *      $outputPage->addVaryHeader( $header, $options );
+        *  }
+        * @endcode
+        *
+        * @return array
+        */
+       public function getVaryHeaders();
+
+       /**
+        * Return the list of cookies that need varying on.
+        * @return string[]
+        */
+       public function getVaryCookies();
+
+}
diff --git a/includes/session/SessionProvider.php b/includes/session/SessionProvider.php
new file mode 100644 (file)
index 0000000..0fd3a71
--- /dev/null
@@ -0,0 +1,487 @@
+<?php
+/**
+ * MediaWiki session provider base class
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ *
+ * @file
+ * @ingroup Session
+ */
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LoggerAwareInterface;
+use Psr\Log\LoggerInterface;
+use Config;
+use Language;
+use WebRequest;
+
+/**
+ * A SessionProvider provides SessionInfo and support for Session
+ *
+ * A SessionProvider is responsible for taking a WebRequest and determining
+ * the authenticated session that it's a part of. It does this by returning an
+ * SessionInfo object with basic information about the session it thinks is
+ * associated with the request, namely the session ID and possibly the
+ * authenticated user the session belongs to.
+ *
+ * The SessionProvider also provides for updating the WebResponse with
+ * information necessary to provide the client with data that the client will
+ * send with later requests, and for populating the Vary and Key headers with
+ * the data necessary to correctly vary the cache on these client requests.
+ *
+ * An important part of the latter is indicating whether it even *can* tell the
+ * client to include such data in future requests, via the persistsSessionId()
+ * and canChangeUser() methods. The cases are (in order of decreasing
+ * commonness):
+ *  - Cannot persist ID, no changing User: The request identifies and
+ *    authenticates a particular local user, and the client cannot be
+ *    instructed to include an arbitrary session ID with future requests. For
+ *    example, OAuth or SSL certificate auth.
+ *  - Can persist ID and can change User: The client can be instructed to
+ *    return at least one piece of arbitrary data, that being the session ID.
+ *    The user identity might also be given to the client, otherwise it's saved
+ *    in the session data. For example, cookie-based sessions.
+ *  - Can persist ID but no changing User: The request uniquely identifies and
+ *    authenticates a local user, and the client can be instructed to return an
+ *    arbitrary session ID with future requests. For example, HTTP Digest
+ *    authentication might somehow use the 'opaque' field as a session ID
+ *    (although getting MediaWiki to return 401 responses without breaking
+ *    other stuff might be a challenge).
+ *  - Cannot persist ID but can change User: I can't think of a way this
+ *    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,
+ * 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
+ */
+abstract class SessionProvider implements SessionProviderInterface, LoggerAwareInterface {
+
+       /** @var LoggerInterface */
+       protected $logger;
+
+       /** @var Config */
+       protected $config;
+
+       /** @var SessionManager */
+       protected $manager;
+
+       /** @var int Session priority. Used for the default newSessionInfo(), but
+        * could be used by subclasses too.
+        */
+       protected $priority;
+
+       /**
+        * @note To fully initialize a SessionProvider, the setLogger(),
+        *  setConfig(), and setManager() methods must be called (and should be
+        *  called in that order). Failure to do so is liable to cause things to
+        *  fail unexpectedly.
+        */
+       public function __construct() {
+               $this->priority = SessionInfo::MIN_PRIORITY + 10;
+       }
+
+       public function setLogger( LoggerInterface $logger ) {
+               $this->logger = $logger;
+       }
+
+       /**
+        * Set configuration
+        * @param Config $config
+        */
+       public function setConfig( Config $config ) {
+               $this->config = $config;
+       }
+
+       /**
+        * Set the session manager
+        * @param SessionManager $manager
+        */
+       public function setManager( SessionManager $manager ) {
+               $this->manager = $manager;
+       }
+
+       /**
+        * Get the session manager
+        * @return SessionManager
+        */
+       public function getManager() {
+               return $this->manager;
+       }
+
+       /**
+        * Provide session info for a request
+        *
+        * If no session exists for the request, return null. Otherwise return an
+        * SessionInfo object identifying the session.
+        *
+        * If multiple SessionProviders provide sessions, the one with highest
+        * priority wins. In case of a tie, an exception is thrown.
+        * SessionProviders are encouraged to make priorities user-configurable
+        * unless only max-priority makes sense.
+        *
+        * @warning This will be called early in the MediaWiki setup process,
+        *  before $wgUser, $wgLang, $wgOut, $wgParser, $wgTitle, and corresponding
+        *  pieces of the main RequestContext are set up! If you try to use these,
+        *  things *will* break.
+        * @note The SessionProvider must not attempt to auto-create users.
+        *  MediaWiki will do this later (when it's safe) if the chosen session has
+        *  a user with a valid name but no ID.
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @param WebRequest $request
+        * @return SessionInfo|null
+        */
+       abstract public function provideSessionInfo( WebRequest $request );
+
+       /**
+        * Provide session info for a new, empty session
+        *
+        * Return null if such a session cannot be created. This base
+        * implementation assumes that it only makes sense if a session ID can be
+        * persisted and changing users is allowed.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @param string|null $id ID to force for the new session
+        * @return SessionInfo|null
+        *  If non-null, must return true for $info->isIdSafe(); pass true for
+        *  $data['idIsSafe'] to ensure this.
+        */
+       public function newSessionInfo( $id = null ) {
+               if ( $this->canChangeUser() && $this->persistsSessionId() ) {
+                       return new SessionInfo( $this->priority, array(
+                               'id' => $id,
+                               'provider' => $this,
+                               'persisted' => false,
+                               'idIsSafe' => true,
+                       ) );
+               }
+               return null;
+       }
+
+       /**
+        * Merge saved session provider metadata
+        *
+        * 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
+        * @return array Resulting metadata
+        * @throws \UnexpectedValueException If the metadata cannot be merged
+        */
+       public function mergeMetadata( array $savedMetadata, array $providedMetadata ) {
+               foreach ( $providedMetadata as $k => $v ) {
+                       if ( array_key_exists( $k, $savedMetadata ) && $savedMetadata[$k] !== $v ) {
+                               throw new \UnexpectedValueException( "Key \"$k\" changed" );
+                       }
+               }
+               return $providedMetadata;
+       }
+
+       /**
+        * Validate a loaded SessionInfo and refresh provider metadata
+        *
+        * This is similar in purpose to the 'SessionCheckInfo' hook, and also
+        * allows for updating the provider metadata. On failure, the provider is
+        * expected to write an appropriate message to its logger.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @param SessionInfo $info
+        * @param WebRequest $request
+        * @param array|null &$metadata Provider metadata, may be altered.
+        * @return bool Return false to reject the SessionInfo after all.
+        */
+       public function refreshSessionInfo( SessionInfo $info, WebRequest $request, &$metadata ) {
+               return true;
+       }
+
+       /**
+        * Indicate whether self::persistSession() can save arbitrary session IDs
+        *
+        * If false, any session passed to self::persistSession() will have an ID
+        * that was originally provided by self::provideSessionInfo().
+        *
+        * If true, the provider may be passed sessions with arbitrary session IDs,
+        * and will be expected to manipulate the request in such a way that future
+        * requests will cause self::provideSessionInfo() to provide a SessionInfo
+        * with that ID.
+        *
+        * For example, a session provider for OAuth would function by matching the
+        * OAuth headers to a particular user, and then would use self::hashToSessionId()
+        * to turn the user and OAuth client ID (and maybe also the user token and
+        * client secret) into a session ID, and therefore can't easily assign that
+        * user+client a different ID. Similarly, a session provider for SSL client
+        * certificates would function by matching the certificate to a particular
+        * user, and then would use self::hashToSessionId() to turn the user and
+        * certificate fingerprint into a session ID, and therefore can't easily
+        * assign a different ID either. On the other hand, a provider that saves
+        * the session ID into a cookie can easily just set the cookie to a
+        * different value.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionBackend only
+        * @return bool
+        */
+       abstract public function persistsSessionId();
+
+       /**
+        * Indicate whether the user associated with the request can be changed
+        *
+        * If false, any session passed to self::persistSession() will have a user
+        * that was originally provided by self::provideSessionInfo(). Further,
+        * self::provideSessionInfo() may only provide sessions that have a user
+        * already set.
+        *
+        * If true, the provider may be passed sessions with arbitrary users, and
+        * will be expected to manipulate the request in such a way that future
+        * requests will cause self::provideSessionInfo() to provide a SessionInfo
+        * with that ID. This can be as simple as not passing any 'userInfo' into
+        * SessionInfo's constructor, in which case SessionInfo will load the user
+        * from the saved session's metadata.
+        *
+        * For example, a session provider for OAuth or SSL client certificates
+        * would function by matching the OAuth headers or certificate to a
+        * particular user, and thus would return false here since it can't
+        * arbitrarily assign those OAuth credentials or that certificate to a
+        * different user. A session provider that shoves information into cookies,
+        * on the other hand, could easily do so.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionBackend only
+        * @return bool
+        */
+       abstract public function canChangeUser();
+
+       /**
+        * Notification that the session ID was reset
+        *
+        * No need to persist here, persistSession() will be called if appropriate.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionBackend only
+        * @param SessionBackend $session Session to persist
+        * @param string $oldId Old session ID
+        * @codeCoverageIgnore
+        */
+       public function sessionIdWasReset( SessionBackend $session, $oldId ) {
+       }
+
+       /**
+        * Persist a session into a request/response
+        *
+        * For example, you might set cookies for the session's ID, user ID, user
+        * name, and user token on the passed request.
+        *
+        * To correctly persist a user independently of the session ID, the
+        * provider should persist both the user ID (or name, but preferably the
+        * ID) and the user token. When reading the data from the request, it
+        * should construct a User object from the ID/name and then verify that the
+        * User object's token matches the token included in the request. Should
+        * the tokens not match, an anonymous user *must* be passed to
+        * SessionInfo::__construct().
+        *
+        * When persisting a user independently of the session ID,
+        * $session->shouldRememberUser() should be checked first. If this returns
+        * false, the user token *must not* be saved to cookies. The user name
+        * and/or ID may be persisted, and should be used to construct an
+        * unverified UserInfo to pass to SessionInfo::__construct().
+        *
+        * A backend that cannot persist sesison ID or user info should implement
+        * this as a no-op.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionBackend only
+        * @param SessionBackend $session Session to persist
+        * @param WebRequest $request Request into which to persist the session
+        */
+       abstract public function persistSession( SessionBackend $session, WebRequest $request );
+
+       /**
+        * Remove any persisted session from a request/response
+        *
+        * For example, blank and expire any cookies set by self::persistSession().
+        *
+        * A backend that cannot persist sesison ID or user info should implement
+        * this as a no-op.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @param WebRequest $request Request from which to remove any session data
+        */
+       abstract public function unpersistSession( WebRequest $request );
+
+       /**
+        * Prevent future sessions for the user
+        *
+        * If the provider is capable of returning a SessionInfo with a verified
+        * UserInfo for the named user in some manner other than by validating
+        * against $user->getToken(), steps must be taken to prevent that from
+        * occurring in the future. This might add the username to a blacklist, or
+        * it might just delete whatever authentication credentials would allow
+        * such a session in the first place (e.g. remove all OAuth grants or
+        * delete record of the SSL client certificate).
+        *
+        * The intention is that the named account will never again be usable for
+        * normal login (i.e. there is no way to undo the prevention of access).
+        *
+        * Note that the passed user name might not exist locally (i.e.
+        * User::idFromName( $username ) === 0); the name should still be
+        * prevented, if applicable.
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @param string $username
+        */
+       public function preventSessionsForUser( $username ) {
+               if ( !$this->canChangeUser() ) {
+                       throw new \BadMethodCallException(
+                               __METHOD__ . ' must be implmented when canChangeUser() is false'
+                       );
+               }
+       }
+
+       /**
+        * Return the HTTP headers that need varying on.
+        *
+        * The return value is such that someone could theoretically do this:
+        * @code
+        *  foreach ( $provider->getVaryHeaders() as $header => $options ) {
+        *      $outputPage->addVaryHeader( $header, $options );
+        *  }
+        * @endcode
+        *
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @return array
+        */
+       public function getVaryHeaders() {
+               return array();
+       }
+
+       /**
+        * Return the list of cookies that need varying on.
+        * @protected For use by \\MediaWiki\\Session\\SessionManager only
+        * @return string[]
+        */
+       public function getVaryCookies() {
+               return array();
+       }
+
+       /**
+        * Get a suggested username for the login form
+        * @protected For use by \\MediaWiki\\Session\\SessionBackend only
+        * @param WebRequest $request
+        * @return string|null
+        */
+       public function suggestLoginUsername( WebRequest $request ) {
+               return null;
+       }
+
+       /**
+        * Fetch the rights allowed the user when the specified session is active.
+        * @param SessionBackend $backend
+        * @return null|string[] Allowed user rights, or null to allow all.
+        */
+       public function getAllowedUserRights( SessionBackend $backend ) {
+               if ( $backend->getProvider() !== $this ) {
+                       // Not that this should ever happen...
+                       throw new \InvalidArgumentException( 'Backend\'s provider isn\'t $this' );
+               }
+
+               return null;
+       }
+
+       /**
+        * @note Only override this if it makes sense to instantiate multiple
+        *  instances of the provider. Value returned must be unique across
+        *  configured providers. If you override this, you'll likely need to
+        *  override self::describeMessage() as well.
+        * @return string
+        */
+       public function __toString() {
+               return get_class( $this );
+       }
+
+       /**
+        * Return a Message identifying this session type
+        *
+        * This default implementation takes the class name, lowercases it,
+        * replaces backslashes with dashes, and prefixes 'sessionprovider-' to
+        * determine the message key. For example, MediaWiki\\Session\\CookieSessionProvider
+        * produces 'sessionprovider-mediawiki-session-cookiesessionprovider'.
+        *
+        * @note If self::__toString() is overridden, this will likely need to be
+        *  overridden as well.
+        * @warning This will be called early during MediaWiki startup. Do not
+        *  use $wgUser, $wgLang, $wgOut, $wgParser, or their equivalents via
+        *  RequestContext from this method!
+        * @return Message
+        */
+       protected function describeMessage() {
+               return wfMessage(
+                       'sessionprovider-' . str_replace( '\\', '-', strtolower( get_class( $this ) ) )
+               );
+       }
+
+       public function describe( Language $lang ) {
+               $msg = $this->describeMessage();
+               $msg->inLanguage( $lang );
+               if ( $msg->isDisabled() ) {
+                       $msg = wfMessage( 'sessionprovider-generic', (string)$this )->inLanguage( $lang );
+               }
+               return $msg->plain();
+       }
+
+       public function whyNoSession() {
+               return null;
+       }
+
+       /**
+        * Hash data as a session ID
+        *
+        * Generally this will only be used when self::persistsSessionId() is false and
+        * the provider has to base the session ID on the verified user's identity
+        * or other static data.
+        *
+        * @param string $data
+        * @param string|null $key Defaults to $this->config->get( 'SecretKey' )
+        * @return string
+        */
+       final protected function hashToSessionId( $data, $key = null ) {
+               if ( !is_string( $data ) ) {
+                       throw new \InvalidArgumentException(
+                               '$data must be a string, ' . gettype( $data ) . ' was passed'
+                       );
+               }
+               if ( $key !== null && !is_string( $key ) ) {
+                       throw new \InvalidArgumentException(
+                               '$key must be a string or null, ' . gettype( $key ) . ' was passed'
+                       );
+               }
+
+               $hash = \MWCryptHash::hmac( "$this\n$data", $key ?: $this->config->get( 'SecretKey' ), false );
+               if ( strlen( $hash ) < 32 ) {
+                       // Should never happen, even md5 is 128 bits
+                       // @codeCoverageIgnoreStart
+                       throw new \UnexpectedValueException( 'Hash fuction returned less than 128 bits' );
+                       // @codeCoverageIgnoreEnd
+               }
+               if ( strlen( $hash ) >= 40 ) {
+                       $hash = wfBaseConvert( $hash, 16, 32, 32 );
+               }
+               return substr( $hash, -32 );
+       }
+
+}
diff --git a/includes/session/SessionProviderInterface.php b/includes/session/SessionProviderInterface.php
new file mode 100644 (file)
index 0000000..02ae23d
--- /dev/null
@@ -0,0 +1,54 @@
+<?php
+/**
+ * MediaWiki\Session\Provider interface
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use Language;
+
+/**
+ * This exists to make IDEs happy, so they don't see the
+ * internal-but-required-to-be-public methods on SessionProvider.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+interface SessionProviderInterface {
+
+       /**
+        * Return an identifier for this session type
+        *
+        * @param Language $lang Language to use.
+        * @return string
+        */
+       public function describe( Language $lang );
+
+       /**
+        * Return a Message for why sessions might not be being persisted.
+        *
+        * For example, "check whether you're blocking our cookies".
+        *
+        * @return Message|null
+        */
+       public function whyNoSession();
+
+}
diff --git a/includes/session/UserInfo.php b/includes/session/UserInfo.php
new file mode 100644 (file)
index 0000000..e844bb6
--- /dev/null
@@ -0,0 +1,187 @@
+<?php
+/**
+ * MediaWiki session user info
+ *
+ * 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 Session
+ */
+
+namespace MediaWiki\Session;
+
+use User;
+
+/**
+ * Object holding data about a session's user
+ *
+ * In general, this class exists for two purposes:
+ * - User doesn't distinguish between "anonymous user" and "non-anonymous user
+ *   that doesn't exist locally", while we do need to.
+ * - We also need the "verified" property described below; tracking it via
+ *   another data item to SessionInfo's constructor makes things much more
+ *   confusing.
+ *
+ * A UserInfo may be "verified". This indicates that the creator knows that the
+ * request really comes from that user, whether that's by validating OAuth
+ * credentials, SSL client certificates, or by having both the user ID and
+ * token available from cookies.
+ *
+ * An "unverified" UserInfo should be used when it's not possible to
+ * authenticate the user, e.g. the user ID cookie is set but the user Token
+ * cookie isn't. If the Token is available but doesn't match, don't return a
+ * UserInfo at all.
+ *
+ * @ingroup Session
+ * @since 1.27
+ */
+final class UserInfo {
+       private $verified = false;
+
+       /** @var User|null */
+       private $user = null;
+
+       private function __construct( User $user = null, $verified ) {
+               if ( $user && $user->isAnon() && !User::isUsableName( $user->getName() ) ) {
+                       $this->verified = true;
+                       $this->user = null;
+               } else {
+                       $this->verified = $verified;
+                       $this->user = $user;
+               }
+       }
+
+       /**
+        * Create an instance for an anonymous (i.e. not logged in) user
+        *
+        * Logged-out users are always "verified".
+        *
+        * @return UserInfo
+        */
+       public static function newAnonymous() {
+               return new self( null, true );
+       }
+
+       /**
+        * Create an instance for a logged-in user by ID
+        * @param int $id User ID
+        * @param bool $verified True if the user is verified
+        * @return UserInfo
+        */
+       public static function newFromId( $id, $verified = false ) {
+               $user = User::newFromId( $id );
+
+               // Ensure the ID actually exists
+               $user->load();
+               if ( $user->isAnon() ) {
+                       throw new \InvalidArgumentException( 'Invalid ID' );
+               }
+
+               return new self( $user, $verified );
+       }
+
+       /**
+        * Create an instance for a logged-in user by name
+        * @param string $name User name (need not exist locally)
+        * @param bool $verified True if the user is verified
+        * @return UserInfo
+        */
+       public static function newFromName( $name, $verified = false ) {
+               $user = User::newFromName( $name, 'usable' );
+               if ( !$user ) {
+                       throw new \InvalidArgumentException( 'Invalid user name' );
+               }
+               return new self( $user, $verified );
+       }
+
+       /**
+        * Create an instance from an existing User object
+        * @param User $user (need not exist locally)
+        * @param bool $verified True if the user is verified
+        * @return UserInfo
+        */
+       public static function newFromUser( User $user, $verified = false ) {
+               return new self( $user, $verified );
+       }
+
+       /**
+        * Return whether this is an anonymous user
+        * @return bool
+        */
+       public function isAnon() {
+               return $this->user === null;
+       }
+
+       /**
+        * Return whether this represents a verified user
+        * @return bool
+        */
+       public function isVerified() {
+               return $this->verified;
+       }
+
+       /**
+        * Return the user ID
+        * @note Do not use this to test for anonymous users!
+        * @return int
+        */
+       public function getId() {
+               return $this->user === null ? 0 : $this->user->getId();
+       }
+
+       /**
+        * Return the user name
+        * @return string|null
+        */
+       public function getName() {
+               return $this->user === null ? null : $this->user->getName();
+       }
+
+       /**
+        * Return the user token
+        * @return string|null
+        */
+       public function getToken() {
+               return $this->user === null || $this->user->getId() === 0 ? null : $this->user->getToken( true );
+       }
+
+       /**
+        * Return a User object
+        * @return User
+        */
+       public function getUser() {
+               return $this->user === null ? new User : $this->user;
+       }
+
+       /**
+        * Return a verified version of this object
+        * @return UserInfo
+        */
+       public function verified() {
+               return $this->verified ? $this : new self( $this->user, true );
+       }
+
+       public function __toString() {
+               if ( $this->user === null ) {
+                       return '<anon>';
+               }
+               return '<' .
+                       ( $this->verified ? '+' : '-' ) . ':' .
+                       $this->getId() . ':' . $this->getName() .
+                       '>';
+       }
+
+}
diff --git a/includes/site/MediaWikiPageNameNormalizer.php b/includes/site/MediaWikiPageNameNormalizer.php
new file mode 100644 (file)
index 0000000..f358bd4
--- /dev/null
@@ -0,0 +1,196 @@
+<?php
+
+namespace MediaWiki\Site;
+
+use FormatJson;
+use Http;
+use UtfNormal\Validator;
+
+/**
+ * Service for normalizing a page name using a MediaWiki api.
+ *
+ * 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.27
+ *
+ * @license GNU GPL v2+
+ * @author John Erling Blad < jeblad@gmail.com >
+ * @author Daniel Kinzler
+ * @author Jeroen De Dauw < jeroendedauw@gmail.com >
+ * @author Marius Hoch
+ */
+class MediaWikiPageNameNormalizer {
+
+       /**
+        * Returns the normalized form of the given page title, using the
+        * normalization rules of the given site. If the given title is a redirect,
+        * the redirect weill be resolved and the redirect target is returned.
+        *
+        * @note This actually makes an API request to the remote site, so beware
+        *   that this function is slow and depends on an external service.
+        *
+        * @see Site::normalizePageName
+        *
+        * @since 1.27
+        *
+        * @param string $pageName
+        * @param string $apiUrl
+        *
+        * @return string
+        * @throws \MWException
+        */
+       public function normalizePageName( $pageName, $apiUrl ) {
+
+               // Check if we have strings as arguments.
+               if ( !is_string( $pageName ) ) {
+                       throw new \MWException( '$pageName must be a string' );
+               }
+
+               // Go on call the external site
+
+               // Make sure the string is normalized into NFC (due to T42017)
+               // but do nothing to the whitespaces, that should work appropriately.
+               // @see https://phabricator.wikimedia.org/T42017
+               $pageName = Validator::cleanUp( $pageName );
+
+               // Build the args for the specific call
+               $args = array(
+                       'action' => 'query',
+                       'prop' => 'info',
+                       'redirects' => true,
+                       'converttitles' => true,
+                       'format' => 'json',
+                       'titles' => $pageName,
+                       // @todo options for maxlag and maxage
+                       // Note that maxlag will lead to a long delay before a reply is made,
+                       // but that maxage can avoid the extreme delay. On the other hand
+                       // maxage could be nice to use anyhow as it stops unnecessary requests.
+                       // Also consider smaxage if maxage is used.
+               );
+
+               $url = wfAppendQuery( $apiUrl, $args );
+
+               // Go on call the external site
+               // @todo we need a good way to specify a timeout here.
+               $ret = Http::get( $url, array(), __METHOD__ );
+
+               if ( $ret === false ) {
+                       wfDebugLog( "MediaWikiSite", "call to external site failed: $url" );
+                       return false;
+               }
+
+               $data = FormatJson::decode( $ret, true );
+
+               if ( !is_array( $data ) ) {
+                       wfDebugLog( "MediaWikiSite", "call to <$url> returned bad json: " . $ret );
+                       return false;
+               }
+
+               $page = static::extractPageRecord( $data, $pageName );
+
+               if ( isset( $page['missing'] ) ) {
+                       wfDebugLog( "MediaWikiSite", "call to <$url> returned a marker for a missing page title! "
+                               . $ret );
+                       return false;
+               }
+
+               if ( isset( $page['invalid'] ) ) {
+                       wfDebugLog( "MediaWikiSite", "call to <$url> returned a marker for an invalid page title! "
+                               . $ret );
+                       return false;
+               }
+
+               if ( !isset( $page['title'] ) ) {
+                       wfDebugLog( "MediaWikiSite", "call to <$url> did not return a page title! " . $ret );
+                       return false;
+               }
+
+               return $page['title'];
+       }
+
+       /**
+        * Get normalization record for a given page title from an API response.
+        *
+        * @param array $externalData A reply from the API on a external server.
+        * @param string $pageTitle Identifies the page at the external site, needing normalization.
+        *
+        * @return array|bool A 'page' structure representing the page identified by $pageTitle.
+        */
+       private static function extractPageRecord( $externalData, $pageTitle ) {
+               // If there is a special case with only one returned page
+               // we can cheat, and only return
+               // the single page in the "pages" substructure.
+               if ( isset( $externalData['query']['pages'] ) ) {
+                       $pages = array_values( $externalData['query']['pages'] );
+                       if ( count( $pages ) === 1 ) {
+                               return $pages[0];
+                       }
+               }
+               // This is only used during internal testing, as it is assumed
+               // a more optimal (and lossfree) storage.
+               // Make initial checks and return if prerequisites are not meet.
+               if ( !is_array( $externalData ) || !isset( $externalData['query'] ) ) {
+                       return false;
+               }
+               // Loop over the tree different named structures, that otherwise are similar
+               $structs = array(
+                       'normalized' => 'from',
+                       'converted' => 'from',
+                       'redirects' => 'from',
+                       'pages' => 'title'
+               );
+               foreach ( $structs as $listId => $fieldId ) {
+                       // Check if the substructure exist at all.
+                       if ( !isset( $externalData['query'][$listId] ) ) {
+                               continue;
+                       }
+                       // Filter the substructure down to what we actually are using.
+                       $collectedHits = array_filter(
+                               array_values( $externalData['query'][$listId] ),
+                               function ( $a ) use ( $fieldId, $pageTitle ) {
+                                       return $a[$fieldId] === $pageTitle;
+                               }
+                       );
+                       // If still looping over normalization, conversion or redirects,
+                       // then we need to keep the new page title for later rounds.
+                       if ( $fieldId === 'from' && is_array( $collectedHits ) ) {
+                               switch ( count( $collectedHits ) ) {
+                                       case 0:
+                                               break;
+                                       case 1:
+                                               $pageTitle = $collectedHits[0]['to'];
+                                               break;
+                                       default:
+                                               return false;
+                               }
+                       } elseif ( $fieldId === 'title' && is_array( $collectedHits ) ) {
+                               // If on the pages structure we should prepare for returning.
+
+                               switch ( count( $collectedHits ) ) {
+                                       case 0:
+                                               return false;
+                                       case 1:
+                                               return array_shift( $collectedHits );
+                                       default:
+                                               return false;
+                               }
+                       }
+               }
+               // should never be here
+               return false;
+       }
+
+}
index 029919c..0f7e5d7 100644 (file)
@@ -1,4 +1,7 @@
 <?php
+
+use MediaWiki\Site\MediaWikiPageNameNormalizer;
+
 /**
  * Class representing a MediaWiki site.
  *
@@ -96,13 +99,6 @@ class MediaWikiSite extends Site {
         * @throws MWException
         */
        public function normalizePageName( $pageName ) {
-
-               // Check if we have strings as arguments.
-               if ( !is_string( $pageName ) ) {
-                       throw new MWException( '$pageName must be a string' );
-               }
-
-               // Go on call the external site
                if ( defined( 'MW_PHPUNIT_TEST' ) ) {
                        // If the code is under test, don't call out to other sites, just
                        // normalize locally.
@@ -112,140 +108,17 @@ class MediaWikiSite extends Site {
                        $t = Title::newFromText( $pageName );
                        return $t->getPrefixedText();
                } else {
+                       static $mediaWikiPageNameNormalizer = null;
 
-                       // Make sure the string is normalized into NFC (due to T42017)
-                       // but do nothing to the whitespaces, that should work appropriately.
-                       // @see https://phabricator.wikimedia.org/T42017
-                       $pageName = UtfNormal\Validator::cleanUp( $pageName );
-
-                       // Build the args for the specific call
-                       $args = array(
-                               'action' => 'query',
-                               'prop' => 'info',
-                               'redirects' => true,
-                               'converttitles' => true,
-                               'format' => 'json',
-                               'titles' => $pageName,
-                               // @todo options for maxlag and maxage
-                               // Note that maxlag will lead to a long delay before a reply is made,
-                               // but that maxage can avoid the extreme delay. On the other hand
-                               // maxage could be nice to use anyhow as it stops unnecessary requests.
-                               // Also consider smaxage if maxage is used.
-                       );
-
-                       $url = wfAppendQuery( $this->getFileUrl( 'api.php' ), $args );
-
-                       // Go on call the external site
-                       // @todo we need a good way to specify a timeout here.
-                       $ret = Http::get( $url, array(), __METHOD__ );
-               }
-
-               if ( $ret === false ) {
-                       wfDebugLog( "MediaWikiSite", "call to external site failed: $url" );
-                       return false;
-               }
-
-               $data = FormatJson::decode( $ret, true );
-
-               if ( !is_array( $data ) ) {
-                       wfDebugLog( "MediaWikiSite", "call to <$url> returned bad json: " . $ret );
-                       return false;
-               }
-
-               $page = static::extractPageRecord( $data, $pageName );
-
-               if ( isset( $page['missing'] ) ) {
-                       wfDebugLog( "MediaWikiSite", "call to <$url> returned a marker for a missing page title! "
-                               . $ret );
-                       return false;
-               }
-
-               if ( isset( $page['invalid'] ) ) {
-                       wfDebugLog( "MediaWikiSite", "call to <$url> returned a marker for an invalid page title! "
-                               . $ret );
-                       return false;
-               }
-
-               if ( !isset( $page['title'] ) ) {
-                       wfDebugLog( "MediaWikiSite", "call to <$url> did not return a page title! " . $ret );
-                       return false;
-               }
-
-               return $page['title'];
-       }
-
-       /**
-        * Get normalization record for a given page title from an API response.
-        *
-        * @since 1.21
-        *
-        * @param array $externalData A reply from the API on a external server.
-        * @param string $pageTitle Identifies the page at the external site, needing normalization.
-        *
-        * @return array|bool A 'page' structure representing the page identified by $pageTitle.
-        */
-       private static function extractPageRecord( $externalData, $pageTitle ) {
-               // If there is a special case with only one returned page
-               // we can cheat, and only return
-               // the single page in the "pages" substructure.
-               if ( isset( $externalData['query']['pages'] ) ) {
-                       $pages = array_values( $externalData['query']['pages'] );
-                       if ( count( $pages ) === 1 ) {
-                               return $pages[0];
-                       }
-               }
-               // This is only used during internal testing, as it is assumed
-               // a more optimal (and lossfree) storage.
-               // Make initial checks and return if prerequisites are not meet.
-               if ( !is_array( $externalData ) || !isset( $externalData['query'] ) ) {
-                       return false;
-               }
-               // Loop over the tree different named structures, that otherwise are similar
-               $structs = array(
-                       'normalized' => 'from',
-                       'converted' => 'from',
-                       'redirects' => 'from',
-                       'pages' => 'title'
-               );
-               foreach ( $structs as $listId => $fieldId ) {
-                       // Check if the substructure exist at all.
-                       if ( !isset( $externalData['query'][$listId] ) ) {
-                               continue;
+                       if ( $mediaWikiPageNameNormalizer === null ) {
+                               $mediaWikiPageNameNormalizer = new MediaWikiPageNameNormalizer();
                        }
-                       // Filter the substructure down to what we actually are using.
-                       $collectedHits = array_filter(
-                               array_values( $externalData['query'][$listId] ),
-                               function ( $a ) use ( $fieldId, $pageTitle ) {
-                                       return $a[$fieldId] === $pageTitle;
-                               }
+
+                       return $mediaWikiPageNameNormalizer->normalizePageName(
+                               $pageName,
+                               $this->getFileUrl( 'api.php' )
                        );
-                       // If still looping over normalization, conversion or redirects,
-                       // then we need to keep the new page title for later rounds.
-                       if ( $fieldId === 'from' && is_array( $collectedHits ) ) {
-                               switch ( count( $collectedHits ) ) {
-                                       case 0:
-                                               break;
-                                       case 1:
-                                               $pageTitle = $collectedHits[0]['to'];
-                                               break;
-                                       default:
-                                               return false;
-                               }
-                       }
-                       // If on the pages structure we should prepare for returning.
-                       elseif ( $fieldId === 'title' && is_array( $collectedHits ) ) {
-                               switch ( count( $collectedHits ) ) {
-                                       case 0:
-                                               return false;
-                                       case 1:
-                                               return array_shift( $collectedHits );
-                                       default:
-                                               return false;
-                               }
-                       }
                }
-               // should never be here
-               return false;
        }
 
        /**
index 25df0f9..143b621 100644 (file)
@@ -358,21 +358,28 @@ abstract class BaseTemplate extends QuickTemplate {
 
                if ( isset( $item['href'] ) || isset( $options['link-fallback'] ) ) {
                        $attrs = $item;
-                       foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary' ) as $k ) {
+                       foreach ( array( 'single-id', 'text', 'msg', 'tooltiponly', 'context', 'primary',
+                               'tooltip-params' ) as $k ) {
                                unset( $attrs[$k] );
                        }
 
                        if ( isset( $item['id'] ) && !isset( $item['single-id'] ) ) {
                                $item['single-id'] = $item['id'];
                        }
+
+                       $tooltipParams = array();
+                       if ( isset( $item['tooltip-params'] ) ) {
+                               $tooltipParams = $item['tooltip-params'];
+                       }
+
                        if ( isset( $item['single-id'] ) ) {
                                if ( isset( $item['tooltiponly'] ) && $item['tooltiponly'] ) {
-                                       $title = Linker::titleAttrib( $item['single-id'] );
+                                       $title = Linker::titleAttrib( $item['single-id'], null, $tooltipParams );
                                        if ( $title !== false ) {
                                                $attrs['title'] = $title;
                                        }
                                } else {
-                                       $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'] );
+                                       $tip = Linker::tooltipAndAccesskeyAttribs( $item['single-id'], $tooltipParams );
                                        if ( isset( $tip['title'] ) && $tip['title'] !== false ) {
                                                $attrs['title'] = $tip['title'];
                                        }
index dbb7c7f..83f119d 100644 (file)
@@ -560,7 +560,7 @@ abstract class Skin extends ContextSource {
                        $classes .= ' catlinks-allhidden';
                }
 
-               return "<div id='catlinks' class='$classes'>{$catlinks}</div>";
+               return "<div id='catlinks' class='$classes' data-mw='interface'>{$catlinks}</div>";
        }
 
        /**
index c0e5556..1328870 100644 (file)
@@ -1204,7 +1204,7 @@ class SkinTemplate extends Skin {
                        $nav_urls['print'] = array(
                                'text' => $this->msg( 'printableversion' )->text(),
                                'href' => $this->getTitle()->getLocalURL(
-                                       $request->appendQueryValue( 'printable', 'yes', true ) )
+                                       $request->appendQueryValue( 'printable', 'yes' ) )
                        );
                }
 
@@ -1246,7 +1246,8 @@ class SkinTemplate extends Skin {
 
                        $nav_urls['contributions'] = array(
                                'text' => $this->msg( 'contributions', $rootUser )->text(),
-                               'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser )
+                               'href' => self::makeSpecialUrlSubpage( 'Contributions', $rootUser ),
+                               'tooltip-params' => array( $rootUser ),
                        );
 
                        $nav_urls['log'] = array(
@@ -1262,7 +1263,8 @@ class SkinTemplate extends Skin {
 
                        if ( $this->showEmailUser( $user ) ) {
                                $nav_urls['emailuser'] = array(
-                                       'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser )
+                                       'href' => self::makeSpecialUrlSubpage( 'Emailuser', $rootUser ),
+                                       'tooltip-params' => array( $rootUser ),
                                );
                        }
 
index 74842aa..13dffb4 100644 (file)
@@ -421,13 +421,11 @@ abstract class ChangesListSpecialPage extends SpecialPage {
 
        /**
         * Return the legend displayed within the fieldset
-        * @todo This should not be static, then we can drop the parameter
-        * @todo Not called by anything, should be called by doHeader()
         *
-        * @param IContextSource $context The object available as $this in non-static functions
         * @return string
         */
-       public static function makeLegend( IContextSource $context ) {
+       public function makeLegend() {
+               $context = $this->getContext();
                $user = $context->getUser();
                # The legend showing what the letters and stuff mean
                $legend = Html::openElement( 'dl' ) . "\n";
index 9755e8e..27e645a 100644 (file)
@@ -275,11 +275,14 @@ abstract class QueryPage extends SpecialPage {
        }
 
        /**
-        * Some special pages (for example SpecialListusers) might not return the
+        * Some special pages (for example SpecialListusers used to) might not return the
         * current object formatted, but return the previous one instead.
         * Setting this to return true will ensure formatResult() is called
         * one more time to make sure that the very last result is formatted
         * as well.
+        *
+        * @deprecated since 1.27
+        *
         * @return bool
         */
        function tryLastResult() {
@@ -325,25 +328,39 @@ abstract class QueryPage extends SpecialPage {
                                                $value = 0;
                                        }
 
-                                       $vals[] = array( 'qc_type' => $this->getName(),
-                                                       'qc_namespace' => $row->namespace,
-                                                       'qc_title' => $row->title,
-                                                       'qc_value' => $value );
+                                       $vals[] = array(
+                                               'qc_type' => $this->getName(),
+                                               'qc_namespace' => $row->namespace,
+                                               'qc_title' => $row->title,
+                                               'qc_value' => $value
+                                       );
                                }
 
-                               $dbw->startAtomic( __METHOD__ );
-                               # Clear out any old cached data
-                               $dbw->delete( 'querycache', array( 'qc_type' => $this->getName() ), $fname );
-                               # Save results into the querycache table on the master
-                               if ( count( $vals ) ) {
-                                       $dbw->insert( 'querycache', $vals, __METHOD__ );
-                               }
-                               # Update the querycache_info record for the page
-                               $dbw->delete( 'querycache_info', array( 'qci_type' => $this->getName() ), $fname );
-                               $dbw->insert( 'querycache_info',
-                                       array( 'qci_type' => $this->getName(), 'qci_timestamp' => $dbw->timestamp() ),
-                                       $fname );
-                               $dbw->endAtomic( __METHOD__ );
+                               $that = $this;
+                               $dbw->doAtomicSection(
+                                       __METHOD__,
+                                       function ( IDatabase $dbw, $fname ) use ( $that, $vals ) {
+                                               # Clear out any old cached data
+                                               $dbw->delete( 'querycache',
+                                                       array( 'qc_type' => $that->getName() ),
+                                                       $fname
+                                               );
+                                               # Save results into the querycache table on the master
+                                               if ( count( $vals ) ) {
+                                                       $dbw->insert( 'querycache', $vals, $fname );
+                                               }
+                                               # Update the querycache_info record for the page
+                                               $dbw->delete( 'querycache_info',
+                                                       array( 'qci_type' => $that->getName() ),
+                                                       $fname
+                                               );
+                                               $dbw->insert( 'querycache_info',
+                                                       array( 'qci_type' => $that->getName(),
+                                                               'qci_timestamp' => $dbw->timestamp() ),
+                                                       $fname
+                                               );
+                                       }
+                               );
                        }
                } catch ( DBError $e ) {
                        if ( !$ignoreErrors ) {
@@ -646,12 +663,9 @@ abstract class QueryPage extends SpecialPage {
                                // @codingStandardsIgnoreEnd
                                $line = $this->formatResult( $skin, $row );
                                if ( $line ) {
-                                       $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
-                                               ? ' class="not-patrolled"'
-                                               : '';
                                        $html[] = $this->listoutput
                                                ? $line
-                                               : "<li{$attr}>{$line}</li>\n";
+                                               : "<li>{$line}</li>\n";
                                }
                        }
 
@@ -660,12 +674,9 @@ abstract class QueryPage extends SpecialPage {
                                $row = null;
                                $line = $this->formatResult( $skin, $row );
                                if ( $line ) {
-                                       $attr = ( isset( $row->usepatrol ) && $row->usepatrol && $row->patrolled == 0 )
-                                               ? ' class="not-patrolled"'
-                                               : '';
                                        $html[] = $this->listoutput
                                                ? $line
-                                               : "<li{$attr}>{$line}</li>\n";
+                                               : "<li>{$line}</li>\n";
                                }
                        }
 
index 65a4eb9..0417146 100644 (file)
@@ -675,6 +675,16 @@ class SpecialPage {
                return $group;
        }
 
+       /**
+        * Indicates whether this special page may perform database writes
+        *
+        * @return bool
+        * @since 1.27
+        */
+       public function doesWrites() {
+               return false;
+       }
+
        /**
         * Under which header this special page is listed in Special:SpecialPages
         * See messages 'specialpages-group-*' for valid names
index 47b4fc8..2e764ba 100644 (file)
@@ -90,12 +90,14 @@ class SpecialPageFactory {
                'Unblock' => 'SpecialUnblock',
                'BlockList' => 'SpecialBlockList',
                'ChangePassword' => 'SpecialChangePassword',
+               'BotPasswords' => 'SpecialBotPasswords',
                'PasswordReset' => 'SpecialPasswordReset',
                'DeletedContributions' => 'DeletedContributionsPage',
                'Preferences' => 'SpecialPreferences',
                'ResetTokens' => 'SpecialResetTokens',
                'Contributions' => 'SpecialContributions',
                'Listgrouprights' => 'SpecialListGroupRights',
+               'Listgrants' => 'SpecialListGrants',
                'Listusers' => 'SpecialListUsers',
                'Listadmins' => 'SpecialListAdmins',
                'Listbots' => 'SpecialListBots',
@@ -133,6 +135,7 @@ class SpecialPageFactory {
                'Randompage' => 'RandomPage',
                'RandomInCategory' => 'SpecialRandomInCategory',
                'Randomredirect' => 'SpecialRandomredirect',
+               'Randomrootpage' => 'SpecialRandomrootpage',
 
                // High use pages
                'Mostlinkedcategories' => 'MostlinkedCategoriesPage',
@@ -498,7 +501,6 @@ class SpecialPageFactory {
         * @return bool
         */
        public static function executePath( Title &$title, IContextSource &$context, $including = false ) {
-
                // @todo FIXME: Redirects broken due to this call
                $bits = explode( '/', $title->getDBkey(), 2 );
                $name = $bits[0];
@@ -507,8 +509,8 @@ class SpecialPageFactory {
                } else {
                        $par = $bits[1];
                }
+
                $page = self::getPage( $name );
-               // Nonexistent?
                if ( !$page ) {
                        $context->getOutput()->setArticleRelated( false );
                        $context->getOutput()->setRobotPolicy( 'noindex,nofollow' );
@@ -523,6 +525,15 @@ class SpecialPageFactory {
                        return false;
                }
 
+               if ( !$including ) {
+                       // Narrow DB query expectations for this HTTP request
+                       $trxLimits = $context->getConfig()->get( 'TrxProfilerLimits' );
+                       $trxProfiler = Profiler::instance()->getTransactionProfiler();
+                       if ( $context->getRequest()->wasPosted() && !$page->doesWrites() ) {
+                               $trxProfiler->setExpectations( $trxLimits['POST-nonwrite'], __METHOD__ );
+                       }
+               }
+
                // Page exists, set the context
                $page->setContext( $context );
 
index 70c7a8b..1f369d8 100644 (file)
@@ -318,5 +318,3 @@ class SpecialActiveUsers extends SpecialPage {
                return 'users';
        }
 }
-
-
index 8109cc9..5b74c8b 100644 (file)
@@ -399,7 +399,7 @@ class AllMessagesTablePager extends TablePager {
 
                        case 'am_default' :
                        case 'am_actual' :
-                               return Sanitizer::escapeHtmlAllowEntities( $value, ENT_QUOTES );
+                               return Sanitizer::escapeHtmlAllowEntities( $value );
                }
 
                return '';
index e125d94..d198ea1 100644 (file)
@@ -51,6 +51,10 @@ class SpecialBlock extends FormSpecialPage {
                parent::__construct( 'Block', 'block' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Checks that the user can unblock themselves if they are trying to do so
         *
@@ -321,7 +325,8 @@ class SpecialBlock extends FormSpecialPage {
        protected function preText() {
                $this->getOutput()->addModules( array( 'mediawiki.special.block', 'mediawiki.userSuggest' ) );
 
-               $text = $this->msg( 'blockiptext' )->parse();
+               $blockCIDRLimit = $this->getConfig()->get( 'BlockCIDRLimit' );
+               $text = $this->msg( 'blockiptext', $blockCIDRLimit['IPv4'], $blockCIDRLimit['IPv6'] )->parse();
 
                $otherBlockMessages = array();
                if ( $this->target !== null ) {
@@ -645,12 +650,25 @@ class SpecialBlock extends FormSpecialPage {
                        return array( 'badipaddress' );
                }
 
-               if ( ( strlen( $data['Expiry'] ) == 0 ) || ( strlen( $data['Expiry'] ) > 50 )
-                       || !self::parseExpiryInput( $data['Expiry'] )
+               $expiryTime = self::parseExpiryInput( $data['Expiry'] );
+
+               if (
+                       // an expiry time is needed
+                       ( strlen( $data['Expiry'] ) == 0 ) ||
+                       // can't be a larger string as 50 (it should be a time format in any way)
+                       ( strlen( $data['Expiry'] ) > 50 ) ||
+                       // check, if the time could be parsed
+                       !$expiryTime
                ) {
                        return array( 'ipb_expiry_invalid' );
                }
 
+               // an expiry time should be in the future, not in the
+               // past (wouldn't make any sense) - bug T123069
+               if ( $expiryTime < wfTimestampNow() ) {
+                       return array( 'ipb_expiry_old' );
+               }
+
                if ( !isset( $data['DisableEmail'] ) ) {
                        $data['DisableEmail'] = false;
                }
@@ -694,7 +712,7 @@ class SpecialBlock extends FormSpecialPage {
                $block->setBlocker( $performer );
                # Truncate reason for whole multibyte characters
                $block->mReason = $wgContLang->truncate( $data['Reason'][0], 255 );
-               $block->mExpiry = self::parseExpiryInput( $data['Expiry'] );
+               $block->mExpiry = $expiryTime;
                $block->prevents( 'createaccount', $data['CreateAccount'] );
                $block->prevents( 'editownusertalk', ( !$wgBlockAllowsUTEdit || $data['DisableUTEdit'] ) );
                $block->prevents( 'sendemail', $data['DisableEmail'] );
@@ -971,6 +989,24 @@ class SpecialBlock extends FormSpecialPage {
                $out->addWikiMsg( 'blockipsuccesstext', wfEscapeWikiText( $this->target ) );
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 9defaba..73a2774 100644 (file)
@@ -46,8 +46,7 @@ class SpecialBlockList extends SpecialPage {
                $out = $this->getOutput();
                $lang = $this->getLanguage();
                $out->setPageTitle( $this->msg( 'ipblocklist' ) );
-               $out->addModuleStyles( 'mediawiki.special' );
-               $out->addModules( 'mediawiki.userSuggest' );
+               $out->addModuleStyles( array( 'mediawiki.special', 'mediawiki.special.blocklist' ) );
 
                $request = $this->getRequest();
                $par = $request->getVal( 'ip', $par );
@@ -71,12 +70,11 @@ class SpecialBlockList extends SpecialPage {
                # Just show the block list
                $fields = array(
                        'Target' => array(
-                               'type' => 'text',
+                               'type' => 'user',
                                'label-message' => 'ipaddressorusername',
                                'tabindex' => '1',
                                'size' => '45',
                                'default' => $this->target,
-                               'cssclass' => 'mw-autocomplete-user', // used by mediawiki.userSuggest
                        ),
                        'Options' => array(
                                'type' => 'multiselect',
@@ -104,7 +102,7 @@ class SpecialBlockList extends SpecialPage {
                );
                $context = new DerivativeContext( $this->getContext() );
                $context->setTitle( $this->getPageTitle() ); // Remove subpage
-               $form = new HTMLForm( $fields, $context );
+               $form = HTMLForm::factory( 'ooui', $fields, $context );
                $form->setMethod( 'get' );
                $form->setWrapperLegendMsg( 'ipblocklist-legend' );
                $form->setSubmitTextMsg( 'ipblocklist-submit' );
diff --git a/includes/specials/SpecialBotPasswords.php b/includes/specials/SpecialBotPasswords.php
new file mode 100644 (file)
index 0000000..93c36ab
--- /dev/null
@@ -0,0 +1,357 @@
+<?php
+/**
+ * Implements Special:BotPasswords
+ *
+ * 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 manage bot passwords
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialBotPasswords extends FormSpecialPage {
+
+       /** @var int Central user ID */
+       private $userId = 0;
+
+       /** @var BotPassword|null Bot password being edited, if any */
+       private $botPassword = null;
+
+       /** @var string Operation being performed: create, update, delete */
+       private $operation = null;
+
+       /** @var string New password set, for communication between onSubmit() and onSuccess() */
+       private $password = null;
+
+       public function __construct() {
+               parent::__construct( 'BotPasswords', 'editmyprivateinfo' );
+       }
+
+       /**
+        * @return bool
+        */
+       public function isListed() {
+               return $this->getConfig()->get( 'EnableBotPasswords' );
+       }
+
+       /**
+        * Main execution point
+        * @param string|null $par
+        */
+       function execute( $par ) {
+               $this->getOutput()->disallowUserJs();
+               $this->requireLogin();
+
+               $par = trim( $par );
+               if ( strlen( $par ) === 0 ) {
+                       $par = null;
+               } elseif ( strlen( $par ) > BotPassword::APPID_MAXLENGTH ) {
+                       throw new ErrorPageError( 'botpasswords', 'botpasswords-bad-appid',
+                               array( htmlspecialchars( $par ) ) );
+               }
+
+               parent::execute( $par );
+       }
+
+       protected function checkExecutePermissions( User $user ) {
+               parent::checkExecutePermissions( $user );
+
+               if ( !$this->getConfig()->get( 'EnableBotPasswords' ) ) {
+                       throw new ErrorPageError( 'botpasswords', 'botpasswords-disabled' );
+               }
+
+               $this->userId = CentralIdLookup::factory()->centralIdFromLocalUser( $this->getUser() );
+               if ( !$this->userId ) {
+                       throw new ErrorPageError( 'botpasswords', 'botpasswords-no-central-id' );
+               }
+       }
+
+       protected function getFormFields() {
+               $that = $this;
+               $user = $this->getUser();
+               $request = $this->getRequest();
+
+               $fields = array();
+
+               if ( $this->par !== null ) {
+                       $this->botPassword = BotPassword::newFromCentralId( $this->userId, $this->par );
+                       if ( !$this->botPassword ) {
+                               $this->botPassword = BotPassword::newUnsaved( array(
+                                       'centralId' => $this->userId,
+                                       'appId' => $this->par,
+                               ) );
+                       }
+
+                       $sep = BotPassword::getSeparator();
+                       $fields[] = array(
+                               'type' => 'info',
+                               'label-message' => 'username',
+                               'default' => $this->getUser()->getName() . $sep . $this->par
+                       );
+
+                       if ( $this->botPassword->isSaved() ) {
+                               $fields['resetPassword'] = array(
+                                       'type' => 'check',
+                                       'label-message' => 'botpasswords-label-resetpassword',
+                               );
+                       }
+
+                       $lang = $this->getLanguage();
+                       $showGrants = MWGrants::getValidGrants();
+                       $fields['grants'] = array(
+                               'type' => 'checkmatrix',
+                               'label-message' => 'botpasswords-label-grants',
+                               'help-message' => 'botpasswords-help-grants',
+                               'columns' => array(
+                                       $this->msg( 'botpasswords-label-grants-column' )->escaped() => 'grant'
+                               ),
+                               'rows' => array_combine(
+                                       array_map( 'MWGrants::getGrantsLink', $showGrants ),
+                                       $showGrants
+                               ),
+                               'default' => array_map(
+                                       function( $g ) {
+                                               return "grant-$g";
+                                       },
+                                       $this->botPassword->getGrants()
+                               ),
+                               'tooltips' => array_combine(
+                                       array_map( 'MWGrants::getGrantsLink', $showGrants ),
+                                       array_map(
+                                               function( $rights ) use ( $lang ) {
+                                                       return $lang->semicolonList( array_map( 'User::getRightDescription', $rights ) );
+                                               },
+                                               array_intersect_key( MWGrants::getRightsByGrant(), array_flip( $showGrants ) )
+                                       )
+                               ),
+                               'force-options-on' => array_map(
+                                       function( $g ) {
+                                               return "grant-$g";
+                                       },
+                                       MWGrants::getHiddenGrants()
+                               ),
+                       );
+
+                       $fields['restrictions'] = array(
+                               'type' => 'textarea',
+                               'label-message' => 'botpasswords-label-restrictions',
+                               'required' => true,
+                               'default' => $this->botPassword->getRestrictions()->toJson( true ),
+                               'rows' => 5,
+                               'validation-callback' => function ( $v ) {
+                                       try {
+                                               MWRestrictions::newFromJson( $v );
+                                               return true;
+                                       } catch ( InvalidArgumentException $ex ) {
+                                               return $ex->getMessage();
+                                       }
+                               },
+                       );
+
+               } else {
+                       $dbr = BotPassword::getDB( DB_SLAVE );
+                       $res = $dbr->select(
+                               'bot_passwords',
+                               array( 'bp_app_id' ),
+                               array( 'bp_user' => $this->userId ),
+                               __METHOD__
+                       );
+                       foreach ( $res as $row ) {
+                               $fields[] = array(
+                                       'section' => 'existing',
+                                       'type' => 'info',
+                                       'raw' => true,
+                                       'default' => Linker::link(
+                                               $this->getPageTitle( $row->bp_app_id ),
+                                               htmlspecialchars( $row->bp_app_id ),
+                                               array(),
+                                               array(),
+                                               array( 'known' )
+                                       ),
+                               );
+                       }
+
+                       $fields['appId'] = array(
+                               'section' => 'createnew',
+                               'type' => 'textwithbutton',
+                               'label-message' => 'botpasswords-label-appid',
+                               'buttondefault' => $this->msg( 'botpasswords-label-create' )->text(),
+                               'required' => true,
+                               'size' => BotPassword::APPID_MAXLENGTH,
+                               'maxlength' => BotPassword::APPID_MAXLENGTH,
+                               'validation-callback' => function ( $v ) {
+                                       $v = trim( $v );
+                                       return $v !== '' && strlen( $v ) <= BotPassword::APPID_MAXLENGTH;
+                               },
+                       );
+
+                       $fields[] = array(
+                               'type' => 'hidden',
+                               'default' => 'new',
+                               'name' => 'op',
+                       );
+               }
+
+               return $fields;
+       }
+
+       protected function alterForm( HTMLForm $form ) {
+               $form->setId( 'mw-botpasswords-form' );
+               $form->setTableId( 'mw-botpasswords-table' );
+               $form->addPreText( $this->msg( 'botpasswords-summary' )->parseAsBlock() );
+               $form->suppressDefaultSubmit();
+
+               if ( $this->par !== null ) {
+                       if ( $this->botPassword->isSaved() ) {
+                               $form->setWrapperLegendMsg( 'botpasswords-editexisting' );
+                               $form->addButton( array(
+                                       'name' => 'op',
+                                       'value' => 'update',
+                                       'label-message' => 'botpasswords-label-update',
+                                       'flags' => array( 'primary', 'progressive' ),
+                               ) );
+                               $form->addButton( array(
+                                       'name' => 'op',
+                                       'value' => 'delete',
+                                       'label-message' => 'botpasswords-label-delete',
+                                       'flags' => array( 'destructive' ),
+                               ) );
+                       } else {
+                               $form->setWrapperLegendMsg( 'botpasswords-createnew' );
+                               $form->addButton( array(
+                                       'name' => 'op',
+                                       'value' => 'create',
+                                       'label-message' => 'botpasswords-label-create',
+                                       'flags' => array( 'primary', 'constructive' ),
+                               ) );
+                       }
+
+                       $form->addButton( array(
+                               'name' => 'op',
+                               'value' => 'cancel',
+                               'label-message' => 'botpasswords-label-cancel'
+                       ) );
+               }
+       }
+
+       public function onSubmit( array $data ) {
+               $op = $this->getRequest()->getVal( 'op', '' );
+
+               switch ( $op ) {
+                       case 'new':
+                               $this->getOutput()->redirect( $this->getPageTitle( $data['appId'] )->getFullURL() );
+                               return false;
+
+                       case 'create':
+                               $this->operation = 'insert';
+                               return $this->save( $data );
+
+                       case 'update':
+                               $this->operation = 'update';
+                               return $this->save( $data );
+
+                       case 'delete':
+                               $this->operation = 'delete';
+                               $bp = BotPassword::newFromCentralId( $this->userId, $this->par );
+                               if ( $bp ) {
+                                       $bp->delete();
+                               }
+                               return Status::newGood();
+
+                       case 'cancel':
+                               $this->getOutput()->redirect( $this->getPageTitle()->getFullURL() );
+                               return false;
+               }
+
+               return false;
+       }
+
+       private function save( array $data ) {
+               $bp = BotPassword::newUnsaved( array(
+                       'centralId' => $this->userId,
+                       'appId' => $this->par,
+                       'restrictions' => MWRestrictions::newFromJson( $data['restrictions'] ),
+                       'grants' => array_merge(
+                               MWGrants::getHiddenGrants(),
+                               preg_replace( '/^grant-/', '', $data['grants'] )
+                       )
+               ) );
+
+               if ( $this->operation === 'insert' || !empty( $data['resetPassword'] ) ) {
+                       $this->password = PasswordFactory::generateRandomPasswordString(
+                               max( 32, $this->getConfig()->get( 'MinimalPasswordLength' ) )
+                       );
+                       $passwordFactory = new PasswordFactory();
+                       $passwordFactory->init( RequestContext::getMain()->getConfig() );
+                       $password = $passwordFactory->newFromPlaintext( $this->password );
+               } else {
+                       $password = null;
+               }
+
+               if ( $bp->save( $this->operation, $password ) ) {
+                       return Status::newGood();
+               } else {
+                       // Messages: botpasswords-insert-failed, botpasswords-update-failed
+                       return Status::newFatal( "botpasswords-{$this->operation}-failed", $this->par );
+               }
+       }
+
+       public function onSuccess() {
+               $out = $this->getOutput();
+
+               switch ( $this->operation ) {
+                       case 'insert':
+                               $out->setPageTitle( $this->msg( 'botpasswords-created-title' )->text() );
+                               $out->addWikiMsg( 'botpasswords-created-body', $this->par );
+                               break;
+
+                       case 'update':
+                               $out->setPageTitle( $this->msg( 'botpasswords-updated-title' )->text() );
+                               $out->addWikiMsg( 'botpasswords-updated-body', $this->par );
+                               break;
+
+                       case 'delete':
+                               $out->setPageTitle( $this->msg( 'botpasswords-deleted-title' )->text() );
+                               $out->addWikiMsg( 'botpasswords-deleted-body', $this->par );
+                               $this->password = null;
+                               break;
+               }
+
+               if ( $this->password !== null ) {
+                       $sep = BotPassword::getSeparator();
+                       $out->addWikiMsg(
+                               'botpasswords-newpassword',
+                               htmlspecialchars( $this->getUser()->getName() . $sep . $this->par ),
+                               htmlspecialchars( $this->password )
+                       );
+                       $this->password = null;
+               }
+
+               $out->addReturnTo( $this->getPageTitle() );
+       }
+
+       protected function getGroupName() {
+               return 'users';
+       }
+
+       protected function getDisplayFormat() {
+               return 'ooui';
+       }
+}
index 9ea18da..71616fa 100644 (file)
@@ -164,7 +164,6 @@ class BrokenRedirectsPage extends QueryPage {
                return $out;
        }
 
-
        /**
         * Cache page content model for performance
         *
index 4812c9d..a9a7f97 100644 (file)
@@ -6,6 +6,10 @@ class SpecialChangeContentModel extends FormSpecialPage {
                parent::__construct( 'ChangeContentModel', 'editcontentmodel' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * @var Title|null
         */
index 51b08f9..989bae9 100644 (file)
@@ -36,6 +36,10 @@ class SpecialChangeEmail extends FormSpecialPage {
                parent::__construct( 'ChangeEmail', 'editmyprivateinfo' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * @return bool
         */
index 91ac4e0..8656798 100644 (file)
@@ -41,6 +41,10 @@ class SpecialChangePassword extends FormSpecialPage {
                $this->listed( false );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Main execution point
         * @param string|null $par
@@ -152,7 +156,10 @@ class SpecialChangePassword extends FormSpecialPage {
                                ? 'resetpass-submit-loggedin'
                                : 'resetpass_submit'
                );
-               $form->addButton( 'wpCancel', $this->msg( 'resetpass-submit-cancel' )->text() );
+               $form->addButton( array(
+                       '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() );
index 0f8b729..fbc5984 100644 (file)
@@ -49,13 +49,12 @@ class SpecialComparePages extends SpecialPage {
        public function execute( $par ) {
                $this->setHeaders();
                $this->outputHeader();
+               $this->getOutput()->addModuleStyles( 'mediawiki.special.comparepages.styles' );
 
-               # Form (.mw-searchInput enables suggestions)
-               $form = new HTMLForm( array(
+               $form = HTMLForm::factory( 'ooui', array(
                        'Page1' => array(
-                               'type' => 'text',
+                               'type' => 'title',
                                'name' => 'page1',
-                               'cssclass' => 'mw-searchInput',
                                'label-message' => 'compare-page1',
                                'size' => '40',
                                'section' => 'page1',
@@ -70,9 +69,8 @@ class SpecialComparePages extends SpecialPage {
                                'validation-callback' => array( $this, 'checkExistingRevision' ),
                        ),
                        'Page2' => array(
-                               'type' => 'text',
+                               'type' => 'title',
                                'name' => 'page2',
-                               'cssclass' => 'mw-searchInput',
                                'label-message' => 'compare-page2',
                                'size' => '40',
                                'section' => 'page2',
index 147f67e..5ed33e0 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Implements Special:Confirmemail and Special:Invalidateemail
+ * Implements Special:Confirmemail
  *
  * 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
@@ -34,6 +34,10 @@ class EmailConfirmation extends UnlistedSpecialPage {
                parent::__construct( 'Confirmemail', 'editmyprivateinfo' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Main execution point
         *
@@ -78,16 +82,37 @@ class EmailConfirmation extends UnlistedSpecialPage {
                $user = $this->getUser();
                $out = $this->getOutput();
 
-               if ( $this->getRequest()->wasPosted() &&
-                       $user->matchEditToken( $this->getRequest()->getText( 'token' ) )
-               ) {
-                       $status = $user->sendConfirmationMail();
-                       if ( $status->isGood() ) {
+               if ( !$user->isEmailConfirmed() ) {
+                       $descriptor = array();
+                       if ( $user->isEmailConfirmationPending() ) {
+                               $descriptor += array(
+                                       'pending' => array(
+                                               'type' => 'info',
+                                               'raw' => true,
+                                               'default' => "<div class=\"error mw-confirmemail-pending\">\n" .
+                                                       $this->msg( 'confirmemail_pending' )->escaped() .
+                                                       "\n</div>",
+                                       ),
+                               );
+                       }
+
+                       $out->addWikiMsg( 'confirmemail_text' );
+                       $form = HTMLForm::factory( 'ooui', $descriptor, $this->getContext() );
+                       $form
+                               ->setMethod( 'post' )
+                               ->setAction( $this->getPageTitle()->getLocalURL() )
+                               ->setSubmitTextMsg( 'confirmemail_send' )
+                               ->setSubmitCallback( array( $this, 'submitSend' ) );
+
+                       $retval = $form->show();
+
+                       if ( $retval === true ) {
+                               // should never happen, but if so, don't let the user without any message
                                $out->addWikiMsg( 'confirmemail_sent' );
-                       } else {
-                               $out->addWikiText( $status->getWikiText( 'confirmemail_sendfailed' ) );
+                       } elseif ( $retval instanceof Status && $retval->isGood() ) {
+                               $out->addWikiText( $retval->getValue() );
                        }
-               } elseif ( $user->isEmailConfirmed() ) {
+               } else {
                        // date and time are separate parameters to facilitate localisation.
                        // $time is kept for backward compat reasons.
                        // 'emailauthenticated' is also used in SpecialPreferences.php
@@ -97,23 +122,22 @@ class EmailConfirmation extends UnlistedSpecialPage {
                        $d = $lang->userDate( $emailAuthenticated, $user );
                        $t = $lang->userTime( $emailAuthenticated, $user );
                        $out->addWikiMsg( 'emailauthenticated', $time, $d, $t );
-               } else {
-                       if ( $user->isEmailConfirmationPending() ) {
-                               $out->wrapWikiMsg(
-                                       "<div class=\"error mw-confirmemail-pending\">\n$1\n</div>",
-                                       'confirmemail_pending'
-                               );
-                       }
+               }
+       }
 
-                       $out->addWikiMsg( 'confirmemail_text' );
-                       $form = Html::openElement(
-                               'form',
-                               array( 'method' => 'post', 'action' => $this->getPageTitle()->getLocalURL() )
-                       ) . "\n";
-                       $form .= Html::hidden( 'token', $user->getEditToken() ) . "\n";
-                       $form .= Xml::submitButton( $this->msg( 'confirmemail_send' )->text() ) . "\n";
-                       $form .= Html::closeElement( 'form' ) . "\n";
-                       $out->addHTML( $form );
+       /**
+        * Callback for HTMLForm send confirmation mail.
+        *
+        * @return Status Status object with the result
+        */
+       public function submitSend() {
+               $status = $this->getUser()->sendConfirmationMail();
+               if ( $status->isGood() ) {
+                       return Status::newGood( $this->msg( 'confirmemail_sent' )->text() );
+               } else {
+                       return Status::newFatal( new RawMessage(
+                               $status->getWikiText( 'confirmemail_sendfailed' )
+                       ) );
                }
        }
 
@@ -142,49 +166,3 @@ class EmailConfirmation extends UnlistedSpecialPage {
                }
        }
 }
-
-/**
- * Special page allows users to cancel an email confirmation using the e-mail
- * confirmation code
- *
- * @ingroup SpecialPage
- */
-class EmailInvalidation extends UnlistedSpecialPage {
-       public function __construct() {
-               parent::__construct( 'Invalidateemail', 'editmyprivateinfo' );
-       }
-
-       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();
-
-               $this->setHeaders();
-               $this->checkReadOnly();
-               $this->checkPermissions();
-               $this->attemptInvalidate( $code );
-       }
-
-       /**
-        * Attempt to invalidate the user's email address and show success or failure
-        * as needed; if successful, link to main page
-        *
-        * @param string $code Confirmation code
-        */
-       function attemptInvalidate( $code ) {
-               $user = User::newFromConfirmationCode( $code, User::READ_LATEST );
-               if ( !is_object( $user ) ) {
-                       $this->getOutput()->addWikiMsg( 'confirmemail_invalid' );
-
-                       return;
-               }
-
-               $user->invalidateEmail();
-               $user->saveSettings();
-               $this->getOutput()->addWikiMsg( 'confirmemail_invalidated' );
-
-               if ( !$this->getUser()->isLoggedIn() ) {
-                       $this->getOutput()->returnToMain();
-               }
-       }
-}
index fc9f750..ab6614b 100644 (file)
@@ -49,11 +49,7 @@ class SpecialContributions extends IncludableSpecialPage {
                        $target = $request->getVal( 'target' );
                }
 
-               // check for radiobox
-               if ( $request->getVal( 'contribs' ) == 'newbie' ) {
-                       $target = 'newbies';
-                       $this->opts['contribs'] = 'newbie';
-               } elseif ( $par === 'newbies' ) { // b/c for WMF
+               if ( $request->getVal( 'contribs' ) == 'newbie' || $par === 'newbies' ) {
                        $target = 'newbies';
                        $this->opts['contribs'] = 'newbie';
                } else {
@@ -649,6 +645,24 @@ class SpecialContributions extends IncludableSpecialPage {
                return $form;
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 30e3833..f8c6832 100644 (file)
@@ -37,6 +37,10 @@ class SpecialCreateAccount extends SpecialRedirectToSpecial {
                );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        // No reason to hide this link on Special:Specialpages
        public function isListed() {
                return true;
index 6f8e786..f6d560f 100644 (file)
@@ -658,6 +658,24 @@ class DeletedContributionsPage extends SpecialPage {
                return $f;
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 97b04c2..916ba6b 100644 (file)
@@ -55,6 +55,10 @@ class SpecialEditTags extends UnlistedSpecialPage {
                parent::__construct( 'EditTags', 'changetags' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par ) {
                $this->checkPermissions();
                $this->checkReadOnly();
index 952ae0e..13ee8b3 100644 (file)
@@ -53,6 +53,10 @@ class SpecialEditWatchlist extends UnlistedSpecialPage {
                parent::__construct( 'EditWatchlist', 'editmywatchlist' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Main execution point
         *
diff --git a/includes/specials/SpecialEmailInvalidate.php b/includes/specials/SpecialEmailInvalidate.php
new file mode 100644 (file)
index 0000000..b5c66ff
--- /dev/null
@@ -0,0 +1,72 @@
+<?php
+/**
+ * Implements Special:EmailInvalidation
+ *
+ * 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 allows users to cancel an email confirmation using the e-mail
+ * confirmation code
+ *
+ * @ingroup SpecialPage
+ */
+class EmailInvalidation extends UnlistedSpecialPage {
+       public function __construct() {
+               parent::__construct( 'Invalidateemail', 'editmyprivateinfo' );
+       }
+
+       public function doesWrites() {
+               return true;
+       }
+
+       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();
+
+               $this->setHeaders();
+               $this->checkReadOnly();
+               $this->checkPermissions();
+               $this->attemptInvalidate( $code );
+       }
+
+       /**
+        * Attempt to invalidate the user's email address and show success or failure
+        * as needed; if successful, link to main page
+        *
+        * @param string $code Confirmation code
+        */
+       function attemptInvalidate( $code ) {
+               $user = User::newFromConfirmationCode( $code, User::READ_LATEST );
+               if ( !is_object( $user ) ) {
+                       $this->getOutput()->addWikiMsg( 'confirmemail_invalid' );
+
+                       return;
+               }
+
+               $user->invalidateEmail();
+               $user->saveSettings();
+               $this->getOutput()->addWikiMsg( 'confirmemail_invalidated' );
+
+               if ( !$this->getUser()->isLoggedIn() ) {
+                       $this->getOutput()->returnToMain();
+               }
+       }
+}
index 3b31530..c036d3d 100644 (file)
@@ -38,6 +38,10 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                parent::__construct( 'Emailuser' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function getDescription() {
                $target = self::getTarget( $this->mTarget );
                if ( !$target instanceof User ) {
@@ -392,6 +396,24 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                }
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 06eb276..00f6609 100644 (file)
@@ -95,8 +95,8 @@ class SpecialExpandTemplates extends SpecialPage {
                }
 
                $out = $this->getOutput();
-               $out->addWikiMsg( 'expand_templates_intro' );
-               $out->addHTML( $this->makeForm( $titleStr, $input ) );
+
+               $this->makeForm( $titleStr, $input );
 
                if ( $output !== false ) {
                        if ( $this->generateXML && strlen( $output ) > 0 ) {
@@ -130,6 +130,22 @@ class SpecialExpandTemplates extends SpecialPage {
                }
        }
 
+       /**
+        * Callback for the HTMLForm used in self::makeForm.
+        * Checks, if the input was given, and if not, returns a fatal Status
+        * object with an error message.
+        *
+        * @param array $values The values submitted to the HTMLForm
+        * @return Status
+        */
+       public function onSubmitInput( array $values ) {
+               $status = Status::newGood();
+               if ( !strlen( $values['input'] ) ) {
+                       $status = Status::newFatal( 'expand_templates_input_missing' );
+               }
+               return $status;
+       }
+
        /**
         * Generate a form allowing users to enter information
         *
@@ -138,69 +154,62 @@ class SpecialExpandTemplates extends SpecialPage {
         * @return string
         */
        private function makeForm( $title, $input ) {
-               $self = $this->getPageTitle();
-               $request = $this->getRequest();
-               $user = $this->getUser();
-
-               $form = Xml::openElement(
-                       'form',
-                       array( 'method' => 'post', 'action' => $self->getLocalUrl() )
-               );
-               $form .= "<fieldset><legend>" . $this->msg( 'expandtemplates' )->escaped() . "</legend>\n";
-
-               $form .= '<p>' . Xml::inputLabel(
-                       $this->msg( 'expand_templates_title' )->plain(),
-                       'wpContextTitle',
-                       'contexttitle',
-                       60,
-                       $title,
-                       array( 'autofocus' => '', 'class' => 'mw-ui-input-inline' )
-               ) . '</p>';
-               $form .= '<p>' . Xml::label(
-                       $this->msg( 'expand_templates_input' )->text(),
-                       'input'
-               ) . '</p>';
-               $form .= Xml::textarea(
-                       'wpInput',
-                       $input,
-                       10,
-                       10,
-                       array( 'id' => 'input' )
+               $fields = array(
+                       'contexttitle' => array(
+                               'type' => 'text',
+                               'label' => $this->msg( 'expand_templates_title' )->plain(),
+                               'name' => 'wpContextTitle',
+                               'id' => 'contexttitle',
+                               'size' => 60,
+                               'default' => $title,
+                               'autofocus' => true,
+                               'cssclass' => 'mw-ui-input-inline',
+                       ),
+                       'input' => array(
+                               'type' => 'textarea',
+                               'name' => 'wpInput',
+                               'label' => $this->msg( 'expand_templates_input' )->text(),
+                               'rows' => 10,
+                               'default' => $input,
+                               'id' => 'input',
+                       ),
+                       'removecomments' => array(
+                               'type' => 'check',
+                               'label' => $this->msg( 'expand_templates_remove_comments' )->text(),
+                               'name' => 'wpRemoveComments',
+                               'id' => 'removecomments',
+                               'default' => $this->removeComments,
+                       ),
+                       'removenowiki' => array(
+                               'type' => 'check',
+                               'label' => $this->msg( 'expand_templates_remove_nowiki' )->text(),
+                               'name' => 'wpRemoveNowiki',
+                               'id' => 'removenowiki',
+                               'default' => $this->removeNowiki,
+                       ),
+                       'generate_xml' => array(
+                               'type' => 'check',
+                               'label' => $this->msg( 'expand_templates_generate_xml' )->text(),
+                               'name' => 'wpGenerateXml',
+                               'id' => 'generate_xml',
+                               'default' => $this->generateXML,
+                       ),
+                       'generate_rawhtml' => array(
+                               'type' => 'check',
+                               'label' => $this->msg( 'expand_templates_generate_rawhtml' )->text(),
+                               'name' => 'wpGenerateRawHtml',
+                               'id' => 'generate_rawhtml',
+                               'default' => $this->generateRawHtml,
+                       ),
                );
 
-               $form .= '<p>' . Xml::checkLabel(
-                       $this->msg( 'expand_templates_remove_comments' )->text(),
-                       'wpRemoveComments',
-                       'removecomments',
-                       $this->removeComments
-               ) . '</p>';
-               $form .= '<p>' . Xml::checkLabel(
-                       $this->msg( 'expand_templates_remove_nowiki' )->text(),
-                       'wpRemoveNowiki',
-                       'removenowiki',
-                       $this->removeNowiki
-               ) . '</p>';
-               $form .= '<p>' . Xml::checkLabel(
-                       $this->msg( 'expand_templates_generate_xml' )->text(),
-                       'wpGenerateXml',
-                       'generate_xml',
-                       $this->generateXML
-               ) . '</p>';
-               $form .= '<p>' . Xml::checkLabel(
-                       $this->msg( 'expand_templates_generate_rawhtml' )->text(),
-                       'wpGenerateRawHtml',
-                       'generate_rawhtml',
-                       $this->generateRawHtml
-               ) . '</p>';
-               $form .= '<p>' . Xml::submitButton(
-                       $this->msg( 'expand_templates_ok' )->text(),
-                       array( 'accesskey' => 's' )
-               ) . '</p>';
-               $form .= "</fieldset>\n";
-               $form .= Html::hidden( 'wpEditToken', $user->getEditToken( '', $request ) );
-               $form .= Xml::closeElement( 'form' );
-
-               return $form;
+               $form = HTMLForm::factory( 'ooui', $fields, $this->getContext() );
+               $form
+                       ->setSubmitTextMsg( 'expand_templates_ok' )
+                       ->setWrapperLegendMsg( 'expandtemplates' )
+                       ->setHeaderText( $this->msg( 'expand_templates_intro' )->parse() )
+                       ->setSubmitCallback( array( $this, 'onSubmitInput' ) )
+                       ->showAlways();
        }
 
        /**
index 91fef03..3ce9c76 100644 (file)
@@ -235,8 +235,8 @@ class SpecialExport extends SpecialPage {
                        'textarea' => array(
                                'class' => 'HTMLTextAreaField',
                                'name' => 'pages',
+                               'label-message' => 'export-manual',
                                'nodata' => true,
-                               'cols' => 40,
                                'rows' => 10,
                                'default' => $page,
                        ),
@@ -301,7 +301,7 @@ class SpecialExport extends SpecialPage {
                        );
                }
 
-               $htmlForm = HTMLForm::factory( 'div', $formDescriptor, $this->getContext() );
+               $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() );
                $htmlForm->setSubmitTextMsg( 'export-submit' );
                $htmlForm->prepareForm()->displayForm( false );
                $this->addHelpLink( 'Help:Export' );
index bb57ee0..323903e 100644 (file)
@@ -241,7 +241,7 @@ class FileDuplicateSearchPage extends QueryPage {
         * @return string[] Matching subpages
         */
        public function prefixSearchSubpages( $search, $limit, $offset ) {
-               $title = Title::newFromText( $search );
+               $title = Title::newFromText( $search, NS_FILE );
                if ( !$title || $title->getNamespace() !== NS_FILE ) {
                        // No prefix suggestion outside of file namespace
                        return array();
index 5ca90ed..0574dbc 100644 (file)
@@ -51,6 +51,10 @@ class SpecialImport extends SpecialPage {
                parent::__construct( 'Import', 'import' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Execute
         * @param string|null $par
@@ -602,7 +606,11 @@ class ImportReporter extends ContextSource {
                                        $successCount )->inContentLanguage()->text();
                                $action = 'upload';
                        } else {
-                               $interwikiTitleStr = $this->mInterwiki . ':' . $foreignTitle->getFullText();
+                               $pageTitle = $foreignTitle->getFullText();
+                               $fullInterwikiPrefix = $this->mInterwiki;
+                               Hooks::run( 'ImportLogInterwikiLink', array( &$fullInterwikiPrefix, &$pageTitle ) );
+
+                               $interwikiTitleStr = $fullInterwikiPrefix . ':' . $pageTitle;
                                $interwiki = '[[:' . $interwikiTitleStr . ']]';
                                $detail = $this->msg( 'import-logentry-interwiki-detail' )->numParams(
                                        $successCount )->params( $interwiki )->inContentLanguage()->text();
index fbdefea..bb35130 100644 (file)
@@ -106,8 +106,6 @@ class SpecialJavaScriptTest extends SpecialPage {
                        return;
                }
 
-               $out->addModules( 'mediawiki.special.javaScriptTest' );
-
                $method = 'view' . ucfirst( $framework );
                $this->$method();
                $out->setPageTitle( $this->msg(
index 317b62f..3f47f91 100644 (file)
@@ -87,7 +87,6 @@ class ListDuplicatedFilesPage extends QueryPage {
                }
        }
 
-
        /**
         * @param Skin $skin
         * @param object $result Result row
index 8de4e2f..9a73a25 100644 (file)
@@ -57,6 +57,24 @@ class SpecialListFiles extends IncludableSpecialPage {
                }
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'media';
        }
diff --git a/includes/specials/SpecialListgrants.php b/includes/specials/SpecialListgrants.php
new file mode 100644 (file)
index 0000000..c5eea3f
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+/**
+ * Implements Special:Listgrants
+ *
+ * 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
+ */
+
+/**
+ * This special page lists all defined rights grants and the associated rights.
+ * See also @ref $wgGrantPermissions and @ref $wgGrantPermissionGroups.
+ *
+ * @ingroup SpecialPage
+ */
+class SpecialListGrants extends SpecialPage {
+       function __construct() {
+               parent::__construct( 'Listgrants' );
+       }
+
+       /**
+        * Show the special page
+        * @param string|null $par
+        */
+       public function execute( $par ) {
+               $this->setHeaders();
+               $this->outputHeader();
+
+               $out = $this->getOutput();
+               $out->addModuleStyles( 'mediawiki.special' );
+
+               $out->addHTML(
+                       \Html::openElement( 'table',
+                               array( 'class' => 'wikitable mw-listgrouprights-table' ) ) .
+                               '<tr>' .
+                               \Html::element( 'th', null, $this->msg( 'listgrants-grant' )->text() ) .
+                               \Html::element( 'th', null, $this->msg( 'listgrants-rights' )->text() ) .
+                               '</tr>'
+               );
+
+               foreach ( $this->getConfig()->get( 'GrantPermissions' ) as $grant => $rights ) {
+                       $descs = array();
+                       $rights = array_filter( $rights ); // remove ones with 'false'
+                       foreach ( $rights as $permission => $granted ) {
+                               $descs[] = $this->msg(
+                                       'listgrouprights-right-display',
+                                       \User::getRightDescription( $permission ),
+                                       '<span class="mw-listgrants-right-name">' . $permission . '</span>'
+                               )->parse();
+                       }
+                       if ( !count( $descs ) ) {
+                               $grantCellHtml = '';
+                       } else {
+                               sort( $descs );
+                               $grantCellHtml = '<ul><li>' . implode( "</li>\n<li>", $descs ) . '</li></ul>';
+                       }
+
+                       $id = \Sanitizer::escapeId( $grant );
+                       $out->addHTML( \Html::rawElement( 'tr', array( 'id' => $id ),
+                               "<td>" . $this->msg( "grant-$grant" )->escaped() . "</td>" .
+                               "<td>" . $grantCellHtml . '</td>'
+                       ) );
+               }
+
+               $out->addHTML( \Html::closeElement( 'table' ) );
+       }
+
+       protected function getGroupName() {
+               return 'users';
+       }
+}
index 382b03f..241db93 100644 (file)
@@ -185,7 +185,7 @@ class SpecialListGroupRights extends SpecialPage {
                                        array(),
                                        Linker::link(
                                                SpecialPage::getTitleFor( 'Allpages' ),
-                                               $namespaceText,
+                                               htmlspecialchars( $namespaceText ),
                                                array(),
                                                array( 'namespace' => $namespace )
                                        )
index a276197..0d495a0 100644 (file)
@@ -33,6 +33,10 @@ class SpecialLockdb extends FormSpecialPage {
                parent::__construct( 'Lockdb', 'siteadmin' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function requiresWrite() {
                return false;
        }
index e44ce5f..28ff1c7 100644 (file)
@@ -258,6 +258,33 @@ class SpecialLog extends SpecialPage {
                                $this->msg( 'log-edit-tags' )->text()
                        ) . "\n";
                }
+
+               // Select: All, None, Invert
+               $links = array();
+               $links[] = Html::element(
+                       'a', array( 'href' => '#', 'id' => 'checkbox-all' ),
+                       $this->msg( 'checkbox-all' )->text()
+               );
+               $links[] = Html::element(
+                       'a', array( 'href' => '#', 'id' => 'checkbox-none' ),
+                       $this->msg( 'checkbox-none' )->text()
+               );
+               $links[] = Html::element(
+                       'a', array( 'href' => '#', 'id' => 'checkbox-invert' ),
+                       $this->msg( 'checkbox-invert' )->text()
+               );
+
+               $buttons .= Html::rawElement( 'p',
+                       array(
+                               'class' => "mw-checkbox-toggle-controls"
+                       ),
+                       $this->msg( 'checkbox-select' )
+                               ->rawParams( $this->getLanguage()->commaList( $links ) )->escaped()
+               );
+
+               $this->getOutput()->addModules( 'mediawiki.checkboxtoggle' );
+               $this->getOutput()->addModuleStyles( 'mediawiki.checkboxtoggle.styles' );
+
                $s .= $buttons . $formcontents . $buttons;
                $s .= Html::closeElement( 'form' );
 
index aeebc42..fb78a40 100644 (file)
@@ -139,9 +139,12 @@ class MediaStatisticsPage extends QueryPage {
                }
                if ( $prevMediaType !== null ) {
                        $this->outputTableEnd();
+                       // add total size of all files
+                       $this->outputMediaType( 'total' );
                        $this->getOutput()->addWikiText(
                                $this->msg( 'mediastatistics-allbytes' )
                                        ->numParams( $this->totalSize )
+                                       ->sizeParams( $this->totalSize )
                                        ->text()
                        );
                }
@@ -155,6 +158,8 @@ class MediaStatisticsPage extends QueryPage {
                $this->getOutput()->addWikiText(
                                $this->msg( 'mediastatistics-bytespertype' )
                                        ->numParams( $this->totalPerType )
+                                       ->sizeParams( $this->totalPerType )
+                                       ->numParams( $this->makePercentPretty( $this->totalPerType / $this->totalBytes ) )
                                        ->text()
                );
                $this->totalSize += $this->totalPerType;
index f11ed9a..0a25180 100644 (file)
@@ -68,6 +68,10 @@ class SpecialMergeHistory extends SpecialPage {
                parent::__construct( 'MergeHistory', 'mergehistory' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * @return void
         */
@@ -478,7 +482,7 @@ class SpecialMergeHistory extends SpecialPage {
 
                $targetLink = Linker::link(
                        $targetTitle,
-                       $targetTitle->getPrefixedText(),
+                       null,
                        array(),
                        array( 'redirect' => 'no' )
                );
index a1bd2fa..27d2304 100644 (file)
@@ -33,7 +33,6 @@ class MovePageForm extends UnlistedSpecialPage {
        /** @var Title */
        protected $newTitle;
 
-
        /** @var string Text input */
        protected $reason;
 
@@ -63,6 +62,10 @@ class MovePageForm extends UnlistedSpecialPage {
                parent::__construct( 'Movepage' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par ) {
                $this->useTransactionalTimeLimit();
 
@@ -146,22 +149,23 @@ class MovePageForm extends UnlistedSpecialPage {
                $out->addModuleStyles( 'mediawiki.special.movePage.styles' );
                $this->addHelpLink( 'Help:Moving a page' );
 
+               $out->addWikiMsg( $this->getConfig()->get( 'FixDoubleRedirects' ) ?
+                       'movepagetext' :
+                       'movepagetext-noredirectfixer'
+               );
+
                if ( $this->oldTitle->getNamespace() == NS_USER && !$this->oldTitle->isSubpage() ) {
                        $out->wrapWikiMsg(
-                               "<div class=\"error mw-moveuserpage-warning\">\n$1\n</div>",
+                               "<div class=\"warningbox mw-moveuserpage-warning\">\n$1\n</div>",
                                'moveuserpage-warning'
                        );
                } elseif ( $this->oldTitle->getNamespace() == NS_CATEGORY ) {
                        $out->wrapWikiMsg(
-                               "<div class=\"error mw-movecategorypage-warning\">\n$1\n</div>",
+                               "<div class=\"warningbox mw-movecategorypage-warning\">\n$1\n</div>",
                                'movecategorypage-warning'
                        );
                }
 
-               $out->addWikiMsg( $this->getConfig()->get( 'FixDoubleRedirects' ) ?
-                       'movepagetext' :
-                       'movepagetext-noredirectfixer'
-               );
                $submitVar = 'wpMove';
                $confirm = false;
 
@@ -186,7 +190,10 @@ class MovePageForm extends UnlistedSpecialPage {
                if ( count( $err ) == 1 && isset( $err[0][0] ) && $err[0][0] == 'articleexists'
                        && $newTitle->quickUserCan( 'delete', $user )
                ) {
-                       $out->addWikiMsg( 'delete_and_move_text', $newTitle->getPrefixedText() );
+                       $out->wrapWikiMsg(
+                               "<div class='warningbox'>\n$1\n</div>\n",
+                               array( 'delete_and_move_text', $newTitle->getPrefixedText() )
+                       );
                        $submitVar = 'wpDeleteAndMove';
                        $confirm = true;
                        $err = array();
@@ -195,7 +202,13 @@ class MovePageForm extends UnlistedSpecialPage {
                if ( count( $err ) == 1 && isset( $err[0][0] ) && $err[0][0] == 'file-exists-sharedrepo'
                        && $user->isAllowed( 'reupload-shared' )
                ) {
-                       $out->addWikiMsg( 'move-over-sharedrepo', $newTitle->getPrefixedText() );
+                       $out->wrapWikiMsg(
+                               "<div class='warningbox'>\n$1\n</div>\n",
+                               array(
+                                       'move-over-sharedrepo',
+                                       $newTitle->getPrefixedText()
+                               )
+                       );
                        $submitVar = 'wpMoveOverSharedFile';
                        $err = array();
                }
@@ -225,7 +238,7 @@ class MovePageForm extends UnlistedSpecialPage {
                }
 
                if ( count( $err ) ) {
-                       $out->addHTML( "<div class='error'>\n" );
+                       $out->addHTML( "<div class='errorbox'>\n" );
                        $action_desc = $this->msg( 'action-move' )->plain();
                        $out->addWikiMsg( 'permissionserrorstext-withaction', count( $err ), $action_desc );
 
index 6b7c038..53fb45e 100644 (file)
@@ -81,9 +81,20 @@ class NewFilesPager extends ReverseChronologicalPager {
         */
        protected $gallery;
 
+       /**
+        * @var bool
+        */
+       protected $showBots;
+
+       /**
+        * @var bool
+        */
+       protected $hidePatrolled;
+
        function __construct( IContextSource $context, $par = null ) {
                $this->like = $context->getRequest()->getText( 'like' );
-               $this->showbots = $context->getRequest()->getBool( 'showbots', 0 );
+               $this->showBots = $context->getRequest()->getBool( 'showbots', 0 );
+               $this->hidePatrolled = $context->getRequest()->getBool( 'hidepatrolled', 0 );
                if ( is_numeric( $par ) ) {
                        $this->setLimit( $par );
                }
@@ -95,7 +106,7 @@ class NewFilesPager extends ReverseChronologicalPager {
                $conds = $jconds = array();
                $tables = array( 'image' );
 
-               if ( !$this->showbots ) {
+               if ( !$this->showBots ) {
                        $groupsWithBotPermission = User::getGroupsWithPermission( 'bot' );
 
                        if ( count( $groupsWithBotPermission ) ) {
@@ -111,6 +122,21 @@ class NewFilesPager extends ReverseChronologicalPager {
                        }
                }
 
+               if ( $this->hidePatrolled ) {
+                       $tables[] = 'recentchanges';
+                       $conds['rc_type'] = RC_LOG;
+                       $conds['rc_log_type'] = 'upload';
+                       $conds['rc_patrolled'] = 0;
+                       $jconds['recentchanges'] = array(
+                               'INNER JOIN',
+                               array(
+                                       'rc_title = img_name',
+                                       'rc_user = img_user',
+                                       'rc_timestamp = img_timestamp'
+                               )
+                       );
+               }
+
                if ( !$this->getConfig()->get( 'MiserMode' ) && $this->like !== null ) {
                        $dbr = wfGetDB( DB_SLAVE );
                        $likeObj = Title::newFromText( $this->like );
@@ -185,6 +211,11 @@ class NewFilesPager extends ReverseChronologicalPager {
                                'label-message' => 'newimages-showbots',
                                'name' => 'showbots',
                        ),
+                       'hidepatrolled' => array(
+                               'type' => 'check',
+                               'label-message' => 'newimages-hidepatrolled',
+                               'name' => 'hidepatrolled',
+                       ),
                        'limit' => array(
                                'type' => 'hidden',
                                'default' => $this->mLimit,
@@ -201,6 +232,10 @@ class NewFilesPager extends ReverseChronologicalPager {
                        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 );
index 9323551..69a9d48 100644 (file)
@@ -38,6 +38,10 @@ class SpecialPageLanguage extends FormSpecialPage {
                parent::__construct( 'PageLanguage', 'pagelang' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        protected function preText() {
                $this->getOutput()->addModules( 'mediawiki.special.pageLanguage' );
        }
@@ -101,6 +105,7 @@ class SpecialPageLanguage extends FormSpecialPage {
 
        public function alterForm( HTMLForm $form ) {
                Hooks::run( 'LanguageSelector', array( $this->getOutput(), 'mw-languageselector' ) );
+               $form->setSubmitTextMsg( 'pagelang-submit' );
        }
 
        /**
index 292b575..21ce1e1 100644 (file)
@@ -51,6 +51,10 @@ class SpecialPasswordReset extends FormSpecialPage {
                parent::__construct( 'PasswordReset', 'editmyprivateinfo' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function userCanExecute( User $user ) {
                return $this->canChangePassword( $user ) === true && parent::userCanExecute( $user );
        }
index 49ab6d5..965a36e 100644 (file)
@@ -31,6 +31,10 @@ class SpecialPreferences extends SpecialPage {
                parent::__construct( 'Preferences' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par ) {
                $this->setHeaders();
                $this->outputHeader();
@@ -49,7 +53,11 @@ class SpecialPreferences extends SpecialPage {
                $out->addModules( 'mediawiki.special.preferences' );
                $out->addModuleStyles( 'mediawiki.special.preferences.styles' );
 
-               if ( $this->getRequest()->getCheck( 'success' ) ) {
+               $request = $this->getRequest();
+               if ( $request->getSessionData( 'specialPreferencesSaveSuccess' ) ) {
+                       // Remove session data for the success message
+                       $request->setSessionData( 'specialPreferencesSaveSuccess', null );
+
                        $out->wrapWikiMsg(
                                Html::rawElement(
                                        'div',
@@ -128,12 +136,14 @@ class SpecialPreferences extends SpecialPage {
                        throw new PermissionsError( 'editmyoptions' );
                }
 
-               $user = $this->getUser();
+               $user = $this->getUser()->getInstanceForUpdate();
                $user->resetOptions( 'all', $this->getContext() );
                $user->saveSettings();
 
-               $url = $this->getPageTitle()->getFullURL( 'success' );
+               // Set session data for the success message
+               $this->getRequest()->setSessionData( 'specialPreferencesSaveSuccess', 1 );
 
+               $url = $this->getPageTitle()->getFullURL();
                $this->getOutput()->redirect( $url );
 
                return true;
diff --git a/includes/specials/SpecialRandomrootpage.php b/includes/specials/SpecialRandomrootpage.php
new file mode 100644 (file)
index 0000000..31a290d
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * Implements Special:Randomrootpage
+ *
+ * Copyright © 2008 Hojjat (aka Huji)
+ *
+ * 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
+ */
+
+class SpecialRandomrootpage extends RandomPage {
+
+       public function __construct() {
+               parent::__construct( 'Randomrootpage' );
+               $dbr = wfGetDB( DB_SLAVE );
+               $this->extra[] = 'page_title NOT ' . $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString() );
+       }
+
+       // Don't select redirects
+       public function isRedirect() {
+               return false;
+       }
+}
index 4848d01..f030231 100644 (file)
@@ -396,7 +396,7 @@ class SpecialRecentChanges extends ChangesListSpecialPage {
                $nondefaults = $opts->getChangedValues();
 
                $panel = array();
-               $panel[] = self::makeLegend( $this->getContext() );
+               $panel[] = $this->makeLegend();
                $panel[] = $this->optionsPanel( $defaults, $nondefaults, $numRows );
                $panel[] = '<hr />';
 
index 553e2b1..328aa11 100644 (file)
@@ -158,6 +158,93 @@ class SpecialRedirect extends FormSpecialPage {
                ) );
        }
 
+       /**
+        * Handle Special:Redirect/logid/xxx
+        * (by redirecting to index.php?title=Special:Log)
+        *
+        * @since 1.27
+        * @return string|null Url to redirect to, or null if $mValue is invalid.
+        */
+       function dispatchLog() {
+               $logid = $this->mValue;
+               if ( !ctype_digit( $logid ) ) {
+                       return null;
+               }
+               $logid = (int)$logid;
+               if ( $logid === 0 ) {
+                       return null;
+               }
+
+               $logparams = array(
+                       'log_id',
+                       'log_timestamp',
+                       'log_type',
+                       'log_user_text',
+               );
+
+               $dbr = wfGetDB( DB_SLAVE );
+
+               // Gets the nested SQL statement which
+               // returns timestamp of the log with the given log ID
+               $inner = $dbr->selectSQLText(
+                       'logging',
+                       array( 'log_timestamp' ),
+                       array( 'log_id' => $logid )
+               );
+
+               // Returns all fields mentioned in $logparams of the logs
+               // with the same timestamp as the one returned by the statement above
+               $logsSameTimestamps = $dbr->select(
+                       'logging',
+                       $logparams,
+                       array( "log_timestamp = ($inner)" )
+               );
+               if ( $logsSameTimestamps->numRows() === 0 ) {
+                       return null;
+               }
+
+               // Stores the row with the same log ID as the one given
+               $rowMain = array();
+               foreach ( $logsSameTimestamps as $row ) {
+                       if ( (int)$row->log_id === $logid ) {
+                               $rowMain = $row;
+                       }
+               }
+
+               array_shift( $logparams );
+
+               // Stores all the rows with the same values in each column
+               // as $rowMain
+               foreach ( $logparams as $cond ) {
+                       $matchedRows = array();
+                       foreach ( $logsSameTimestamps as $row ) {
+                               if ( $row->$cond === $rowMain->$cond ) {
+                                       $matchedRows[] = $row;
+                               }
+                       }
+                       if ( count( $matchedRows ) === 1 ) {
+                               break;
+                       }
+                       $logsSameTimestamps = $matchedRows;
+               }
+               $query = array( 'title' => 'Special:Log', 'limit' => count( $matchedRows ) );
+
+               // A map of database field names from table 'logging' to the values of $logparams
+               $keys = array(
+                       'log_timestamp' => 'offset',
+                       'log_type' => 'type',
+                       'log_user_text' => 'user'
+               );
+
+               foreach ( $logparams as $logKey ) {
+                       $query[$keys[$logKey]] = $matchedRows[0]->$logKey;
+               }
+               $query['offset'] = $query['offset'] + 1;
+               $url = $query;
+
+               return wfAppendQuery( wfScript( 'index' ), $url );
+       }
+
        /**
         * Use appropriate dispatch* method to obtain a redirection URL,
         * and either: redirect, set a 404 error code and error message,
@@ -181,6 +268,9 @@ class SpecialRedirect extends FormSpecialPage {
                        case 'page':
                                $url = $this->dispatchPage();
                                break;
+                       case 'logid':
+                               $url = $this->dispatchLog();
+                               break;
                        default:
                                $this->getOutput()->setStatusCode( 404 );
                                $url = null;
@@ -207,11 +297,12 @@ class SpecialRedirect extends FormSpecialPage {
                $ns = array(
                        // subpage => message
                        // Messages: redirect-user, redirect-page, redirect-revision,
-                       // redirect-file
+                       // redirect-file, redirect-logid
                        'user' => $mp . '-user',
                        'page' => $mp . '-page',
                        'revision' => $mp . '-revision',
                        'file' => $mp . '-file',
+                       'logid' => $mp . '-logid',
                );
                $a = array();
                $a['type'] = array(
@@ -273,6 +364,7 @@ class SpecialRedirect extends FormSpecialPage {
                        'page',
                        'revision',
                        'user',
+                       'logid',
                );
        }
 
index cba5a44..38e977b 100644 (file)
@@ -34,6 +34,10 @@ class SpecialResetTokens extends FormSpecialPage {
                parent::__construct( 'ResetTokens' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Returns the token information list for this page after running
         * the hook and filtering out disabled preferences.
index 6dcbcb1..44e44ba 100644 (file)
@@ -109,6 +109,10 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                parent::__construct( 'Revisiondelete', 'deletedhistory' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par ) {
                $this->useTransactionalTimeLimit();
 
index 4217553..eeaf2d3 100644 (file)
@@ -34,6 +34,10 @@ class SpecialRunJobs extends UnlistedSpecialPage {
                parent::__construct( 'RunJobs' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par = '' ) {
                $this->getOutput()->disable();
 
index ba11862..74a1322 100644 (file)
@@ -38,18 +38,27 @@ class ShortPagesPage extends QueryPage {
        }
 
        public function getQueryInfo() {
+               $tables = array( 'page' );
+               $conds = array(
+                       'page_namespace' => MWNamespace::getContentNamespaces(),
+                       'page_is_redirect' => 0
+               );
+               $joinConds = array();
+               $options = array( 'USE INDEX' => array( 'page' => 'page_redirect_namespace_len' ) );
+
+               // Allow extensions to modify the query
+               Hooks::run( 'ShortPagesQuery', array( &$tables, &$conds, &$joinConds, &$options ) );
+
                return array(
-                       'tables' => array( 'page' ),
+                       'tables' => $tables,
                        'fields' => array(
                                'namespace' => 'page_namespace',
                                'title' => 'page_title',
                                'value' => 'page_len'
                        ),
-                       'conds' => array(
-                               'page_namespace' => MWNamespace::getContentNamespaces(),
-                               'page_is_redirect' => 0
-                       ),
-                       'options' => array( 'USE INDEX' => 'page_redirect_namespace_len' )
+                       'conds' => $conds,
+                       'join_conds' => $joinConds,
+                       'options' => $options
                );
        }
 
index f81f1c3..cf807ef 100644 (file)
@@ -36,6 +36,10 @@ class SpecialUnblock extends SpecialPage {
                parent::__construct( 'Unblock', 'block' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par ) {
                $this->checkPermissions();
                $this->checkReadOnly();
@@ -237,6 +241,24 @@ class SpecialUnblock extends SpecialPage {
                return true;
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index aada064..f99a52d 100644 (file)
@@ -51,6 +51,10 @@ class PageArchive {
                $this->config = $config;
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * List all deleted pages recorded in the archive table. Returns result
         * wrapper with (ar_namespace, ar_title, count) fields, ordered by page
@@ -568,7 +572,11 @@ class PageArchive {
                                return Status::newFatal( "undeleterevdel" );
                        }
                        // Safe to insert now...
-                       $newid = $article->insertOn( $dbw );
+                       $newid = $article->insertOn( $dbw, $row->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...
index dc03a4a..b73e3c5 100644 (file)
@@ -32,6 +32,10 @@ class SpecialUnlockdb extends FormSpecialPage {
                parent::__construct( 'Unlockdb', 'siteadmin' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function requiresWrite() {
                return false;
        }
index c8c4642..5b3c43e 100644 (file)
@@ -38,6 +38,10 @@ class SpecialUpload extends SpecialPage {
                parent::__construct( 'Upload', 'upload' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /** Misc variables **/
 
        /** @var WebRequest|FauxRequest The request this form is supposed to handle */
@@ -405,8 +409,14 @@ class SpecialUpload extends SpecialPage {
 
                $form = $this->getUploadForm( $warningHtml, $sessionKey, /* $hideIgnoreWarning */ true );
                $form->setSubmitText( $this->msg( 'upload-tryagain' )->text() );
-               $form->addButton( 'wpUploadIgnoreWarning', $this->msg( 'ignorewarning' )->text() );
-               $form->addButton( 'wpCancelUpload', $this->msg( 'reuploaddesc' )->text() );
+               $form->addButton( array(
+                       'name' => 'wpUploadIgnoreWarning',
+                       'value' => $this->msg( 'ignorewarning' )->text()
+               ) );
+               $form->addButton( array(
+                       'name' => 'wpCancelUpload',
+                       'value' => $this->msg( 'reuploaddesc' )->text()
+               ) );
 
                $this->showUploadForm( $form );
 
index eb34008..1cec116 100644 (file)
@@ -52,6 +52,10 @@ class SpecialUploadStash extends UnlistedSpecialPage {
                parent::__construct( 'UploadStash', 'upload' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Execute page -- can output a file directly or show a listing of them.
         *
index fec1e3a..24e1675 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup SpecialPage
  */
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Session\SessionManager;
 
 /**
  * Implements Special:UserLogin
@@ -132,6 +133,10 @@ class LoginForm extends SpecialPage {
                $wgUseMediaWikiUIEverywhere = true;
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Returns an array of all valid error messages.
         *
@@ -263,9 +268,9 @@ class LoginForm extends SpecialPage {
         * @param string|null $subPage
         */
        public function execute( $subPage ) {
-               if ( session_id() == '' ) {
-                       wfSetupSession();
-               }
+               // Make sure session is persisted
+               $session = MediaWiki\Session\SessionManager::getGlobalSession();
+               $session->persist();
 
                $this->load();
 
@@ -276,6 +281,17 @@ class LoginForm extends SpecialPage {
                }
                $this->setHeaders();
 
+               // Make sure it's possible to log in
+               if ( $this->mType !== 'signup' && !$session->canSetUser() ) {
+                       throw new ErrorPageError(
+                               'cannotloginnow-title',
+                               'cannotloginnow-text',
+                               array(
+                                       $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
@@ -1376,7 +1392,7 @@ class LoginForm extends SpecialPage {
                        if ( $user->isLoggedIn() ) {
                                $this->mUsername = $user->getName();
                        } else {
-                               $this->mUsername = $this->getRequest()->getCookie( 'UserName' );
+                               $this->mUsername = $this->getRequest()->getSession()->suggestLoginUsername();
                        }
                }
 
@@ -1552,7 +1568,8 @@ class LoginForm extends SpecialPage {
        function hasSessionCookie() {
                global $wgDisableCookieCheck;
 
-               return $wgDisableCookieCheck ? true : $this->getRequest()->checkSessionCookie();
+               return $wgDisableCookieCheck ||
+                       SessionManager::singleton()->getPersistedSessionId( $this->getRequest() ) !== null;
        }
 
        /**
@@ -1571,7 +1588,7 @@ class LoginForm extends SpecialPage {
        public static function setLoginToken() {
                global $wgRequest;
                // Generate a token directly instead of using $user->getEditToken()
-               // because the latter reuses $_SESSION['wsEditToken']
+               // because the latter reuses wsEditToken in the session
                $wgRequest->setSessionData( 'wsLoginToken', MWCryptRand::generateHex( 32 ) );
        }
 
@@ -1617,7 +1634,7 @@ class LoginForm extends SpecialPage {
                        $wgCookieSecure = false;
                }
 
-               wfResetSessionID();
+               MediaWiki\Session\SessionManager::getGlobalSession()->resetId();
        }
 
        /**
index 080dc11..6e34690 100644 (file)
@@ -31,6 +31,10 @@ class SpecialUserlogout extends UnlistedSpecialPage {
                parent::__construct( 'Userlogout' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        function execute( $par ) {
                /**
                 * Some satellite ISPs use broken precaching schemes that log people out straight after
@@ -44,6 +48,18 @@ class SpecialUserlogout extends UnlistedSpecialPage {
                $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',
+                               array(
+                                       $session->getProvider()->describe( RequestContext::getMain()->getLanguage() )
+                               )
+                       );
+               }
+
                $user = $this->getUser();
                $oldName = $user->getName();
                $user->logout();
index ea22274..7351c33 100644 (file)
@@ -41,6 +41,10 @@ class UserrightsPage extends SpecialPage {
                parent::__construct( 'Userrights' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function isRestricted() {
                return true;
        }
@@ -469,7 +473,12 @@ class UserrightsPage extends SpecialPage {
                                        $this->mFetchedUser === null ? array( 'autofocus' => '' ) : array()
                                )
                        ) . ' ' .
-                       Xml::submitButton( $this->msg( 'editusergroup' )->text() ) .
+                       Xml::submitButton(
+                               $this->msg(
+                                       'editusergroup',
+                                       $this->mFetchedUser === null ? '[]' : $this->mFetchedUser->getName()
+                               )->text()
+                       ) .
                        Html::closeElement( 'fieldset' ) .
                        Html::closeElement( 'form' ) . "\n"
                );
@@ -599,7 +608,7 @@ class UserrightsPage extends SpecialPage {
                                <tr>
                                        <td></td>
                                        <td class='mw-submit'>" .
-                                               Xml::submitButton( $this->msg( 'saveusergroups' )->text(),
+                                               Xml::submitButton( $this->msg( 'saveusergroups', $user->getName() )->text(),
                                                        array( 'name' => 'saveusergroups' ) +
                                                                Linker::tooltipAndAccesskeyAttribs( 'userrights-set' )
                                                ) .
@@ -776,6 +785,24 @@ class UserrightsPage extends SpecialPage {
                LogEventsList::showLogExtract( $output, 'rights', $user->getUserPage() );
        }
 
+       /**
+        * Return an array of subpages beginning with $search that this special page will accept.
+        *
+        * @param string $search Prefix to search for
+        * @param int $limit Maximum number of results to return (usually 10)
+        * @param int $offset Number of results to skip (usually 0)
+        * @return string[] Matching subpages
+        */
+       public function prefixSearchSubpages( $search, $limit, $offset ) {
+               $user = User::newFromName( $search );
+               if ( !$user ) {
+                       // No prefix suggestion for invalid user
+                       return array();
+               }
+               // Autocomplete subpage as user list - public to allow caching
+               return UserNamePrefixSearch::search( 'public', $search, $limit, $offset );
+       }
+
        protected function getGroupName() {
                return 'users';
        }
index 80560be..43228fa 100644 (file)
@@ -707,7 +707,7 @@ class SpecialVersion extends SpecialPage {
         *  - The name of (name), and URL link to (url), the extension
         *  - Official version number (version) and if available version control system
         *    revision (path), link, and date
-        *  - If available the short name of the license (license-name) and a linke
+        *  - If available the short name of the license (license-name) and a link
         *    to ((LICENSE)|(COPYING))(\.txt)? if it exists.
         *  - Description of extension (descriptionmsg or description)
         *  - List of authors (author) and link to a ((AUTHORS)|(CREDITS))(\.txt)? file if it exists
@@ -840,7 +840,7 @@ class SpecialVersion extends SpecialPage {
                        if ( isset( $extension['license-name'] ) ) {
                                $licenseName = $out->parseInline( $extension['license-name'] );
                        } elseif ( $this->getExtLicenseFileName( $extensionPath ) ) {
-                               $licenseName = $this->msg( 'version-ext-license' );
+                               $licenseName = $this->msg( 'version-ext-license' )->escaped();
                        }
                        if ( $licenseName !== null ) {
                                $licenseLink = Linker::link(
index c894a79..7ab6578 100644 (file)
@@ -605,7 +605,7 @@ class SpecialWatchlist extends ChangesListSpecialPage {
                        array( 'id' => 'mw-watchlist-options' )
                );
 
-               $form .= SpecialRecentChanges::makeLegend( $this->getContext() );
+               $form .= $this->makeLegend();
 
                $this->getOutput()->addHTML( $form );
        }
diff --git a/includes/templates/EnhancedChangesListGroup.mustache b/includes/templates/EnhancedChangesListGroup.mustache
new file mode 100644 (file)
index 0000000..352eb17
--- /dev/null
@@ -0,0 +1,28 @@
+<table class="{{# tableClasses }}{{ . }} {{/ tableClasses }}">
+       <tr>
+               <td>
+                       <span class="mw-collapsible-toggle mw-collapsible-arrow mw-enhancedchanges-arrow mw-enhancedchanges-arrow-space"></span>
+               </td>
+               <td class="mw-enhanced-rc">{{{ collectedRcFlags }}}&#160;{{ timestamp }}&#160;</td>
+               <td>
+                       {{# rev-deleted-event }}<span class="history-deleted">{{{ . }}}</span>{{/ rev-deleted-event }}
+                       {{{ articleLink }}}{{{ languageDirMark }}}{{{ logText }}}
+                       <span class="mw-changeslist-separator">. .</span>
+                       {{# charDifference }}{{{ . }}} <span class="mw-changeslist-separator">. .</span>{{/ charDifference }}
+                       <span class="changedby">{{{ users }}}</span>
+                       {{ numberofWatchingusers }}
+               </td>
+       </tr>
+       {{# lines }}
+       <tr class="{{# classes }}{{ . }} {{/ classes }}">
+               <td></td>
+               <td class="mw-enhanced-rc">{{{ recentChangesFlags }}}&#160;</td>
+               <td class="mw-enhanced-rc-nested">
+                       {{# timestampLink }}
+                       <span class="mw-enhanced-rc-time">{{{ . }}}</span>
+                       {{/ timestampLink }}
+                       {{# data }}{{{ . }}}{{/ data }}
+               </td>
+       </tr>
+       {{/ lines }}
+</table>
index 75540c1..1276842 100644 (file)
@@ -178,17 +178,10 @@ class UploadFromStash extends UploadBase {
        }
 
        /**
-        * Perform the upload, then remove the database record afterward.
-        * @param string $comment
-        * @param string $pageText
-        * @param bool $watch
-        * @param User $user
-        * @return Status
+        * Remove the database record after a successful upload.
         */
-       public function performUpload( $comment, $pageText, $watch, $user ) {
-               $rv = parent::performUpload( $comment, $pageText, $watch, $user );
+       public function postProcessUpload() {
+               parent::postProcessUpload();
                $this->unsaveUploadedFile();
-
-               return $rv;
        }
 }
index f897a79..23a4962 100644 (file)
@@ -390,7 +390,7 @@ class UploadFromUrl extends UploadBase {
                        'userName' => $user->getName(),
                        'leaveMessage' => $this->mAsync == 'async-leavemessage',
                        'ignoreWarnings' => $this->mIgnoreWarnings,
-                       'sessionId' => session_id(),
+                       'sessionId' => MediaWiki\Session\SessionManager::getGlobalSession()->getId(),
                        'sessionKey' => $sessionKey,
                ) );
                $job->initializeSessionData();
diff --git a/includes/user/BotPassword.php b/includes/user/BotPassword.php
new file mode 100644 (file)
index 0000000..6f713f1
--- /dev/null
@@ -0,0 +1,447 @@
+<?php
+/**
+ * Utility class for bot passwords
+ *
+ * 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
+ */
+
+use MediaWiki\Session\BotPasswordSessionProvider;
+use MediaWiki\Session\SessionInfo;
+
+/**
+ * Utility class for bot passwords
+ * @since 1.27
+ */
+class BotPassword implements IDBAccessObject {
+
+       const APPID_MAXLENGTH = 32;
+
+       /** @var bool */
+       private $isSaved;
+
+       /** @var int */
+       private $centralId;
+
+       /** @var string */
+       private $appId;
+
+       /** @var string */
+       private $token;
+
+       /** @var MWRestrictions */
+       private $restrictions;
+
+       /** @var string[] */
+       private $grants;
+
+       /** @var int */
+       private $flags = self::READ_NORMAL;
+
+       /**
+        * @param object $row bot_passwords database row
+        * @param bool $isSaved Whether the bot password was read from the database
+        * @param int $flags IDBAccessObject read flags
+        */
+       protected function __construct( $row, $isSaved, $flags = self::READ_NORMAL ) {
+               $this->isSaved = $isSaved;
+               $this->flags = $flags;
+
+               $this->centralId = (int)$row->bp_user;
+               $this->appId = $row->bp_app_id;
+               $this->token = $row->bp_token;
+               $this->restrictions = MWRestrictions::newFromJson( $row->bp_restrictions );
+               $this->grants = FormatJson::decode( $row->bp_grants );
+       }
+
+       /**
+        * 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.
+        * @return DatabaseBase
+        */
+       public static function getDB( $db ) {
+               global $wgBotPasswordsCluster, $wgBotPasswordsDatabase;
+
+               $lb = $wgBotPasswordsCluster
+                       ? wfGetLBFactory()->getExternalLB( $wgBotPasswordsCluster )
+                       : wfGetLB( $wgBotPasswordsDatabase );
+               return $lb->getConnectionRef( $db, array(), $wgBotPasswordsDatabase );
+       }
+
+       /**
+        * Load a BotPassword from the database
+        * @param User $user
+        * @param string $appId
+        * @param int $flags IDBAccessObject read flags
+        * @return BotPassword|null
+        */
+       public static function newFromUser( User $user, $appId, $flags = self::READ_NORMAL ) {
+               $centralId = CentralIdLookup::factory()->centralIdFromLocalUser(
+                       $user, CentralIdLookup::AUDIENCE_RAW, $flags
+               );
+               return $centralId ? self::newFromCentralId( $centralId, $appId, $flags ) : null;
+       }
+
+       /**
+        * Load a BotPassword from the database
+        * @param int $centralId from CentralIdLookup
+        * @param string $appId
+        * @param int $flags IDBAccessObject read flags
+        * @return BotPassword|null
+        */
+       public static function newFromCentralId( $centralId, $appId, $flags = self::READ_NORMAL ) {
+               global $wgEnableBotPasswords;
+
+               if ( !$wgEnableBotPasswords ) {
+                       return null;
+               }
+
+               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $flags );
+               $db = self::getDB( $index );
+               $row = $db->selectRow(
+                       'bot_passwords',
+                       array( 'bp_user', 'bp_app_id', 'bp_token', 'bp_restrictions', 'bp_grants' ),
+                       array( 'bp_user' => $centralId, 'bp_app_id' => $appId ),
+                       __METHOD__,
+                       $options
+               );
+               return $row ? new self( $row, true, $flags ) : null;
+       }
+
+       /**
+        * Create an unsaved BotPassword
+        * @param array $data Data to use to create the bot password. Keys are:
+        *  - user: (User) User object to create the password for. Overrides username and centralId.
+        *  - username: (string) Username to create the password for. Overrides centralId.
+        *  - centralId: (int) User central ID to create the password for.
+        *  - appId: (string) App ID for the password.
+        *  - restrictions: (MWRestrictions, optional) Restrictions.
+        *  - grants: (string[], optional) Grants.
+        * @param int $flags IDBAccessObject read flags
+        * @return BotPassword|null
+        */
+       public static function newUnsaved( array $data, $flags = self::READ_NORMAL ) {
+               $row = (object)array(
+                       'bp_user' => 0,
+                       'bp_app_id' => isset( $data['appId'] ) ? trim( $data['appId'] ) : '',
+                       'bp_token' => '**unsaved**',
+                       'bp_restrictions' => isset( $data['restrictions'] )
+                               ? $data['restrictions']
+                               : MWRestrictions::newDefault(),
+                       'bp_grants' => isset( $data['grants'] ) ? $data['grants'] : array(),
+               );
+
+               if (
+                       $row->bp_app_id === '' || strlen( $row->bp_app_id ) > self::APPID_MAXLENGTH ||
+                       !$row->bp_restrictions instanceof MWRestrictions ||
+                       !is_array( $row->bp_grants )
+               ) {
+                       return null;
+               }
+
+               $row->bp_restrictions = $row->bp_restrictions->toJson();
+               $row->bp_grants = FormatJson::encode( $row->bp_grants );
+
+               if ( isset( $data['user'] ) ) {
+                       if ( !$data['user'] instanceof User ) {
+                               return null;
+                       }
+                       $row->bp_user = CentralIdLookup::factory()->centralIdFromLocalUser(
+                               $data['user'], CentralIdLookup::AUDIENCE_RAW, $flags
+                       );
+               } elseif ( isset( $data['username'] ) ) {
+                       $row->bp_user = CentralIdLookup::factory()->centralIdFromName(
+                               $data['username'], CentralIdLookup::AUDIENCE_RAW, $flags
+                       );
+               } elseif ( isset( $data['centralId'] ) ) {
+                       $row->bp_user = $data['centralId'];
+               }
+               if ( !$row->bp_user ) {
+                       return null;
+               }
+
+               return new self( $row, false, $flags );
+       }
+
+       /**
+        * Indicate whether this is known to be saved
+        * @return bool
+        */
+       public function isSaved() {
+               return $this->isSaved;
+       }
+
+       /**
+        * Get the central user ID
+        * @return int
+        */
+       public function getUserCentralId() {
+               return $this->centralId;
+       }
+
+       /**
+        * Get the app ID
+        * @return string
+        */
+       public function getAppId() {
+               return $this->appId;
+       }
+
+       /**
+        * Get the token
+        * @return string
+        */
+       public function getToken() {
+               return $this->token;
+       }
+
+       /**
+        * Get the restrictions
+        * @return MWRestrictions
+        */
+       public function getRestrictions() {
+               return $this->restrictions;
+       }
+
+       /**
+        * Get the grants
+        * @return string[]
+        */
+       public function getGrants() {
+               return $this->grants;
+       }
+
+       /**
+        * Get the separator for combined user name + app ID
+        * @return string
+        */
+       public static function getSeparator() {
+               global $wgUserrightsInterwikiDelimiter;
+               return $wgUserrightsInterwikiDelimiter;
+       }
+
+       /**
+        * Get the password
+        * @return Password
+        */
+       protected function getPassword() {
+               list( $index, $options ) = DBAccessObjectUtils::getDBOptions( $this->flags );
+               $db = self::getDB( $index );
+               $password = $db->selectField(
+                       'bot_passwords',
+                       'bp_password',
+                       array( 'bp_user' => $this->centralId, 'bp_app_id' => $this->appId ),
+                       __METHOD__,
+                       $options
+               );
+               if ( $password === false ) {
+                       return PasswordFactory::newInvalidPassword();
+               }
+
+               $passwordFactory = new \PasswordFactory();
+               $passwordFactory->init( \RequestContext::getMain()->getConfig() );
+               try {
+                       return $passwordFactory->newFromCiphertext( $password );
+               } catch ( PasswordError $ex ) {
+                       return PasswordFactory::newInvalidPassword();
+               }
+       }
+
+       /**
+        * Save the BotPassword to the database
+        * @param string $operation 'update' or 'insert'
+        * @param Password|null $password Password to set.
+        * @return bool Success
+        */
+       public function save( $operation, Password $password = null ) {
+               $conds = array(
+                       'bp_user' => $this->centralId,
+                       'bp_app_id' => $this->appId,
+               );
+               $fields = array(
+                       'bp_token' => MWCryptRand::generateHex( User::TOKEN_LENGTH ),
+                       'bp_restrictions' => $this->restrictions->toJson(),
+                       'bp_grants' => FormatJson::encode( $this->grants ),
+               );
+
+               if ( $password !== null ) {
+                       $fields['bp_password'] = $password->toString();
+               } elseif ( $operation === 'insert' ) {
+                       $fields['bp_password'] = PasswordFactory::newInvalidPassword()->toString();
+               }
+
+               $dbw = self::getDB( DB_MASTER );
+               switch ( $operation ) {
+                       case 'insert':
+                               $dbw->insert( 'bot_passwords', $fields + $conds, __METHOD__, array( 'IGNORE' ) );
+                               break;
+
+                       case 'update':
+                               $dbw->update( 'bot_passwords', $fields, $conds, __METHOD__ );
+                               break;
+
+                       default:
+                               return false;
+               }
+               $ok = (bool)$dbw->affectedRows();
+               if ( $ok ) {
+                       $this->token = $dbw->selectField( 'bot_passwords', 'bp_token', $conds, __METHOD__ );
+                       $this->isSaved = true;
+               }
+               return $ok;
+       }
+
+       /**
+        * Delete the BotPassword from the database
+        * @return bool Success
+        */
+       public function delete() {
+               $conds = array(
+                       'bp_user' => $this->centralId,
+                       'bp_app_id' => $this->appId,
+               );
+               $dbw = self::getDB( DB_MASTER );
+               $dbw->delete( 'bot_passwords', $conds, __METHOD__ );
+               $ok = (bool)$dbw->affectedRows();
+               if ( $ok ) {
+                       $this->token = '**unsaved**';
+                       $this->isSaved = false;
+               }
+               return $ok;
+       }
+
+       /**
+        * Invalidate all passwords for a user, by name
+        * @param string $username User name
+        * @return bool Whether any passwords were invalidated
+        */
+       public static function invalidateAllPasswordsForUser( $username ) {
+               $centralId = CentralIdLookup::factory()->centralIdFromName(
+                       $username, CentralIdLookup::AUDIENCE_RAW, CentralIdLookup::READ_LATEST
+               );
+               return $centralId && self::invalidateAllPasswordsForCentralId( $centralId );
+       }
+
+       /**
+        * Invalidate all passwords for a user, by central ID
+        * @param int $centralId
+        * @return bool Whether any passwords were invalidated
+        */
+       public static function invalidateAllPasswordsForCentralId( $centralId ) {
+               global $wgEnableBotPasswords;
+
+               if ( !$wgEnableBotPasswords ) {
+                       return false;
+               }
+
+               $dbw = self::getDB( DB_MASTER );
+               $dbw->update(
+                       'bot_passwords',
+                       array( 'bp_password' => PasswordFactory::newInvalidPassword()->toString() ),
+                       array( 'bp_user' => $centralId ),
+                       __METHOD__
+               );
+               return (bool)$dbw->affectedRows();
+       }
+
+       /**
+        * Remove all passwords for a user, by name
+        * @param string $username User name
+        * @return bool Whether any passwords were removed
+        */
+       public static function removeAllPasswordsForUser( $username ) {
+               $centralId = CentralIdLookup::factory()->centralIdFromName(
+                       $username, CentralIdLookup::AUDIENCE_RAW, CentralIdLookup::READ_LATEST
+               );
+               return $centralId && self::removeAllPasswordsForCentralId( $centralId );
+       }
+
+       /**
+        * Remove all passwords for a user, by central ID
+        * @param int $centralId
+        * @return bool Whether any passwords were removed
+        */
+       public static function removeAllPasswordsForCentralId( $centralId ) {
+               global $wgEnableBotPasswords;
+
+               if ( !$wgEnableBotPasswords ) {
+                       return false;
+               }
+
+               $dbw = self::getDB( DB_MASTER );
+               $dbw->delete(
+                       'bot_passwords',
+                       array( 'bp_user' => $centralId ),
+                       __METHOD__
+               );
+               return (bool)$dbw->affectedRows();
+       }
+
+       /**
+        * Try to log the user in
+        * @param string $username Combined user name and app ID
+        * @param string $password Supplied password
+        * @param WebRequest $request
+        * @return Status On success, the good status's value is the new Session object
+        */
+       public static function login( $username, $password, WebRequest $request ) {
+               global $wgEnableBotPasswords;
+
+               if ( !$wgEnableBotPasswords ) {
+                       return Status::newFatal( 'botpasswords-disabled' );
+               }
+
+               $manager = MediaWiki\Session\SessionManager::singleton();
+               $provider = $manager->getProvider(
+                       'MediaWiki\\Session\\BotPasswordSessionProvider'
+               );
+               if ( !$provider ) {
+                       return Status::newFatal( 'botpasswords-no-provider' );
+               }
+
+               // Split name into name+appId
+               $sep = self::getSeparator();
+               if ( strpos( $username, $sep ) === false ) {
+                       return Status::newFatal( 'botpasswords-invalid-name', $sep );
+               }
+               list( $name, $appId ) = explode( $sep, $username, 2 );
+
+               // Find the named user
+               $user = User::newFromName( $name );
+               if ( !$user || $user->isAnon() ) {
+                       return Status::newFatal( 'nosuchuser', $name );
+               }
+
+               // Get the bot password
+               $bp = self::newFromUser( $user, $appId );
+               if ( !$bp ) {
+                       return Status::newFatal( 'botpasswords-not-exist', $name, $appId );
+               }
+
+               // Check restrictions
+               $status = $bp->getRestrictions()->check( $request );
+               if ( !$status->isOk() ) {
+                       return Status::newFatal( 'botpasswords-restriction-failed' );
+               }
+
+               // Check the password
+               if ( !$bp->getPassword()->equals( $password ) ) {
+                       return Status::newFatal( 'wrongpassword' );
+               }
+
+               // Ok! Create the session.
+               return Status::newGood( $provider->newSessionForRequest( $user, $bp, $request ) );
+       }
+}
index 638a3e2..4c2b5b7 100644 (file)
@@ -62,6 +62,16 @@ abstract class CentralIdLookup implements IDBAccessObject {
                return self::$instances[$providerId];
        }
 
+       /**
+        * Reset internal cache for unit testing
+        */
+       public static function resetCache() {
+               if ( !defined( 'MW_PHPUNIT_TEST' ) ) {
+                       throw new MWException( __METHOD__ . ' may only be called from unit tests!' );
+               }
+               self::$instances = array();
+       }
+
        final public function getProviderId() {
                return $this->providerId;
        }
index fed9664..47e67e5 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Session\SessionManager;
+
 /**
  * String Some punctuation to prevent editing from broken text-mangling proxies.
  * @ingroup Constants
@@ -99,6 +101,7 @@ class User implements IDBAccessObject {
                'apihighlimits',
                'applychangetags',
                'autoconfirmed',
+               'autocreateaccount',
                'autopatrol',
                'bigdelete',
                'block',
@@ -148,7 +151,6 @@ class User implements IDBAccessObject {
                'patrol',
                'patrolmarks',
                'protect',
-               'proxyunbannable',
                'purge',
                'read',
                'reupload',
@@ -227,7 +229,7 @@ class User implements IDBAccessObject {
         *  - 'defaults'   anonymous user initialised from class defaults
         *  - 'name'       initialise from mName
         *  - 'id'         initialise from mId
-        *  - 'session'    log in from cookies or session if possible
+        *  - 'session'    log in from session if possible
         *
         * Use the User::newFrom*() family of functions to set this.
         */
@@ -311,14 +313,26 @@ class User implements IDBAccessObject {
         * @param integer $flags User::READ_* constant bitfield
         */
        public function load( $flags = self::READ_NORMAL ) {
+               global $wgFullyInitialised;
+
                if ( $this->mLoadedItems === true ) {
                        return;
                }
 
                // Set it now to avoid infinite recursion in accessors
+               $oldLoadedItems = $this->mLoadedItems;
                $this->mLoadedItems = true;
                $this->queryFlagsUsed = $flags;
 
+               // If this is called too early, things are likely to break.
+               if ( $this->mFrom === 'session' && empty( $wgFullyInitialised ) ) {
+                       \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
+                               ->warning( 'User::loadFromSession called before the end of Setup.php' );
+                       $this->loadDefaults();
+                       $this->mLoadedItems = $oldLoadedItems;
+                       return;
+               }
+
                switch ( $this->mFrom ) {
                        case 'defaults':
                                $this->loadDefaults();
@@ -541,8 +555,8 @@ class User implements IDBAccessObject {
        }
 
        /**
-        * Create a new user object using data from session or cookies. If the
-        * login credentials are invalid, the result is an anonymous user.
+        * Create a new user object using data from session. If the login
+        * credentials are invalid, the result is an anonymous user.
         *
         * @param WebRequest|null $request Object to use; $wgRequest will be used if omitted.
         * @return User
@@ -662,6 +676,8 @@ class User implements IDBAccessObject {
                        $user->saveSettings();
                }
 
+               SessionManager::singleton()->preventSessionsForUser( $user->getName() );
+
                return $user;
        }
 
@@ -895,7 +911,6 @@ class User implements IDBAccessObject {
                return $this->getPasswordValidity( $password ) === true;
        }
 
-
        /**
         * Given unvalidated password input, return error message on failure.
         *
@@ -1070,8 +1085,8 @@ class User implements IDBAccessObject {
                $this->mOptionOverrides = null;
                $this->mOptionsLoaded = false;
 
-               $loggedOut = $this->getRequest()->getCookie( 'LoggedOut' );
-               if ( $loggedOut !== null ) {
+               $loggedOut = $this->mRequest ? $this->mRequest->getSession()->getLoggedOutTimestamp() : 0;
+               if ( $loggedOut !== 0 ) {
                        $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
                } else {
                        $this->mTouched = '1'; # Allow any pages to be cached
@@ -1116,84 +1131,32 @@ class User implements IDBAccessObject {
        }
 
        /**
-        * Load user data from the session or login cookie.
+        * Load user data from the session.
         *
         * @return bool True if the user is logged in, false otherwise.
         */
        private function loadFromSession() {
+               // Deprecated hook
                $result = null;
-               Hooks::run( 'UserLoadFromSession', array( $this, &$result ) );
+               Hooks::run( 'UserLoadFromSession', array( $this, &$result ), '1.27' );
                if ( $result !== null ) {
                        return $result;
                }
 
-               $request = $this->getRequest();
-
-               $cookieId = $request->getCookie( 'UserID' );
-               $sessId = $request->getSessionData( 'wsUserID' );
-
-               if ( $cookieId !== null ) {
-                       $sId = intval( $cookieId );
-                       if ( $sessId !== null && $cookieId != $sessId ) {
-                               wfDebugLog( 'loginSessions', "Session user ID ($sessId) and
-                                       cookie user ID ($sId) don't match!" );
-                               return false;
-                       }
-                       $request->setSessionData( 'wsUserID', $sId );
-               } elseif ( $sessId !== null && $sessId != 0 ) {
-                       $sId = $sessId;
-               } else {
-                       return false;
-               }
-
-               if ( $request->getSessionData( 'wsUserName' ) !== null ) {
-                       $sName = $request->getSessionData( 'wsUserName' );
-               } elseif ( $request->getCookie( 'UserName' ) !== null ) {
-                       $sName = $request->getCookie( 'UserName' );
-                       $request->setSessionData( 'wsUserName', $sName );
-               } else {
-                       return false;
-               }
-
-               $proposedUser = User::newFromId( $sId );
-               if ( !$proposedUser->isLoggedIn() ) {
-                       // Not a valid ID
-                       return false;
-               }
-
-               global $wgBlockDisablesLogin;
-               if ( $wgBlockDisablesLogin && $proposedUser->isBlocked() ) {
-                       // User blocked and we've disabled blocked user logins
-                       return false;
-               }
-
-               if ( $request->getSessionData( 'wsToken' ) ) {
-                       $passwordCorrect =
-                               ( $proposedUser->getToken( false ) === $request->getSessionData( 'wsToken' ) );
-                       $from = 'session';
-               } elseif ( $request->getCookie( 'Token' ) ) {
-                       # Get the token from DB/cache and clean it up to remove garbage padding.
-                       # This deals with historical problems with bugs and the default column value.
-                       $token = rtrim( $proposedUser->getToken( false ) ); // correct token
-                       // Make comparison in constant time (bug 61346)
-                       $passwordCorrect = strlen( $token )
-                               && hash_equals( $token, $request->getCookie( 'Token' ) );
-                       $from = 'cookie';
-               } else {
-                       // No session or persistent login cookie
-                       return false;
-               }
-
-               if ( ( $sName === $proposedUser->getName() ) && $passwordCorrect ) {
-                       $this->loadFromUserObject( $proposedUser );
-                       $request->setSessionData( 'wsToken', $this->mToken );
-                       wfDebug( "User: logged in from $from\n" );
+               // MediaWiki\Session\Session already did the necessary authentication of the user
+               // returned here, so just use it if applicable.
+               $session = $this->getRequest()->getSession();
+               $user = $session->getUser();
+               if ( $user->isLoggedIn() ) {
+                       $this->loadFromUserObject( $user );
+                       // Other code expects these to be set in the session, so set them.
+                       $session->set( 'wsUserID', $this->getId() );
+                       $session->set( 'wsUserName', $this->getName() );
+                       $session->set( 'wsToken', $this->mToken );
                        return true;
-               } else {
-                       // Invalid credentials
-                       wfDebug( "User: can't log in from $from, invalid credentials\n" );
-                       return false;
                }
+
+               return false;
        }
 
        /**
@@ -1288,9 +1251,14 @@ class User implements IDBAccessObject {
                        $all = false;
                }
 
+               if ( isset( $row->user_touched ) ) {
+                       $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
+               } else {
+                       $all = false;
+               }
+
                if ( isset( $row->user_email ) ) {
                        $this->mEmail = $row->user_email;
-                       $this->mTouched = wfTimestamp( TS_MW, $row->user_touched );
                        $this->mToken = $row->user_token;
                        if ( $this->mToken == '' ) {
                                $this->mToken = null;
@@ -1556,9 +1524,7 @@ class User implements IDBAccessObject {
                $block = Block::newFromTarget( $this, $ip, !$bFromSlave );
 
                // Proxy blocking
-               if ( !$block instanceof Block && $ip !== null && !$this->isAllowed( 'proxyunbannable' )
-                       && !in_array( $ip, $wgProxyWhitelist )
-               ) {
+               if ( !$block instanceof Block && $ip !== null && !in_array( $ip, $wgProxyWhitelist ) ) {
                        // Local list
                        if ( self::isLocallyBlockedProxy( $ip ) ) {
                                $block = new Block;
@@ -1577,7 +1543,6 @@ class User implements IDBAccessObject {
                if ( !$block instanceof Block
                        && $wgApplyIpBlocksToXff
                        && $ip !== null
-                       && !$this->isAllowed( 'proxyunbannable' )
                        && !in_array( $ip, $wgProxyWhitelist )
                ) {
                        $xff = $this->getRequest()->getHeader( 'X-Forwarded-For' );
@@ -2443,6 +2408,9 @@ class User implements IDBAccessObject {
                        ),
                        __METHOD__
                );
+
+               // When the main password is changed, invalidate all bot passwords too
+               BotPassword::invalidateAllPasswordsForUser( $this->getName() );
        }
 
        /**
@@ -3016,6 +2984,12 @@ class User implements IDBAccessObject {
        public function getRights() {
                if ( is_null( $this->mRights ) ) {
                        $this->mRights = self::getGroupPermissions( $this->getEffectiveGroups() );
+
+                       $allowedRights = $this->getRequest()->getSession()->getAllowedUserRights();
+                       if ( $allowedRights !== null ) {
+                               $this->mRights = array_intersect( $this->mRights, $allowedRights );
+                       }
+
                        Hooks::run( 'UserGetRights', array( $this, &$this->mRights ) );
                        // Force reindexation of rights when a hook has unset one of them
                        $this->mRights = array_values( array_unique( $this->mRights ) );
@@ -3277,13 +3251,6 @@ class User implements IDBAccessObject {
                if ( $action === '' ) {
                        return true; // In the spirit of DWIM
                }
-               // Patrolling may not be enabled
-               if ( $action === 'patrol' || $action === 'autopatrol' ) {
-                       global $wgUseRCPatrol, $wgUseNPPatrol;
-                       if ( !$wgUseRCPatrol && !$wgUseNPPatrol ) {
-                               return false;
-                       }
-               }
                // Use strict parameter to avoid matching numeric 0 accidentally inserted
                // by misconfiguration: 0 == 'foo'
                return in_array( $action, $this->getRights(), true );
@@ -3310,6 +3277,18 @@ class User implements IDBAccessObject {
                );
        }
 
+       /**
+        * Check whether to enable new files patrol features for this user
+        * @return bool True or false
+        */
+       public function useFilePatrol() {
+               global $wgUseRCPatrol, $wgUseFilePatrol;
+               return (
+                       ( $wgUseRCPatrol || $wgUseFilePatrol )
+                               && ( $this->isAllowedAny( 'patrol', 'patrolmarks' ) )
+               );
+       }
+
        /**
         * Get the WebRequest object to use with this object
         *
@@ -3324,17 +3303,6 @@ class User implements IDBAccessObject {
                }
        }
 
-       /**
-        * Get the current skin, loading it if required
-        * @return Skin The current skin
-        * @todo FIXME: Need to check the old failback system [AV]
-        * @deprecated since 1.18 Use ->getSkin() in the most relevant outputting context you have
-        */
-       public function getSkin() {
-               wfDeprecated( __METHOD__, '1.18' );
-               return RequestContext::getMain()->getSkin();
-       }
-
        /**
         * Get a WatchedItem for this user and $title.
         *
@@ -3502,6 +3470,7 @@ class User implements IDBAccessObject {
        /**
         * Set a cookie on the user's client. Wrapper for
         * WebResponse::setCookie
+        * @deprecated since 1.27
         * @param string $name Name of the cookie to set
         * @param string $value Value to set
         * @param int $exp Expiration time, as a UNIX time value;
@@ -3517,6 +3486,7 @@ class User implements IDBAccessObject {
        protected function setCookie(
                $name, $value, $exp = 0, $secure = null, $params = array(), $request = null
        ) {
+               wfDeprecated( __METHOD__, '1.27' );
                if ( $request === null ) {
                        $request = $this->getRequest();
                }
@@ -3526,6 +3496,7 @@ class User implements IDBAccessObject {
 
        /**
         * Clear a cookie on the user's client
+        * @deprecated since 1.27
         * @param string $name Name of the cookie to clear
         * @param bool $secure
         *  true: Force setting the secure attribute when setting the cookie
@@ -3534,6 +3505,7 @@ class User implements IDBAccessObject {
         * @param array $params Array of options sent passed to WebResponse::setcookie()
         */
        protected function clearCookie( $name, $secure = null, $params = array() ) {
+               wfDeprecated( __METHOD__, '1.27' );
                $this->setCookie( $name, '', time() - 86400, $secure, $params );
        }
 
@@ -3544,6 +3516,7 @@ class User implements IDBAccessObject {
         *
         * @see User::setCookie
         *
+        * @deprecated since 1.27
         * @param string $name Name of the cookie to set
         * @param string $value Value to set
         * @param bool $secure
@@ -3554,6 +3527,8 @@ class User implements IDBAccessObject {
        protected function setExtendedLoginCookie( $name, $value, $secure ) {
                global $wgExtendedLoginCookieExpiration, $wgCookieExpiration;
 
+               wfDeprecated( __METHOD__, '1.27' );
+
                $exp = time();
                $exp += $wgExtendedLoginCookieExpiration !== null
                        ? $wgExtendedLoginCookieExpiration
@@ -3563,7 +3538,7 @@ class User implements IDBAccessObject {
        }
 
        /**
-        * Set the default cookies for this session on the user's client.
+        * Persist this user's session (e.g. set cookies)
         *
         * @param WebRequest|null $request WebRequest object to use; $wgRequest will be used if null
         *        is passed.
@@ -3571,72 +3546,36 @@ class User implements IDBAccessObject {
         * @param bool $rememberMe Whether to add a Token cookie for elongated sessions
         */
        public function setCookies( $request = null, $secure = null, $rememberMe = false ) {
-               global $wgExtendedLoginCookies;
-
-               if ( $request === null ) {
-                       $request = $this->getRequest();
-               }
-
                $this->load();
                if ( 0 == $this->mId ) {
                        return;
                }
-               if ( !$this->mToken ) {
-                       // When token is empty or NULL generate a new one and then save it to the database
-                       // This allows a wiki to re-secure itself after a leak of it's user table or $wgSecretKey
-                       // Simply by setting every cell in the user_token column to NULL and letting them be
-                       // regenerated as users log back into the wiki.
-                       $this->setToken();
-                       if ( !wfReadOnly() ) {
-                               $this->saveSettings();
-                       }
-               }
-               $session = array(
-                       'wsUserID' => $this->mId,
-                       'wsToken' => $this->mToken,
-                       'wsUserName' => $this->getName()
-               );
-               $cookies = array(
-                       'UserID' => $this->mId,
-                       'UserName' => $this->getName(),
-               );
-               if ( $rememberMe ) {
-                       $cookies['Token'] = $this->mToken;
-               } else {
-                       $cookies['Token'] = false;
-               }
 
-               Hooks::run( 'UserSetCookies', array( $this, &$session, &$cookies ) );
-
-               foreach ( $session as $name => $value ) {
-                       $request->setSessionData( $name, $value );
+               $session = $this->getRequest()->getSession();
+               if ( $request && $session->getRequest() !== $request ) {
+                       $session = $session->sessionWithRequest( $request );
                }
-               foreach ( $cookies as $name => $value ) {
-                       if ( $value === false ) {
-                               $this->clearCookie( $name );
-                       } elseif ( $rememberMe && in_array( $name, $wgExtendedLoginCookies ) ) {
-                               $this->setExtendedLoginCookie( $name, $value, $secure );
-                       } else {
-                               $this->setCookie( $name, $value, 0, $secure, array(), $request );
+               $delay = $session->delaySave();
+
+               if ( !$session->getUser()->equals( $this ) ) {
+                       if ( !$session->canSetUser() ) {
+                               \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
+                                       ->warning( __METHOD__ .
+                                               ": Cannot save user \"$this\" to a user \"{$session->getUser()}\"'s immutable session"
+                                       );
+                               return;
                        }
+                       $session->setUser( $this );
                }
 
-               /**
-                * If wpStickHTTPS was selected, also set an insecure cookie that
-                * will cause the site to redirect the user to HTTPS, if they access
-                * it over HTTP. Bug 29898. Use an un-prefixed cookie, so it's the same
-                * as the one set by centralauth (bug 53538). Also set it to session, or
-                * standard time setting, based on if rememberme was set.
-                */
-               if ( $request->getCheck( 'wpStickHTTPS' ) || $this->requiresHTTPS() ) {
-                       $this->setCookie(
-                               'forceHTTPS',
-                               'true',
-                               $rememberMe ? 0 : null,
-                               false,
-                               array( 'prefix' => '' ) // no prefix
-                       );
+               $session->setRememberUser( $rememberMe );
+               if ( $secure !== null ) {
+                       $session->setForceHTTPS( $secure );
                }
+
+               $session->persist();
+
+               ScopedCallback::consume( $delay );
        }
 
        /**
@@ -3649,20 +3588,29 @@ class User implements IDBAccessObject {
        }
 
        /**
-        * Clear the user's cookies and session, and reset the instance cache.
+        * Clear the user's session, and reset the instance cache.
         * @see logout()
         */
        public function doLogout() {
-               $this->clearInstanceCache( 'defaults' );
-
-               $this->getRequest()->setSessionData( 'wsUserID', 0 );
-
-               $this->clearCookie( 'UserID' );
-               $this->clearCookie( 'Token' );
-               $this->clearCookie( 'forceHTTPS', false, array( 'prefix' => '' ) );
-
-               // Remember when user logged out, to prevent seeing cached pages
-               $this->setCookie( 'LoggedOut', time(), time() + 86400 );
+               $session = $this->getRequest()->getSession();
+               if ( !$session->canSetUser() ) {
+                       \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
+                               ->warning( __METHOD__ . ": Cannot log out of an immutable session" );
+               } elseif ( !$session->getUser()->equals( $this ) ) {
+                       \MediaWiki\Logger\LoggerFactory::getInstance( 'session' )
+                               ->warning( __METHOD__ .
+                                       ": Cannot log user \"$this\" out of a user \"{$session->getUser()}\"'s session"
+                               );
+                       // But we still may as well make this user object anon
+                       $this->clearInstanceCache( 'defaults' );
+               } else {
+                       $this->clearInstanceCache( 'defaults' );
+                       $delay = $session->delaySave();
+                       $session->setLoggedOutTimestamp( time() );
+                       $session->setUser( new User );
+                       $session->set( 'wsUserID', 0 ); // Other code expects this
+                       ScopedCallback::consume( $delay );
+               }
        }
 
        /**
@@ -4160,17 +4108,6 @@ class User implements IDBAccessObject {
                );
        }
 
-       /**
-        * Generate a looking random token for various uses.
-        *
-        * @return string The new random token
-        * @deprecated since 1.20: Use MWCryptRand for secure purposes or
-        *   wfRandomString for pseudo-randomness.
-        */
-       public static function generateToken() {
-               return MWCryptRand::generateHex( 32 );
-       }
-
        /**
         * Get the embedded timestamp from a token.
         * @param string $val Input token
@@ -4570,7 +4507,14 @@ class User implements IDBAccessObject {
        }
 
        /**
-        * Check if all users have the given permission
+        * Check if all users may be assumed to have the given permission
+        *
+        * We generally assume so if the right is granted to '*' and isn't revoked
+        * on any group. It doesn't attempt to take grants or other extension
+        * limitations on rights into account in the general case, though, as that
+        * would require it to always return false and defeat the purpose.
+        * Specifically, session-based rights restrictions (such as OAuth or bot
+        * passwords) are applied based on the current session.
         *
         * @since 1.22
         * @param string $right Right to check
@@ -4599,7 +4543,14 @@ class User implements IDBAccessObject {
                        }
                }
 
-               // Allow extensions (e.g. OAuth) to say false
+               // Remove any rights that aren't allowed to the global-session user
+               $allowedRights = SessionManager::getGlobalSession()->getAllowedUserRights();
+               if ( $allowedRights !== null && !in_array( $right, $allowedRights, true ) ) {
+                       $cache[$right] = false;
+                       return false;
+               }
+
+               // Allow extensions to say false
                if ( !Hooks::run( 'UserIsEveryoneAllowed', array( $right ) ) ) {
                        $cache[$right] = false;
                        return false;
diff --git a/includes/user/UserNamePrefixSearch.php b/includes/user/UserNamePrefixSearch.php
new file mode 100644 (file)
index 0000000..f565266
--- /dev/null
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Prefix search of user names.
+ *
+ * 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
+ */
+
+/**
+ * Handles searching prefixes of user names
+ *
+ * @since 1.27
+ */
+class UserNamePrefixSearch {
+
+       /**
+        * Do a prefix search of user names and return a list of matching user names.
+        *
+        * @param string|User $audience The string 'public' or a user object to show the search for
+        * @param string $search
+        * @param int $limit
+        * @param int $offset How many results to offset from the beginning
+        * @return array Array of strings
+        */
+       public static function search( $audience, $search, $limit, $offset = 0 ) {
+               $user = User::newFromName( $search );
+
+               $dbr = wfGetDB( DB_SLAVE );
+               $prefix = $user ? $user->getName() : '';
+               $tables = array( 'user' );
+               $cond = array( 'user_name ' . $dbr->buildLike( $prefix, $dbr->anyString() ) );
+               $joinConds = array();
+
+               // Filter out hidden user names
+               if ( $audience === 'public' || !$audience->isAllowed( 'hideuser' ) ) {
+                       $tables[] = 'ipblocks';
+                       $cond['ipb_deleted'] = array( 0, null );
+                       $joinConds['ipblocks'] = array( 'LEFT JOIN', 'user_id=ipb_user' );
+               }
+
+               $res = $dbr->selectFieldValues(
+                       $tables,
+                       'user_name',
+                       $cond,
+                       __METHOD__,
+                       array(
+                               'LIMIT' => $limit,
+                               'ORDER BY' => 'user_name',
+                               'OFFSET' => $offset
+                       ),
+                       $joinConds
+               );
+
+               return $res === false ? array() : $res;
+       }
+}
index 2dfc902..f5d7828 100644 (file)
@@ -97,7 +97,6 @@ class MWCryptHKDF {
                'whirlpool' => 64,
        );
 
-
        /**
         * @param string $secretKeyMaterial
         * @param string $algorithm Name of hashing algorithm
@@ -214,7 +213,6 @@ class MWCryptHKDF {
                );
        }
 
-
        /**
         * RFC5869 defines HKDF in 2 steps, extraction and expansion.
         * From http://eprint.iacr.org/2010/264.pdf:
diff --git a/includes/utils/MWGrants.php b/includes/utils/MWGrants.php
new file mode 100644 (file)
index 0000000..b9b51d5
--- /dev/null
@@ -0,0 +1,214 @@
+<?php
+/**
+ * Functions and constants to deal with grants
+ *
+ * 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
+ */
+
+/**
+ * A collection of public static functions to deal with grants.
+ */
+class MWGrants {
+
+       /**
+        * List all known grants.
+        * @return array
+        */
+       public static function getValidGrants() {
+               global $wgGrantPermissions;
+
+               return array_keys( $wgGrantPermissions );
+       }
+
+       /**
+        * Map all grants to corresponding user rights.
+        * @return array grant => array of rights
+        */
+       public static function getRightsByGrant() {
+               global $wgGrantPermissions;
+
+               $res = array();
+               foreach ( $wgGrantPermissions as $grant => $rights ) {
+                       $res[$grant] = array_keys( array_filter( $rights ) );
+               }
+               return $res;
+       }
+
+       /**
+        * Fetch the display name of the grant
+        * @param string $grant
+        * @param Language|string|null $lang
+        * @return string Grant description
+        */
+       public static function grantName( $grant, $lang = null ) {
+               // Give grep a chance to find the usages:
+               // grant-blockusers, grant-createeditmovepage, grant-delete,
+               // grant-editinterface, grant-editmycssjs, grant-editmywatchlist,
+               // grant-editpage, grant-editprotected, grant-highvolume,
+               // grant-oversight, grant-patrol, grant-protect, grant-rollback,
+               // grant-sendemail, grant-uploadeditmovefile, grant-uploadfile,
+               // grant-basic, grant-viewdeleted, grant-viewmywatchlist,
+               // grant-createaccount
+               $msg = wfMessage( "grant-$grant" );
+               if ( $lang !== null ) {
+                       if ( is_string( $lang ) ) {
+                               $lang = Language::factory( $lang );
+                       }
+                       $msg->inLanguage( $lang );
+               }
+               if ( !$msg->exists() ) {
+                       $msg = wfMessage( 'grant-generic', $grant );
+                       if ( $lang ) {
+                               $msg->inLanguage( $lang );
+                       }
+               }
+               return $msg->text();
+       }
+
+       /**
+        * Fetch the display names for the grants.
+        * @param string[] $grants
+        * @param Language|string|null $lang
+        * @return string[] Corresponding grant descriptions
+        */
+       public static function grantNames( array $grants, $lang = null ) {
+               if ( $lang !== null ) {
+                       if ( is_string( $lang ) ) {
+                               $lang = Language::factory( $lang );
+                       }
+               }
+
+               $ret = array();
+               foreach ( $grants as $grant ) {
+                       $ret[] = self::grantName( $grant, $lang );
+               }
+               return $ret;
+       }
+
+       /**
+        * Fetch the rights allowed by a set of grants.
+        * @param string[]|string $grants
+        * @return string[]
+        */
+       public static function getGrantRights( $grants ) {
+               global $wgGrantPermissions;
+
+               $rights = array();
+               foreach ( (array)$grants as $grant ) {
+                       if ( isset( $wgGrantPermissions[$grant] ) ) {
+                               $rights = array_merge( $rights, array_keys( array_filter( $wgGrantPermissions[$grant] ) ) );
+                       }
+               }
+               return array_unique( $rights );
+       }
+
+       /**
+        * Test that all grants in the list are known.
+        * @param string[] $grants
+        * @return bool
+        */
+       public static function grantsAreValid( array $grants ) {
+               return array_diff( $grants, self::getValidGrants() ) === array();
+       }
+
+       /**
+        * Divide the grants into groups.
+        * @param string[]|null $grantsFilter
+        * @return array Map of (group => (grant list))
+        */
+       public static function getGrantGroups( $grantsFilter = null ) {
+               global $wgGrantPermissions, $wgGrantPermissionGroups;
+
+               if ( is_array( $grantsFilter ) ) {
+                       $grantsFilter = array_flip( $grantsFilter );
+               }
+
+               $groups = array();
+               foreach ( $wgGrantPermissions as $grant => $rights ) {
+                       if ( $grantsFilter !== null && !isset( $grantsFilter[$grant] ) ) {
+                               continue;
+                       }
+                       if ( isset( $wgGrantPermissionGroups[$grant] ) ) {
+                               $groups[$wgGrantPermissionGroups[$grant]][] = $grant;
+                       } else {
+                               $groups['other'][] = $grant;
+                       }
+               }
+
+               return $groups;
+       }
+
+       /**
+        * Get the list of grants that are hidden and should always be granted
+        * @return string[]
+        */
+       public static function getHiddenGrants() {
+               global $wgGrantPermissionGroups;
+
+               $grants = array();
+               foreach ( $wgGrantPermissionGroups as $grant => $group ) {
+                       if ( $group === 'hidden' ) {
+                               $grants[] = $grant;
+                       }
+               }
+               return $grants;
+       }
+
+       /**
+        * Generate a link to Special:ListGrants for a particular grant name.
+        *
+        * This should be used to link end users to a full description of what
+        * rights they are giving when they authorize a grant.
+        *
+        * @param string $grant the grant name
+        * @param Language|string|null $lang
+        * @return string (proto-relative) HTML link
+        */
+       public static function getGrantsLink( $grant, $lang = null ) {
+               return \Linker::linkKnown(
+                       \SpecialPage::getTitleFor( 'Listgrants', false, $grant ),
+                       htmlspecialchars( self::grantName( $grant, $lang ) )
+               );
+       }
+
+       /**
+        * Generate wikitext to display a list of grants
+        * @param string[]|null $grantsFilter If non-null, only display these grants.
+        * @param Language|string|null $lang
+        * @return string Wikitext
+        */
+       public static function getGrantsWikiText( $grantsFilter, $lang = null ) {
+               global $wgContLang;
+
+               if ( is_string( $lang ) ) {
+                       $lang = Language::factory( $lang );
+               } elseif ( $lang === null ) {
+                       $lang = $wgContLang;
+               }
+
+               $s = '';
+               foreach ( self::getGrantGroups( $grantsFilter ) as $group => $grants ) {
+                       if ( $group === 'hidden' ) {
+                               continue; // implicitly granted
+                       }
+                       $s .= "*<span class=\"mw-grantgroup\">" .
+                               wfMessage( "grant-group-$group" )->inLanguage( $lang )->text() . "</span>\n";
+                       $s .= ":" . $lang->semicolonList( self::grantNames( $grants, $lang ) ) . "\n";
+               }
+               return "$s\n";
+       }
+
+}
diff --git a/includes/utils/MWRestrictions.php b/includes/utils/MWRestrictions.php
new file mode 100644 (file)
index 0000000..3b4dc11
--- /dev/null
@@ -0,0 +1,144 @@
+<?php
+/**
+ * A class to check request restrictions expressed as a JSON object
+ *
+ * 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
+ */
+
+/**
+ * A class to check request restrictions expressed as a JSON object
+ */
+class MWRestrictions {
+
+       private $ipAddresses = array( '0.0.0.0/0', '::/0' );
+
+       /**
+        * @param array $restrictions
+        */
+       protected function __construct( array $restrictions = null ) {
+               if ( $restrictions !== null ) {
+                       $this->loadFromArray( $restrictions );
+               }
+       }
+
+       /**
+        * @return MWRestrictions
+        */
+       public static function newDefault() {
+               return new self();
+       }
+
+       /**
+        * @param array $restrictions
+        * @return MWRestrictions
+        */
+       public static function newFromArray( array $restrictions ) {
+               return new self( $restrictions );
+       }
+
+       /**
+        * @param string $json JSON representation of the restrictions
+        * @return MWRestrictions
+        */
+       public static function newFromJson( $json ) {
+               $restrictions = FormatJson::decode( $json, true );
+               if ( !is_array( $restrictions ) ) {
+                       throw new InvalidArgumentException( 'Invalid restrictions JSON' );
+               }
+               return new self( $restrictions );
+       }
+
+       private function loadFromArray( array $restrictions ) {
+               static $validKeys = array( 'IPAddresses' );
+               static $neededKeys = array( 'IPAddresses' );
+
+               $keys = array_keys( $restrictions );
+               $invalidKeys = array_diff( $keys, $validKeys );
+               if ( $invalidKeys ) {
+                       throw new InvalidArgumentException(
+                               'Array contains invalid keys: ' . join( ', ', $invalidKeys )
+                       );
+               }
+               $missingKeys = array_diff( $neededKeys, $keys );
+               if ( $missingKeys ) {
+                       throw new InvalidArgumentException(
+                               'Array is missing required keys: ' . join( ', ', $missingKeys )
+                       );
+               }
+
+               if ( !is_array( $restrictions['IPAddresses'] ) ) {
+                       throw new InvalidArgumentException( 'IPAddresses is not an array' );
+               }
+               foreach ( $restrictions['IPAddresses'] as $ip ) {
+                       if ( !\IP::isIPAddress( $ip ) ) {
+                               throw new InvalidArgumentException( "Invalid IP address: $ip" );
+                       }
+               }
+               $this->ipAddresses = $restrictions['IPAddresses'];
+       }
+
+       /**
+        * Return the restrictions as an array
+        * @return array
+        */
+       public function toArray() {
+               return array(
+                       'IPAddresses' => $this->ipAddresses,
+               );
+       }
+
+       /**
+        * Return the restrictions as a JSON string
+        * @param bool|string $pretty Pretty-print the JSON output, see FormatJson::encode
+        * @return string
+        */
+       public function toJson( $pretty = false ) {
+               return FormatJson::encode( $this->toArray(), $pretty, FormatJson::ALL_OK );
+       }
+
+       public function __toString() {
+               return $this->toJson();
+       }
+
+       /**
+        * Test against the passed WebRequest
+        * @param WebRequest $request
+        * @return Status
+        */
+       public function check( WebRequest $request ) {
+               $ok = array(
+                       'ip' => $this->checkIP( $request->getIP() ),
+               );
+               $status = Status::newGood();
+               $status->setResult( $ok === array_filter( $ok ), $ok );
+               return $status;
+       }
+
+       /**
+        * Test an IP address
+        * @param string $ip
+        * @return bool
+        */
+       public function checkIP( $ip ) {
+               foreach ( $this->ipAddresses as $range ) {
+                       if ( \IP::isInRange( $ip, $range ) ) {
+                               return true;
+                       }
+               }
+
+               return false;
+       }
+}
index e2de900..9ac5009 100644 (file)
@@ -135,7 +135,7 @@ class UIDGenerator {
                        $time = $info[0];
                        $counter = $info[1];
                }
-               // Take the 46 MSBs of "milliseconds since epoch"
+               // Take the 46 LSBs of "milliseconds since epoch"
                $id_bin = $this->millisecondsSinceEpochBinary( $time );
                // Add a 10 bit counter resulting in 56 bits total
                $id_bin .= str_pad( decbin( $counter ), 10, '0', STR_PAD_LEFT );
@@ -191,7 +191,7 @@ class UIDGenerator {
                        $counter = $info[1];
                        $clkSeq = $info[2];
                }
-               // Take the 46 bits of "milliseconds since epoch"
+               // Take the 46 LSBs of "milliseconds since epoch"
                $id_bin = $this->millisecondsSinceEpochBinary( $time );
                // Add a 20 bit counter resulting in 66 bits total
                $id_bin .= str_pad( decbin( $counter ), 20, '0', STR_PAD_LEFT );
@@ -334,7 +334,7 @@ class UIDGenerator {
         * @see UIDGenerator::newSequentialPerNodeID()
         * @param string $bucket Arbitrary bucket name (should be ASCII)
         * @param int $bits Bit size (16 to 48) of resulting numbers before wrap-around
-        * @param int $count Number of IDs to return (1 to 10000)
+        * @param int $count Number of IDs to return
         * @param int $flags (supports UIDGenerator::QUICK_VOLATILE)
         * @return array Ordered list of float integer values
         * @since 1.23
@@ -350,7 +350,7 @@ class UIDGenerator {
         * @see UIDGenerator::newSequentialPerNodeID()
         * @param string $bucket Arbitrary bucket name (should be ASCII)
         * @param int $bits Bit size (16 to 48) of resulting numbers before wrap-around
-        * @param int $count Number of IDs to return (1 to 10000)
+        * @param int $count Number of IDs to return
         * @param int $flags (supports UIDGenerator::QUICK_VOLATILE)
         * @return array Ordered list of float integer values
         * @throws RuntimeException
@@ -358,8 +358,6 @@ class UIDGenerator {
        protected function getSequentialPerNodeIDs( $bucket, $bits, $count, $flags ) {
                if ( $count <= 0 ) {
                        return array(); // nothing to do
-               } elseif ( $count > 10000 ) {
-                       throw new RuntimeException( "Number of requested IDs ($count) is too high." );
                } elseif ( $bits < 16 || $bits > 48 ) {
                        throw new RuntimeException( "Requested bit size ($bits) is out of range." );
                }
@@ -373,12 +371,9 @@ class UIDGenerator {
                        $cache = ObjectCache::getLocalServerInstance();
                }
                if ( $cache ) {
-                       $counter = $cache->incr( $bucket, $count );
+                       $counter = $cache->incrWithInit( $bucket, $cache::TTL_INDEFINITE, $count, $count );
                        if ( $counter === false ) {
-                               if ( !$cache->add( $bucket, (int)$count ) ) {
-                                       throw new RuntimeException( 'Unable to set value to ' . get_class( $cache ) );
-                               }
-                               $counter = $count;
+                               throw new RuntimeException( 'Unable to set value to ' . get_class( $cache ) );
                        }
                }
 
@@ -531,7 +526,7 @@ class UIDGenerator {
 
        /**
         * @param array $time Result of UIDGenerator::millitime()
-        * @return string 46 MSBs of "milliseconds since epoch" in binary (rolls over in 4201)
+        * @return string 46 LSBs of "milliseconds since epoch" in binary (rolls over in 4201)
         * @throws RuntimeException
         */
        protected function millisecondsSinceEpochBinary( array $time ) {
index 47d24dc..1b6e9d6 100644 (file)
@@ -107,11 +107,6 @@ class FakeConverter {
                return $key;
        }
 
-       /** @deprecated since 1.22 is no longer used */
-       function armourMath( $text ) {
-               return $text;
-       }
-
        function validateVariant( $variant = null ) {
                return $variant === $this->mLang->getCode() ? $variant : null;
        }
index 69f518b..cb2d24f 100644 (file)
@@ -4157,17 +4157,6 @@ class Language {
                return (bool)$this->mConverter->validateVariant( $variant );
        }
 
-       /**
-        * Put custom tags (e.g. -{ }-) around math to prevent conversion
-        *
-        * @param string $text
-        * @return string
-        * @deprecated since 1.22 is no longer used
-        */
-       public function armourMath( $text ) {
-               return $this->mConverter->armourMath( $text );
-       }
-
        /**
         * Perform output conversion on a string, and encode for safe HTML output.
         * @param string $text Text to be converted
index 78b3572..b00aa34 100644 (file)
@@ -1084,22 +1084,6 @@ class LanguageConverter {
                return true;
        }
 
-       /**
-        * Armour rendered math against conversion.
-        * Escape special chars in parsed math text. (in most cases are img elements)
-        *
-        * @param string $text Text to armour against conversion
-        * @return string Armoured text where { and } have been converted to
-        *   &#123; and &#125;
-        * @deprecated since 1.22 is no longer used
-        */
-       public function armourMath( $text ) {
-               // convert '-{' and '}-' to '-&#123;' and '&#125;-' to prevent
-               // any unwanted markup appearing in the math image tag.
-               $text = strtr( $text, array( '-{' => '-&#123;', '}-' => '&#125;-' ) );
-               return $text;
-       }
-
        /**
         * Get the cached separator pattern for ConverterRule::parseRules()
         * @return string
index dbf4cce..d374c85 100644 (file)
@@ -71,15 +71,14 @@ class LanguageOs extends Language {
                if ( preg_match( '/тæ$/u', $word ) ) {
                        $word = mb_substr( $word, 0, -1 );
                        $end_allative = 'æм';
-               }
-               # Works if $word is in singular form.
-               # Checking if $word ends on one of the vowels: е, ё, и, о, ы, э, ю, я.
-               elseif ( preg_match( "/[аæеёиоыэюя]$/u", $word ) ) {
+               } elseif ( preg_match( "/[аæеёиоыэюя]$/u", $word ) ) {
+                       # Works if $word is in singular form.
+                       # Checking if $word ends on one of the vowels: е, ё, и, о, ы, э, ю, я.
                        $jot = 'й';
-               }
-               # Checking if $word ends on 'у'. 'У' can be either consonant 'W' or vowel 'U' in cyrillic Ossetic.
-               # Examples: {{grammar:genitive|аунеу}} = аунеуы, {{grammar:genitive|лæппу}} = лæппуйы.
-               elseif ( preg_match( "/у$/u", $word ) ) {
+               } elseif ( preg_match( "/у$/u", $word ) ) {
+                       # Checking if $word ends on 'у'. 'У'
+                       # can be either consonant 'W' or vowel 'U' in cyrillic Ossetic.
+                       # Examples: {{grammar:genitive|аунеу}} = аунеуы, {{grammar:genitive|лæппу}} = лæппуйы.
                        if ( !preg_match( "/[аæеёиоыэюя]$/u", mb_substr( $word, -2, 1 ) ) ) {
                                $jot = 'й';
                        }
index 4bce5b1..836ef4a 100644 (file)
@@ -55,7 +55,8 @@
                        "Mervat Salman",
                        "Shbib Al-Subaie",
                        "Matma Rex",
-                       "Haytham morsy"
+                       "Haytham morsy",
+                       "BAB ZAA"
                ]
        },
        "tog-underline": "سطر تحت الوصلات:",
        "october-date": "تشرين الأول/أكتوبر $1",
        "november-date": "تشرين الثاني/نوفمبر $1",
        "december-date": "كانون الأول/ديسمبر $1",
+       "period-am": "صباحا",
+       "period-pm": "مساءً",
        "pagecategories": "{{PLURAL:$1|بلا تصنيف|تصنيف|تصنيفان|تصنيفات}}",
        "category_header": "صفحات تصنيف «$1»",
        "subcategories": "تصنيفات فرعية",
        "laggedslavemode": "'''تحذير:''' الصفحة قد لا تحتوي على أحدث التحديثات.",
        "readonly": "قاعدة البيانات مقفلة",
        "enterlockreason": "أدخل سببا للقفل ذاكرا تقديرا لوقت إزالة الغلق",
-       "readonlytext": "Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات Ù\85Ù\82Ù\81Ù\84Ø© Ø­Ø§Ù\84Ù\8aا Ø£Ù\85اÙ\85 Ø§Ù\84Ù\85دخÙ\84ات Ø§Ù\84جدÙ\8aدة Ù\88 Ø§Ù\84تعدÙ\8aÙ\84ات Ø§Ù\84أخرÙ\89. Ø§Ù\84سبب ØºØ§Ù\84با Ù\85ا Ù\8aÙ\83Ù\88Ù\86 Ø§Ù\84صÙ\8aاÙ\86Ø©Ø\8c Ù\88 Ø³ØªØ¹Ù\88د Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات Ù\84Ù\84عÙ\85Ù\84 Ø§Ù\84طبÙ\8aعÙ\8a Ù\82رÙ\8aبا.\n\nاÙ\84إدارÙ\8a الذي أغلق قاعدة البيانات أعطى التفسير التالي: $1",
+       "readonlytext": "Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات Ù\85Ù\82Ù\81Ù\84Ø© Ø­Ø§Ù\84Ù\8aا Ø£Ù\85اÙ\85 Ø§Ù\84Ù\85دخÙ\84ات Ø§Ù\84جدÙ\8aدة Ù\88 Ø§Ù\84تعدÙ\8aÙ\84ات Ø§Ù\84أخرÙ\89. Ø§Ù\84سبب ØºØ§Ù\84با Ù\85ا Ù\8aÙ\83Ù\88Ù\86 Ø§Ù\84صÙ\8aاÙ\86Ø©Ø\8c Ù\88 Ø³ØªØ¹Ù\88د Ù\82اعدة Ø§Ù\84بÙ\8aاÙ\86ات Ù\84Ù\84عÙ\85Ù\84 Ø§Ù\84طبÙ\8aعÙ\8a Ù\82رÙ\8aبا.\n\nإدارÙ\8a Ø§Ù\84Ù\86ظاÙ\85 الذي أغلق قاعدة البيانات أعطى التفسير التالي: $1",
        "missing-article": "لم تجد قاعدة البيانات نصّ صفحة كان يجب أن يوجد، الصفحة هي \"$1\" $2.\n\nعادة ما يحدث هذا عند اتباع فرق قديم أو رابط تأريخ صفحة محذوفة.\n\nإذا لم تكن هذه هي الحال فمن المحتمل أنك قد وقعت على علّة في البرمجية.\nمن فضلك أبلغ أحد [[Special:ListUsers/sysop|الإداريين]]، و أعطه مسار هذه الصفحة.",
        "missingarticle-rev": "(رقم المراجعة: $1)",
        "missingarticle-diff": "(فرق: $1، $2)",
        "viewsource": "اعرض المصدر",
        "viewsource-title": "استعرض مصدر $1",
        "actionthrottled": "تم كبح الفعل",
-       "actionthrottledtext": "احترازا من السُّخام، يُحظر إجراء هذا الفعل مرات كثيرة في فترة زمنية قصيرة، و لقد تجاوزت هذا الحد.\nمن فضلك حاول مجددا بعد عدة دقائق.",
+       "actionthrottledtext": "احترازا من السُّخام، يُحظر إجراء هذا الفعل مرات كثيرة في فترة زمنية قصيرة، ولقد تجاوزت هذا الحد.\nمن فضلك حاول مجددا بعد عدة دقائق.",
        "protectedpagetext": "هذه الصفحة تمت حمايتها لمنع التعديل أو أية عمليات أخرى.",
        "viewsourcetext": "يمكنك مطالعة و نسخ مصدر هذه الصفحة.",
        "viewyourtext": "يمكنك رؤية و نسخ مصدر <strong>تعديلاتك</strong> لهذه الصفحة.",
        "mypreferencesprotected": "ليس لديك صلاحية تعديل تفضيلاتك.",
        "ns-specialprotected": "الصفحات الخاصة لا يمكن تعديلها.",
        "titleprotected": "{{GENDER:$1|حمى|حمت}} [[User:$1|$1]] هذا العنوان من الإنشاء.\nالسبب المعطى هو ''$2''.",
-       "filereadonlyerror": "تعذر ØªØ¹Ø¯Ù\8aÙ\84 Ø§Ù\84Ù\85Ù\84Ù\81 \"$1\" Ù\84Ø£Ù\86 Ù\85ستÙ\88دع Ø§Ù\84Ù\85Ù\84Ù\81 \"$2\" Ù\81Ù\8a Ù\88ضع Ø§Ù\84Ù\82راءة Ù\81Ù\82Ø·. \n\nاÙ\84Ù\85دÙ\8aر الذي قام بغلقه قدم التفسير التالي: \"$3\".",
+       "filereadonlyerror": "تعذر ØªØ¹Ø¯Ù\8aÙ\84 Ø§Ù\84Ù\85Ù\84Ù\81 \"$1\" Ù\84Ø£Ù\86 Ù\85ستÙ\88دع Ø§Ù\84Ù\85Ù\84Ù\81 \"$2\" Ù\81Ù\8a Ù\88ضع Ø§Ù\84Ù\82راءة Ù\81Ù\82Ø·. \n\nإدارÙ\8a Ø§Ù\84Ù\86ظاÙ\85 الذي قام بغلقه قدم التفسير التالي: \"$3\".",
        "invalidtitle-knownnamespace": "عنوان غير صالح في النطاق «$2» مع نص «$3»",
        "invalidtitle-unknownnamespace": "عنوان غير صالح ذو نطاق غير معروف رقم $1 ونص «$2»",
        "exception-nologin": "غير مسجل الدخول",
        "wrongpassword": "كلمة السر التي أدخلتها غير صحيحة.\nمن فضلك حاول مرة أخرى.",
        "wrongpasswordempty": "كلمة السر المدخلة كانت فارغة.\nمن فضلك حاول مرة أخرى.",
        "passwordtooshort": "يجب أن تتكون كلمة السر على الأقل من {{PLURAL:$1|حرف واحد|حرفين|$1 حروف|$1 حرفا|$1 حرف}}.",
+       "passwordtoolong": "كلمات السر لا يجب أن تكون أطول من  {{PLURAL:$1|1 حرف|$1 حروف}}.",
        "password-name-match": "يجب أن تكون كلمة المرور مختلفة عن اسم المستخدم.",
        "password-login-forbidden": "تم منع استخدام اسم المستخدم هذا وكلمة السر.",
        "mailmypassword": "أعد تعيين كلمة السر",
        "passwordreset-emailtext-ip": "أحد ما (قد يكون أنت، من العنوان $1)  طلب إعادة ضبط كلمة سر حسابك على {{SITENAME}} ($4). {{PLURAL:$3||الحساب|الحسابان| الحسابات}} أدناه قد اقترنت ببريدك الإلكتروني :\n\n$2\n\n{{PLURAL:$3||كلمة السر المؤقتة|كلمات السر المؤقتة}} ستنتهي صلاحيتها في {{PLURAL:$5||يوم واحد|يومين|$5 أيام|$5 يوما|$5 يوم}}\nيمكنك تسجيل الدخول واختيار كلمة سر جديدة. إذا كان هذا الطلب تم بواسطة شخص أخر، أو إذا تذكرت كلمة السر الأصلية الخاصة بك، ولم تعد ترغب في تغييرها، يمكنك تجاهل هذه الرسالة ومتابعة استخدام كلمة السر القديمة.",
        "passwordreset-emailtext-user": "المستخدم $1 على {{SITENAME}} طلب إعادة ضبط كلمة سر حسابك على {{SITENAME}} ($4). {{PLURAL:$3||الحساب|الحسابان| الحسابات}} أدناه قد اقترنت ببريدك الإلكتروني :\n\n$2\n\n{{PLURAL:$3||كلمة السر المؤقتة|كلمات السر المؤقتة}} ستنتهي صلاحيتها في {{PLURAL:$5||يوم واحد|يومين|$5 أيام|$5 يوما|$5 يوم}}\nيمكنك تسجيل الدخول واختيار كلمة سر جديدة. إذا كان هذا الطلب تم بواسطة شخص أخر، أو إذا تذكرت كلمة السر الأصلية الخاصة بك، ولم تعد ترغب في تغييرها، يمكنك تجاهل هذه الرسالة ومتابعة استخدام كلمة السر القديمة.",
        "passwordreset-emailelement": "اسم {{GENDER:$1\n|المستخدم|المستخدمة}}: \n$1\n\nكلمة السر المؤقتة: \n$2",
-       "passwordreset-emailsentemail": "أُرسل بريد إلكتروني لإعادة ضبط كلمة السر.",
+       "passwordreset-emailsentemail": "إذا كان هذا العنوان البريد مرتبط بحسابك، من ثم سيتم إرسال بريد إلكتروني لإعادة تعيين كلمة السر.",
+       "passwordreset-emailsentusername": "إذا كان هناك عنوان بريد إلكتروني مرتبط بهذا المستخدم، ثم سيتم إرسال بريد إلكتروني لإعادة تعيين كلمة السر.",
        "passwordreset-emailsent-capture": "أُرسل بريد إلكتروني لإعادة ضبط كلمة السر، وهو معروض بالأسفل.",
        "passwordreset-emailerror-capture": "تم توليد رسالة بريد إلكتروني لتصفير كلمة السر نصّه التالي، إلا أنه تعذّر إرسال الرّسالة إلى {{GENDER:$2|المستخدم|المستخدمة}}: $1",
        "changeemail": "تغيير أو إزالة عنوان البريد الإلكتروني",
        "copyrightwarning2": "من فضلك لاحظ أن جميع المساهمات في {{SITENAME}} يمكن أن تعدل أو تتغير أو تزال من قبل المساهمين الآخرين.\nإذا لم تكن ترغب أن تعدل مشاركاتك بهذا الشكل، لا تضعها هنا.<br />\nأنت تقر أيضا أنك كتبت هذا بنفسك، أو نسخته من مصدر يخضع للملكية العامة، أو مصدر حر آخر (انظر $1 للتفاصيل).\n'''لا تضف أي عمل ذي حقوق محفوظة بدون تصريح!'''",
        "editpage-cannot-use-custom-model": "نموذج المحتوى لهذه الصفحة لا يمكن تغييره.",
        "longpageerror": "'''خطأ: النص الذي قمت بإدخاله {{PLURAL:$1|واحد كيلوبايت|$1 كيلوبيات}} أطول, وهو أطول من الحد الأقصى {{PLURAL:$2|واحد كيلوبايت|$2 كيلوبايت}}.'''\nو يتعذر حفظه.",
-       "readonlywarning": "'''تحذير: لقد أغلقت قاعدة البيانات للصيانة، لذلك لن تتمكن من حفظ التعديلات التي قمت بها حاليا.\nإذا رغبت بإمكانك أن تنسخ النص الذي تعمل عليه وتحفظه في ملف نصي إلى وقت لاحق.'''\n\nالإداري الذي أغلقها أعطى هذا التفسير: $1",
+       "readonlywarning": "<strong>تحذير: لقد أغلقت قاعدة البيانات للصيانة، لذلك لن تتمكن من حفظ التعديلات التي قمت بها حاليا.\nإذا رغبت بإمكانك أن تنسخ النص الذي تعمل عليه وتحفظه في ملف نصي إلى وقت لاحق.</strong>\n\nإداري النظام الذي أغلقها أعطى هذا التفسير: $1",
        "protectedpagewarning": "'''تحذير: تمت حماية هذه الصفحة حتى يمكن للمستخدمين ذوي الصلاحيات الإدارية فقط تعديلها.'''\nآخر مدخلة سجل موفرة بالأسفل كمرجع:",
        "semiprotectedpagewarning": "'''ملاحظة:''' هذه الصفحة محمية بحيث يمكن للمستخدمين المسجلين وحدهم تعديلها.",
        "cascadeprotectedwarning": "<strong>تحذير:</strong> تمت حماية هذه الصفحة بحيث يستطيع المستخدمون ذوو الصلاحيات الإدارية فقط تعديلها، وذلك لأنها مدمجة في {{PLURAL:$1||الصفحة التالية والتي تمت حمايتها|الصفحتين التاليتين واللتين تمت حمايتها|الصفحات التالية والتي تمت حمايتها}} بخاصية \"حماية الصفحات المدمجة\":",
        "right-blockemail": "منع مستخدم من إرسال بريد إلكتروني",
        "right-hideuser": "منع اسم مستخدم، مخفيا إياه عن العامة",
        "right-ipblock-exempt": "تفادي عمليات منع الأيبي، المنع التلقائي ومنع النطاق",
-       "right-proxyunbannable": "تفادي عمليات المنع الأوتوماتيكية للبروكسيهات",
        "right-unblockself": "رفع المنع عن أنفسهم",
        "right-protect": "تغيير مستويات الحماية وتعديل الصفحات المحمية",
        "right-editprotected": "تعديل الصفحات التي حمايتها \"{{int:protect-level-sysop}}\"",
        "right-passwordreset": "عرض رسائل إعادة ضبط كلمات السر",
        "right-managechangetags": "إنشاء وحذف [[Special:Tags|الوسوم]] من قاعدة البيانات",
        "right-applychangetags": "تطبيق [[Special:Tags|الوسوم]]  مع التغييرات التي أجريتها.",
+       "grant-generic": "\"$1\" حزمة الصلاحيات",
        "newuserlogpage": "سجل إنشاء المستخدمين",
        "newuserlogpagetext": "هذا سجل بعمليات إنشاء المستخدمين.",
        "rightslog": "سجل صلاحيات المستخدمين",
        "mw-widgets-dateinput-no-date": "لا تاريخ تم اختياره",
        "mw-widgets-titleinput-description-new-page": "الصفحة غير موجودة بعد",
        "mw-widgets-titleinput-description-redirect": "تحويل إلى $1",
-       "api-error-blacklisted": "اختر عنوانا مختلفا ومفهوما."
+       "api-error-blacklisted": "اختر عنوانا مختلفا ومفهوما.",
+       "randomrootpage": "صفحة جذر عشوائية"
 }
index fc2fc81..3e7650d 100644 (file)
@@ -4,7 +4,8 @@
                        "Bachounda",
                        "Oldstoneage",
                        "아라",
-                       "Amire80"
+                       "Amire80",
+                       "GeekEmad"
                ]
        },
        "tog-underline": "تسطار الوصيلات:",
        "nstab-template": "مودال",
        "nstab-help": "باجة تاع معاونة",
        "nstab-category": "تصنيف",
+       "mainpage-nstab": "الپاجة اللولانيّة",
        "nosuchaction": "الشي الّي طلبتهُ ما كاينش",
        "nosuchactiontext": "الفعلة الّي مطلوبة فل URL ماشي مقبولة.\nبالاك ما دخّلتوش الـ URL كيما لازم ولا تاني تبّعتو كاش وصيل مغلوط.\nينجم تاني يكون كاين عُلّة فل لوجيسيال الّي مستعمل فـ {{SITENAME}}.",
        "nosuchspecialpage": "هاد الباجة الخوصوصيّة ما كاينش منها",
        "createaccountreason": "سبّة:",
        "createacct-reason": "سبّة",
        "createacct-reason-ph": "علاش راك تخلق حساب وحداخُر",
-       "createacct-captcha": "تحقق أمني",
-       "createacct-imgcaptcha-ph": "دخّل النصّ الّي راك تشوفهُ لفوق",
        "createacct-submit": "اصنع حسابك",
        "createacct-another-submit": "اخلق حساب وحداخُر",
        "createacct-benefit-heading": "{{SITENAME}} مخلوق من عند شي ناس غير كيفك.",
        "passwordreset-emailtext-ip": "شي واحد (يكون بالاك نتا، لادريسة إيپي $1) راه طلَب المصاوبة تاع كلمت` السرّ تاعك ف {{SITENAME}} ($4). {{PLURAL:$3|هاد الحساب |هاد الحسابات}} تاع المستعملي {{PLURAL:$3|راه مربوط|راهم مربوطين}} ب لادريسة تاع الإيمال:\n\n$2\n\n{{PLURAL:$3|هاد كلمت` السرّ المأقّتة|هادي كلمات` السرّ المأقّتة}} غادي يكمل صلوحها منّا على {{PLURAL:$5|نهار واحد|$5 إيّام}}.\nمليح لوكان تدخُل ل`السيت من ضركا و تبدّل كلمت` السرّ.\nيلا كاش ما وحداخُر دار هاد المطلب ولا راك تفكّرت كلمت` السرّ تاعك و ما بقيتش باغي تبدّلها، تنجم برك تنسا هاد الميساج و تستعمل كلمت` السرّ تاعك تاع مضاري.",
        "passwordreset-emailtext-user": "المستعملي $1 ف {{SITENAME}} راه طلب تبدال ف كلمت` السرّ تاعك ف {{SITENAME}}\n($4). {{PLURAL:$3|الحساب|الحسايات}} تاع المستعملي {{PLURAL:$3|راه مربوط|راهم مربوطين}} ب لادريسة تاع ليمال هادي:\n\n$2\n\n{{PLURAL:$3|هاد كلمت` السرّ المأقّتة|هادي كلمات` السرّ المأقّتة}} غادي يكمل صلوحها منّا على {{PLURAL:$5|نهار واحد|$5 إيّام}}.\nمادابيك تسجّل داخل ضركا و تختار كلمت` سرّ جديدة. يلا كان وحداخُر دار هاد المطلب، ولا راك ضركا تفكّرت كلمت` السرّ تاعك القديمة و ما بقيتش باغي تبدّلها، تنجم برك تتنسّا هاد الميساج و تدخُل ب كلمت` السرّ تاعك تاع مضاري.",
        "passwordreset-emailelement": "سميّت` المستعملي: \n$1\n\nكلمت` السرّ المأقّتة: \n$2",
-       "passwordreset-emailsent": "راه نبعَت إيمال تاع تبدال كلمت` السرّ.",
+       "passwordreset-emailsentemail": "راه نبعَت إيمال تاع تبدال كلمت` السرّ.",
        "passwordreset-emailsent-capture": "راه اترسل إيمال تاع تبدال كلمت` السرّ، و راه محطوط هنا لتحت.",
        "passwordreset-emailerror-capture": "راه اترسل الإيمال تاع تبدال كلمت` السرّ، الّي راح محطوط هنا لتحت، بصّح البعيت تاعهُ لل {{GENDER:$2|مستعملي}} ما نجحش: $1",
        "changeemail": "بدّل لادريسة تاع الإيمال",
-       "changeemail-text": "كمّل الكتبة ف` الجدوال هادا باش تبدّل لادريسة تاع الإيمال تاعك. يلزم لك تدخّل كلمت` السرّ تاعك باش تأكّد هاد التبدال.",
+       "changeemail-header": "كمّل الكتبة ف` الجدوال هادا باش تبدّل لادريسة تاع الإيمال تاعك. يلزم لك تدخّل كلمت` السرّ تاعك باش تأكّد هاد التبدال.",
        "changeemail-no-info": "لازم لك تكون مسجّل داخل باش توصَل ل هاد الپاجة بسّراح.",
        "changeemail-oldemail": "لادريسة تاع الإيمال السارية:",
        "changeemail-newemail": "لادريسة تاع الإيمال الجديدة:",
        "unwatch": "ما تزيدش تعس",
        "watchlist-details": "{{PLURAL:$1||باجه وحده|باجتين|$1 باجات|$1 باجه}} في ليستت مراقبتك، من غير اعتبار باجات النقاش هي باجات منفصله.",
        "wlshowlast": "بين آخر $1 سوايع $2 يامات",
+       "watchlistall2": "لكل",
        "watchlist-options": "ابسيون ليستت المراقبه",
        "actioncomplete": "العمليه اندارت",
        "actionfailed": "العمليه فشلت",
        "contributions": "مساهمات {{GENDER:$1|المستخدم|المستخدمه}}",
        "contributions-title": "مساهمات {{GENDER:$1|المستخدم|المستخدمه}} $1",
        "mycontris": "المساهمات تاعي",
+       "anoncontribs": "المساهمات",
        "contribsub2": "ل{{GENDER:$3|$1}} ($2)",
        "uctop": "ذ الوقت",
        "month": "من شهر (وأقدم):",
        "tooltip-pt-logout": "سجل خروج",
        "tooltip-pt-createaccount": "ننصح باش تصنع حساب و تسجل دخلتك ; على كل حال ماهوش ضروري",
        "tooltip-ca-talk": "مناقشه على هاد باجت المحتوا",
-       "tooltip-ca-edit": "تÙ\82در ØªØ¨Ø¯Ù\84 Ù\87اذ Ø§Ù\84باجÙ\87 Ø\8cÙ\85اذابÙ\8aÙ\83 ØªØ³ØªØ¹Ù\85Ù\84 Ù\82Ù\81Ù\84Ù\87 Ø§Ù\84Ù\85راجعÙ\87 Ù\82بÙ\84 Ù\85ا ØªØ³Ø¬Ù\84",
+       "tooltip-ca-edit": "بدÙ\91Ù\84 Ù\87اد Ø§Ù\84Ù\80صÙ\81Ø­Ù\87",
        "tooltip-ca-addsection": "ابدأ طرف جديد",
        "tooltip-ca-viewsource": "هاذ الباجه محميه. و شنو تقدرو تشوفو الأصلي نتاعها",
        "tooltip-ca-history": "المراجعات التوالا تاع الباجة (معا المساهمين تاوعها)",
        "tooltip-t-permalink": "وصيل دايم رايح ل هاد النسخة تاع الباجة",
        "tooltip-ca-nstab-main": "شوف باجه المحتوى",
        "tooltip-ca-nstab-user": "شوف باجت المستعمل",
-       "tooltip-ca-nstab-special": "هذه الباجه خصوصيه،ما تقدرش تبدل فيها",
+       "tooltip-ca-nstab-special": "هذه الباجه خصوصيه، و ما تقدرش تتبدل",
        "tooltip-ca-nstab-project": "شوف باجت البروجي",
        "tooltip-ca-nstab-image": "شوف باجت الملف",
        "tooltip-ca-nstab-template": "شوفان القالب",
index 14d07ba..138696f 100644 (file)
        "suppressionlog": "سجل الإخفاء",
        "suppressionlogtext": "تحت فى لستة بعمليات المسح والمنع اللى فيها محتوى مستخبى على الإداريين.\nشوف [[Special:IPBlockList|للستة المنع]] علشان تشوف عمليات المنع الشغالة دلوقتى .",
        "mergehistory": "دمج تواريخ الصفحة",
-       "mergehistory-header": " الصفحةدى  بتسمح لك بدمج نسخ تاريخ صفحة  فى صفحة تانية.\nاتأكد من أن التغيير دا ح يحافظ على استمرارية تاريخ الصفحة.",
+       "mergehistory-header": "الصفحة دى بتسمح لك بدمج نسخ تاريخ صفحة  فى صفحة تانية.\nاتأكد من أن التغيير دا ح يحافظ على استمرارية تاريخ الصفحة.",
        "mergehistory-box": "دمج تعديلات صفحتين:",
        "mergehistory-from": "الصفحه المصدر:",
        "mergehistory-into": "الصفحه الهدف:",
        "right-blockemail": "منع يوزر من إنه يبعت إيميل",
        "right-hideuser": "منع اسم يوزر، و خبيه عن الناس",
        "right-ipblock-exempt": "إتفادى عمليات منع الأيبي، المنع الأوتوماتيكى ومنع النطاق.",
-       "right-proxyunbannable": "إتفادى عمليات المنع الأوتوماتيكية للبروكسيهات",
        "right-unblockself": "رفع المنع عن نفسهم",
        "right-protect": "تغيير مستويات الحماية وتعديل الصفحات المحمية",
        "right-editprotected": "تعديل الصفحات المحمية (من غير الحماية المتضمنة)",
        "special-characters-group-bangla": "بانجلاديشى",
        "special-characters-group-telugu": "Telugu",
        "special-characters-group-sinhala": "Sinhala",
-       "special-characters-group-gujarati": "Gujarati"
+       "special-characters-group-gujarati": "Gujarati",
+       "randomrootpage": "صفحة جذر عشوائية"
 }
index 280086c..f9572d1 100644 (file)
        "october-date": "$1 d'ochobre",
        "november-date": "$1 de payares",
        "december-date": "$1 d'avientu",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoría|Categoríes}}",
        "category_header": "Páxines na categoría «$1»",
        "subcategories": "Subcategoríes",
        "databaseerror-query": "Consulta: $1",
        "databaseerror-function": "Función: $1",
        "databaseerror-error": "Error: $1",
-       "transaction-duration-limit-exceeded": "Para evitar crear un gran atrasu na replicación, esta transaición albortóse porque la duración de la escritura ($1) pasó la llende de $2 segundos.\nSi tas cambiando munchos oxetos al mesmu tiempu, intenta meyor facer operaciones múltiples más pequeñes.",
+       "transaction-duration-limit-exceeded": "Para evitar crear un gran atrasu na replicación, esta transaición albortóse porque la duración de la escritura ($1) pasó la llende de $2 {{PLURAL:$2|segundu|segundos}}.\nSi tas cambiando munchos oxetos al mesmu tiempu, intenta meyor facer operaciones múltiples más pequeñes.",
        "laggedslavemode": "'''Avisu:''' Esta páxina pue que nun tenga actualizaciones recientes.",
        "readonly": "Base de datos candada",
        "enterlockreason": "Introduz un motivu pal candáu, amestando una estimación de cuándo va tener llugar el descandáu",
        "virus-scanfailed": "fallu d'escanéu (códigu $1)",
        "virus-unknownscanner": "antivirus desconocíu:",
        "logouttext": "'''Zarró la sesión.'''\n\nTenga en cuenta que dalgunes páxines puen siguir apaeciendo como si inda tuviera la sesión aniciada, mentanto nun llimpie la caché del navegador.",
+       "cannotlogoutnow-title": "Nun puede zarrase sesión agora",
+       "cannotlogoutnow-text": "Nun puede zarrase sesión cuando s'usa $1.",
        "welcomeuser": "¡Bienllegáu, $1!",
        "welcomecreation-msg": "Creóse la to cuenta.\nNun t'escaezas de camudar les tos [[Special:Preferences|preferencies de {{SITENAME}}]].",
        "yourname": "Nome d'usuariu:",
        "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",
+       "cannotloginnow-text": "Nun puede aniciase sesión cuando s'usa $1.",
        "yourdomainname": "El to dominiu:",
        "password-change-forbidden": "Nun se pueden camudar les contraseñes nesta wiki.",
        "externaldberror": "O hebo un fallu d'autenticación de la base de datos o nun tienes permisu p'anovar la to cuenta esterna.",
        "resetpass_submit": "Configurar la contraseña y aniciar sesión",
        "changepassword-success": "¡Camudóse la contraseña correutamente!",
        "changepassword-throttled": "Ficisti demasiaos intentos d'aniciu de sesión recientes.\nPor favor espera $1 enantes d'intentalo otra vuelta.",
+       "botpasswords": "Contraseñes de bots",
+       "botpasswords-summary": "Les <em>contraseñes de bot</em> permiten l'accesu a una cuenta d'usuariu por aciu de la API sin usar les credenciales d'accesu de la cuenta principal. Los permisos d'usuariu disponibles al aniciar sesión con una contraseña de bot puen tar torgaos.\n\nSi nun sabes pa qué val esto, probablemente nun tendríes d'usalo. Naide tendría de pidite nunca que xeneres una d'estes y que-y la deas.",
+       "botpasswords-disabled": "Les contraseñes de bot tán desactivaes.",
+       "botpasswords-no-central-id": "Pa usar contraseñes de bot tienes d'aniciar sesión con una cuenta centralizada.",
+       "botpasswords-existing": "Contraseñes de bots esistentes",
+       "botpasswords-createnew": "Crear una contraseña nueva de bot",
+       "botpasswords-editexisting": "Editar una contraseña de bot esistiente",
+       "botpasswords-label-appid": "Nome del bot:",
+       "botpasswords-label-create": "Crear",
+       "botpasswords-label-update": "Anovar",
+       "botpasswords-label-cancel": "Encaboxar",
+       "botpasswords-label-delete": "Desaniciar",
+       "botpasswords-label-resetpassword": "Reestablecer la contraseña",
+       "botpasswords-label-grants": "Permisos aplicables:",
+       "botpasswords-help-grants": "Cada permisu da accesu a los permisos de usuario llistaos que yá tenga la cuenta. Mira la [[Special:ListGrants|tabla de permisos]] pa más información.",
+       "botpasswords-label-restrictions": "Torgues d'usu:",
+       "botpasswords-label-grants-column": "Permitío",
+       "botpasswords-bad-appid": "El nome del bot \"$1\" nun ye válidu.",
+       "botpasswords-insert-failed": "Nun pudo amestase'l nome de bot «$1». ¿Taba añadíu yá?",
+       "botpasswords-update-failed": "Nun pudo anovase'l nome de bot «$1». ¿Desaniciaríase?",
+       "botpasswords-created-title": "Creóse la contraseña de bot",
+       "botpasswords-created-body": "La contraseña de bot «$1» creóse correchamente.",
+       "botpasswords-updated-title": "Anovóse la contraseña de bot",
+       "botpasswords-updated-body": "La contraseña de bot «$1» anovóse correchamente.",
+       "botpasswords-deleted-title": "Desanicióse la contraseña de bot",
+       "botpasswords-deleted-body": "La contraseña de bot «$1» desanicióse.",
+       "botpasswords-newpassword": "La nueva contraseña p'aniciar sesión con strong>$1</strong> ye <strong>$2</strong>. <em>Por favor, rexistra esto pa referencies futures.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider nun ta disponible.",
+       "botpasswords-restriction-failed": "Hai torgues de contraseña de bot que torgaron esti aniciu de sesión.",
+       "botpasswords-invalid-name": "El nome d'usuariu especificáu nun contien el separador de contraseña de bot («$1»).",
+       "botpasswords-not-exist": "L'usuariu «$1» nun tien una contraseña de bot llamada «$2».",
        "resetpass_forbidden": "Nun puen camudase les contraseñes",
        "resetpass-no-info": "Tienes d'aniciar sesión pa entrar direutamente a esta páxina.",
        "resetpass-submit-loggedin": "Camudar la contraseña",
        "passwordreset-emailtext-ip": "Dalguién (seique vusté, dende la direición IP $1)solicitó'l reaniciu de la so contraseña de {{SITENAME}} ($4).\n{{PLURAL:$3|La cuenta d'usuariu siguiente ta asociada|Les cuentes d'usuariu siguientes tán asociaes}}\na esta direición de corréu electrónicu:\n\n$2\n\n{{PLURAL:$3|Esta contraseña provisional caduca|Estes contraseñes provisionales caduquen}} {{PLURAL:$5|nun día|en $5 díes}}.\nTendría d'aniciar sesión y escoyer una contraseña nueva agora. Si esta solicitú la fizo otra persona,\no si recordó la clave orixinal y yá nun quier camudala, pue escaecer esti mensaxe y siguir\nusando la contraseña antigua.",
        "passwordreset-emailtext-user": "L'usuariu $1 de {{SITENAME}} solicitó un reaniciu de la so contraseña de {{SITENAME}} ($4). {{PLURAL:$3|La cuenta d'usuariu siguiente ta asociada|Les cuentes d'usuariu siguientes tán asociaes}} con esta direición de corréu electrónicu:\n\n$2\n\n{{PLURAL:$3|Esta contraseña provisional caduca|Estes contraseñes provisionales caduquen}} {{PLURAL:$5|nun día|en $5 díes}}.\nTendría d'aniciar sesión y escoyer una contraseña nueva agora. Si esta solicitú la fizo otra persona, o si recordó la clave orixinal y yá nun quier camudala, pue escaecer esti mensaxe y siguir usando la contraseña antigua.",
        "passwordreset-emailelement": "Nome d'usuariu: \n$1\n\nContraseña temporal: \n$2",
-       "passwordreset-emailsentemail": "Si esta ye una direición de corréu electrónicu rexistrada pa la to cuenta, unviaráse un corréu pa reaniciar la contraseña.",
-       "passwordreset-emailsentusername": "Si hai una direición rexistrada de corréu electrónicu correspondiente a esta cuenta, unviaráse un corréu electrónicu pa reaniciar la contraseña.",
+       "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",
        "changeemail": "Camudar o desaniciar la dirección de corréu electrónicu",
        "userrights": "Xestión de permisos d'usuariu",
        "userrights-lookup-user": "Xestión de grupos del usuariu",
        "userrights-user-editname": "Escribe un nome d'usuariu:",
-       "editusergroup": "Editar los grupos del usuariu",
+       "editusergroup": "Editar los grupos {{GENDER:$1|del usuariu|de la usuaria}}",
        "editinguser": "Camudando los permisos {{GENDER:$1|del usuariu|de la usuaria}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Editar los grupos d'usuariu",
-       "saveusergroups": "Guardar los grupos d'usuariu",
+       "saveusergroups": "Guardar los grupos {{GENDER:$1|del usuariu|de la usuaria}}",
        "userrights-groupsmember": "Miembru de:",
        "userrights-groupsmember-auto": "Miembru implícitu de:",
        "userrights-groups-help": "Pues camudar los grupos a los que pertenez esti usuariu.\n* Un caxellu marcáu significa que l'usuariu ta nesi grupu.\n* Un caxellu non marcáu significa que l'usuariu nun ta nesi grupu.\n* Un * indica que nun pues eliminalu del grupu una vegada tea inxeríu, o viceversa.",
        "right-createpage": "Crear páxines (que nun seyan páxines d'alderique)",
        "right-createtalk": "Crear páxines d'alderique",
        "right-createaccount": "Crear cuentes nueves d'usuariu",
+       "right-autocreateaccount": "Aniciar sesión automáticamente con una cuenta d'usuariu esterna",
        "right-minoredit": "Marcar ediciones como menores",
        "right-move": "Treslladar páxines",
        "right-move-subpages": "Treslladar les páxines coles sos subpáxines",
        "right-blockemail": "Bloquia-y l'unviu de corréu electrónicu a un usuariu",
        "right-hideuser": "Bloquiar un nome d'usuariu, tapeciéndolu al públicu",
        "right-ipblock-exempt": "Saltar los bloqueos d'IP, los autobloqueos y los bloqueos de rangos",
-       "right-proxyunbannable": "Saltar los bloqueos automáticos de los proxys",
        "right-unblockself": "Desbloquiase ún mesmu",
        "right-protect": "Camudar los niveles de proteición y editar páxines protexíes en cascada",
        "right-editprotected": "Editar les páxines protexíes como \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Crear y desaniciar [[Special:Tags|etiquetes]] dende la base de datos",
        "right-applychangetags": "Aplicar [[Special:Tags|etiquetes]] xunto colos cambios propios",
        "right-changetags": "Amestar y desaniciar [[Special:Tags|etiquetes]] arbitraries en revisiones individuales y entraes del rexistru",
+       "grant-generic": "Conxuntu de drechos \"$1\"",
+       "grant-group-page-interaction": "Interactuar con páxines",
+       "grant-group-file-interaction": "Interactuar con multimedia",
+       "grant-group-watchlist-interaction": "Interactuar cola to llista de vixilancia",
+       "grant-group-email": "Unviar correos",
+       "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-other": "Actividaes variaes",
+       "grant-blockusers": "Bloquiar y desbloquiar usuarios",
+       "grant-createaccount": "Crear cuentes",
+       "grant-createeditmovepage": "Crear, editar y mover páxines",
+       "grant-delete": "Desaniciar páxines, revisiones y entraes del rexistru",
+       "grant-editinterface": "Editar l'espaciu de nomes MediaWiki y los CSS/JavaScript d'usuariu",
+       "grant-editmycssjs": "Editar los CSS/JavaScript d'usuariu propios",
+       "grant-editmyoptions": "Editar les preferencies d'usuariu propies",
+       "grant-editmywatchlist": "Editar la llista de vixilancia propia",
+       "grant-editpage": "Editar páxines esistentes",
+       "grant-editprotected": "Editar páxines protexíes",
+       "grant-highvolume": "Ediciones de gran volume",
+       "grant-oversight": "Tapecer usuarios y desaniciar revisiones",
+       "grant-patrol": "Patrullar los cambios fechos nes páxines",
+       "grant-protect": "Protexer y desprotexer páxines",
+       "grant-rollback": "Desfacer los cambios fechos nes páxines",
+       "grant-sendemail": "Unviar corréu a otros usuarios",
+       "grant-uploadeditmovefile": "Xubir, trocar y mover ficheros",
+       "grant-uploadfile": "Xubir ficheros nuevos",
+       "grant-basic": "Permisos básicos",
+       "grant-viewdeleted": "Ver los ficheros y páxines desaniciaos",
+       "grant-viewmywatchlist": "Ver la to llista de siguimientu",
        "newuserlogpage": "Rexistru de creación d'usuarios",
        "newuserlogpagetext": "Esti ye un rexistru de creación d'usuarios.",
        "rightslog": "Rexistru de perfil d'usuariu",
        "action-createpage": "crear páxines",
        "action-createtalk": "crear páxines 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-minoredit": "marcar esta edición como menor",
        "action-move": "treslladar esta páxina",
        "upload-form-label-select-file": "Seleiciona un ficheru",
        "upload-form-label-infoform-title": "Detalles",
        "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-name-tooltip": "Un títulu descriptivu únicu pal ficheru, que sirvirá para da-y nome al ficheru. Se pue usar llinguax normal con espacios. Nun amiestes la estensión del ficheru.",
        "upload-form-label-infoform-description": "Descripción",
+       "upload-form-label-infoform-description-tooltip": "Describe de mou curtiu cualquier cosa notable de la obra.\nPa una semeya, cuenta les principales coses qu'apaecen, la ocasión o'l sitiu.",
        "upload-form-label-usage-title": "Usu",
        "upload-form-label-usage-filename": "Nome del ficheru",
        "foreign-structured-upload-form-label-own-work": "Esti ye'l mio propiu trabayu",
        "log-title-wildcard": "Buscar títulos qu'emprimen con esti testu",
        "showhideselectedlogentries": "Amosar/anubrir les entraes del rexistru seleicionaes",
        "log-edit-tags": "Editar les etiquetes de les entraes del rexistru seleicionaes",
+       "checkbox-select": "Seleicionar: $1",
+       "checkbox-all": "Too",
+       "checkbox-none": "Nengunu",
+       "checkbox-invert": "Invertir",
        "allpages": "Toles páxines",
        "nextpage": "Páxina siguiente ($1)",
        "prevpage": "Páxina anterior ($1)",
        "listgrouprights-namespaceprotection-header": "Torgues d'espaciu de nomes",
        "listgrouprights-namespaceprotection-namespace": "Espaciu de nomes",
        "listgrouprights-namespaceprotection-restrictedto": "Permisu(os) d'edición del usuariu",
+       "listgrants": "Permisos",
+       "listgrants-summary": "Esta ye la llista d'autorizaciones colos accesos asociaos a los permisos de usuario. los usuarios puen autorizar a les aplicaciones qu'usen la so cuenta, pero con permisos llendaos basándose nos permisos que'l usuariu diera a l'aplicación. Sicasí, una aplicación qu'actua nel nome d'un usuariu nun pué usar realmente permisos que nun tenga'l propiu usuariu.\nPué haber [[{{MediaWiki:Listgrouprights-helppage}}|más información]] tocante a los permisos individuales.",
+       "listgrants-grant": "Permisu",
+       "listgrants-rights": "Permisos",
        "trackingcategories": "Categoríes de siguimientu",
        "trackingcategories-summary": "Esta páxina llista les categoríes de siguimientu que rellena automáticamente'l software de MediaWiki. Puen camudase los nomes alterando los mensaxes del sistema correspondientes nel espaciu de nomes {{ns:8}}.",
        "trackingcategories-msg": "Categoría de siguimientu",
        "wlshowhideanons": "usuarios anónimos",
        "wlshowhidepatr": "ediciones supervisaes",
        "wlshowhidemine": "les mios ediciones",
+       "wlshowhidecategorization": "categorización de páxina",
        "watchlist-options": "Opciones de la llista de siguimientu",
        "watching": "Vixilando...",
        "unwatching": "Dexando de vixilar...",
        "unblock": "Desbloquiar usuariu",
        "blockip": "Bloquiar {{GENDER:$1|al usuariu|a la usuaria}}",
        "blockip-legend": "Bloquiar usuariu",
-       "blockiptext": "Usa'l siguiente formulariu pa bloquiar el permisu d'escritura a una IP o a un usuariu concretu.\nEsto debería facese sólo pa prevenir vandalismu como indiquen les [[{{MediaWiki:Policy-url}}|polítiques]]. Da un motivu específicu (como por exemplu citar páxines que fueron vandalizaes).",
+       "blockiptext": "Usa'l siguiente formulariu pa bloquiar l'accesu d'escritura a una direición IP o a un usuariu concretu.\nEsto debería facese sólo pa prevenir vandalismu como indiquen les [[{{MediaWiki:Policy-url}}|polítiques]]. Da un motivu específicu (como por exemplu citar páxines que fueron vandalizaes).\nPues bloquiar rangos d'IPs usando la sintaxis [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; el mayor rangu permitíu ye /$1 pa IPv4 y /$2 pa IPv6.",
        "ipaddressorusername": "Direición IP o nome d'usuariu:",
        "ipbexpiry": "Caducidá:",
        "ipbreason": "Motivu:",
        "block-log-flags-hiddenname": "nome d'usuariu anubríu",
        "range_block_disabled": "La capacidá d'alministrador pa crear bloqueos d'intervalos ta desactivada.",
        "ipb_expiry_invalid": "Tiempu incorrectu.",
+       "ipb_expiry_old": "La fecha de caducidá ta pasada.",
        "ipb_expiry_temp": "Los bloqueos de nome d'usuariu escondíos han ser permanentes.",
        "ipb_hide_invalid": "Nun se pue desaniciar esta cuenta; tien más {{PLURAL:$1|d'una edición|de $1 ediciones}}.",
        "ipb_already_blocked": "\"$1\" yá ta bloquiáu",
        "export-download": "Guardar como archivu",
        "export-templates": "Inxerir plantíes",
        "export-pagelinks": "Incluyir páxines enllazaes fasta una profundidá de:",
+       "export-manual": "Amestar páxines a mano:",
        "allmessages": "Tolos mensaxes del sistema",
        "allmessagesname": "Nome",
        "allmessagesdefault": "Testu predetermináu",
        "javascripttest-pagetext-frameworks": "Escueyi un de los siguientes entornos de pruebes: $1",
        "javascripttest-pagetext-skins": "Escueyi una apariencia pa executar les pruebes:",
        "javascripttest-qunit-intro": "Ver la [$1 documentación de les pruebes] en mediawiki.org.",
-       "tooltip-pt-userpage": "La to páxina d'usuariu",
+       "tooltip-pt-userpage": "La to páxina d'{{GENDER:|usuariu|usuaria}}",
        "tooltip-pt-anonuserpage": "La páxina d'usuariu de la IP cola que tas editando",
-       "tooltip-pt-mytalk": "La to páxina d'alderique",
+       "tooltip-pt-mytalk": "La {{GENDER:|to}} páxina d'alderique",
        "tooltip-pt-anontalk": "Alderique de les ediciones feches con esta direición IP",
-       "tooltip-pt-preferences": "Les tos preferencies",
+       "tooltip-pt-preferences": "Les {{GENDER:|tos}} preferencies",
        "tooltip-pt-watchlist": "Llista de les páxines nes que tas vixilando los cambios",
-       "tooltip-pt-mycontris": "Llista de les tos collaboraciones",
+       "tooltip-pt-mycontris": "Llista de les {{GENDER:|tos}} collaboraciones",
        "tooltip-pt-anoncontribs": "Una llista d'ediciones feches dende esta dirección IP",
        "tooltip-pt-login": "T'encamentamos que t'identifiques, anque nun ye obligatorio",
        "tooltip-pt-logout": "Salir",
        "tooltip-t-recentchangeslinked": "Cambios recientes nes páxines enllazaes dende esta",
        "tooltip-feed-rss": "Canal RSS pa esta páxina",
        "tooltip-feed-atom": "Canal Atom pa esta páxina",
-       "tooltip-t-contributions": "Llista de collaboraciones d'esti usuariu",
-       "tooltip-t-emailuser": "Unvia un corréu a esti usuariu",
+       "tooltip-t-contributions": "Llista de collaboraciones d'{{GENDER:$1|esti usuariu|esta usuaria}}",
+       "tooltip-t-emailuser": "Unvia un corréu a {{GENDER:$1|esti usuariu|esta usuaria}}",
        "tooltip-t-info": "Más información sobro esta páxina",
        "tooltip-t-upload": "Xubir ficheros",
        "tooltip-t-specialpages": "Llista de toles páxines especiales",
        "pageinfo-category-files": "Númberu de ficheros",
        "markaspatrolleddiff": "Marcar como supervisada",
        "markaspatrolledtext": "Marcar esta páxina como supervisada",
+       "markaspatrolledtext-file": "Marcar esta versión del ficheru como patrullada",
        "markedaspatrolled": "Marcar como supervisada",
        "markedaspatrolledtext": "La revisión seleicionada de [[:$1]] se marcó como supervisada.",
        "rcpatroldisabled": "Supervisión de cambios recientes desactivada",
        "newimages-legend": "Peñera",
        "newimages-label": "Nome d'archivu (o una parte d'él):",
        "newimages-showbots": "Ver les xubíes de los bots",
+       "newimages-hidepatrolled": "Despintar les entraes patrullaes",
        "noimages": "Nun hai nada que ver.",
        "ilsubmit": "Guetar",
        "bydate": "por fecha",
        "scarytranscludefailed-httpstatus": "[Falló la recuperación de la plantía pa $1: HTTP $2]",
        "scarytranscludetoolong": "[La URL ye demasiao llarga]",
        "deletedwhileediting": "'''Avisu''': ¡Esta páxina desanicióse depués de qu'entamaras a editala!",
-       "confirmrecreate": "L'usuariu [[User:$1|$1]] ([[User talk:$1|alderique]]) esborró esta páxina depués de qu'empecipiaras a editala pol siguiente motivu:\n: ''$2''\nPor favor confirma que daveres quies volver a crear esta páxina.",
-       "confirmrecreate-noreason": "L'usuariu [[User:$1|$1]] ([[User talk:$1|talk]]) desanició esta páxina depués de que principiaras a editala.  Por favor confirma que daveres quies volver a crear esta páxina.",
+       "confirmrecreate": "{{GENDER:$1|L'usuariu|La usuaria}} [[User:$1|$1]] ([[User talk:$1|alderique]]) desanició esta páxina depués de qu'empecipiaras a editala pol siguiente motivu:\n: <em>$2</em>\nPor favor confirma que daveres quies volver a crear esta páxina.",
+       "confirmrecreate-noreason": "{{GENDER:$1|L'usuariu|La usuaria}} [[User:$1|$1]] ([[User talk:$1|alderique]]) desanició esta páxina depués de que principiaras a editala.  Por favor confirma que daveres quies volver a crear esta páxina.",
        "recreate": "Volver a crear",
        "confirm_purge_button": "Aceutar",
        "confirm-purge-top": "¿Llimpiar la caché d'esta páxina?",
        "watchlisttools-edit": "Ver y editar la llista de siguimientu",
        "watchlisttools-raw": "Editar la llista de siguimientu (ensin formatu)",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|alderique]])",
+       "timezone-local": "Llocal",
        "duplicate-defaultsort": "Avisu: La clave d'ordenación predeterminada \"$2\" anula la clave d'ordenación anterior \"$1\".",
        "duplicate-displaytitle": "<strong>Avisu:</strong> El títulu a amosar \"$2\" anula el títulu anterior \"$1\".",
        "invalid-indicator-name": "<strong>Error:</strong> L'atributu <code>name</code> de los indicadores d'estáu de la páxina nun pue tar baleru.",
        "version-libraries-license": "Llicencia",
        "version-libraries-description": "Descripción",
        "version-libraries-authors": "Autores",
-       "redirect": "Redireición por ficheru, usuariu, páxina o ID de revisión",
+       "redirect": "Redireición por ID del ficheru, usuariu, páxina, revisión o rexistru",
        "redirect-legend": "Redirixir a un ficheru o una páxina",
-       "redirect-summary": "Esta páxina especial redirixe a un ficheru (dando'l so nome), una páxina (dando una ID de revisión o de páxina) o una páxina d'usuariu (dando un númberu d'ID d'usuariu). Usu: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],  [[{{#Special:Redirect}}/revision/328429]], o [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Esta páxina especial redirixe a un ficheru (dando'l so nome), una páxina (dando una ID de revisión o de páxina), una páxina d'usuariu (dando un númberu d'ID d'usuariu) o una entrada del rexistru (dando la ID del rexistru). Usu: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],  [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], o [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Dir",
        "redirect-lookup": "Buscar:",
        "redirect-value": "Valor:",
        "redirect-page": "ID de la páxina",
        "redirect-revision": "Revisión de páxina",
        "redirect-file": "Nome del ficheru",
+       "redirect-logid": "ID del rexistru",
        "redirect-not-exists": "Nun s'alcontró'l valor",
        "fileduplicatesearch": "Buscar archivos duplicaos",
        "fileduplicatesearch-summary": "Busca archivos duplicaos basándose nos sos valores fragmentarios.",
        "tags-deactivate": "desactivar",
        "tags-hitcount": "$1 {{PLURAL:$1|cambiu|cambios}}",
        "tags-manage-no-permission": "Nun tienes permisu p'alministrar etiquetes de cambiu.",
+       "tags-manage-blocked": "Nun puedes xestionar etiquetes de cambiu mentanto teas bloquiáu.",
        "tags-create-heading": "Crear una etiqueta nueva",
        "tags-create-explanation": "De mou predetermináu, les etiquetes nueves que se creen tarán disponibles pa que les usen los usuarios y bots.",
        "tags-create-tag-name": "Nome de la etiqueta:",
        "tags-deactivate-not-allowed": "Nun ye posible desactivar la etiqueta «$1».",
        "tags-deactivate-submit": "Desactivar",
        "tags-apply-no-permission": "Nun tienes permisu p'aplicar etiquetes de cambios xunto colos cambios.",
+       "tags-apply-blocked": "Nun puedes aplicar etiquetes de cambiu xunto colos cambios mentanto teas bloquiáu.",
        "tags-apply-not-allowed-one": "Nun se permite aplicar manualmente la etiqueta «$1».",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|La siguiente etiqueta nun pue|Les siguientes etiquetes nun puen}} aplicase manualmente: $1",
        "tags-update-no-permission": "Nun tienes permisu p'amestar o desaniciar etiquetes de cambiu nes revisiones individuales o entraes del rexistru.",
+       "tags-update-blocked": "Nun puedes amestar o desaniciar etiquetes de cambiu mentanto teas bloquiáu.",
        "tags-update-add-not-allowed-one": "Nun se permite amestar manualmente la etiqueta «$1».",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|La siguiente etiqueta nun pue|Les siguientes etiquetes nun puen}} amestase manualmente: $1",
        "tags-update-remove-not-allowed-one": "Nun se permite desaniciar la etiqueta «$1».",
        "expand_templates_preview": "Vista previa",
        "expand_templates_preview_fail_html": "<em>Como {{SITENAME}} tien activáu el códigu HTML puru y hebo una perda de datos de la sesión, la vista previa ta tapecida como precaución escontra ataques de JavaScript.</em>\n\n<strong>Si esti ye un intentu llexítimu d'accesu a la vista previa, vuelvi a intentalo.</strong>\nSi inda nun funciona, intenta [[Special:UserLogout|salir]] y volver a entrar na to cuenta.",
        "expand_templates_preview_fail_html_anon": "<em>Como {{SITENAME}} tien activáu el códigu HTML puru y nun aniciasti sesión, la vista previa ta tapecida como precaución escontra ataques de JavaScript.</em>\n\n<strong>Si esti ye un intentu llexítimu d'accesu a la vista previa, intenta [[Special:UserLogin|entrar]] y vuelvi a intentalo.</strong>",
+       "expand_templates_input_missing": "Fai falta dar daqué de testu d'entrada.",
        "pagelanguage": "Selector de llingua de la páxina",
        "pagelang-name": "Páxina",
        "pagelang-language": "Llingua",
        "pagelang-use-default": "Usar la llingua predeterminada",
        "pagelang-select-lang": "Escoyer llingua",
+       "pagelang-submit": "Unviar",
        "right-pagelang": "Cambiar la llingua de la páxina",
        "action-pagelang": "cambiar la llingua de la páxina",
        "log-name-pagelang": "Rexistru de cambios de llingua",
        "mediastatistics": "Estadístiques de multimedia",
        "mediastatistics-summary": "Estadístiques sobro los tipos de ficheros xubíos. Esto sólo incluye la versión más nueva d'un ficheru. Escluyense les versiones antigües o desaniciaes de los ficheros.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Tamañu total del ficheru pa esta sección: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Tamañu total del ficheru pa tolos ficheros: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "Tipu MIME",
        "mediastatistics-table-extensions": "Estensiones posibles",
        "mediastatistics-table-count": "Númberu de ficheros",
        "mediastatistics-header-text": "Testual",
        "mediastatistics-header-executable": "Executables",
        "mediastatistics-header-archive": "Formatos comprimíos",
+       "mediastatistics-header-total": "Tolos ficheros",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|coma al final desanicióse|comes al final desaniciáronse}} de JSON",
        "json-error-unknown": "Hebo un problema col JSON. Error: $1",
        "json-error-depth": "Pasóse de la fondura máxima de la pila",
        "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."
+       "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",
+       "sessionprovider-nocookies": "Les cookies puen tar desactivaes. Asegúrate de tener activaes les cookies y vuelve a principiar.",
+       "randomrootpage": "Páxina raíz al debalu"
 }
index 9cbb3ef..a879fbc 100644 (file)
@@ -46,7 +46,7 @@
        "tog-watchmoves": "Adlarını dəyişdiyim səhifələri və faylları izlədiyim səhifələrə əlavə et",
        "tog-watchdeletion": "Sildiyim səhifələri və faylları izlədiyim səhifələrə əlavə et",
        "tog-minordefault": "Standart olaraq bütün redaktələri kiçik redaktə kimi nişanla",
-       "tog-previewontop": "Sınaq göstərişi yazma sahəsindən əvvəl göstər",
+       "tog-previewontop": "Sınaq göstərişini redaktə pəncərəsindən əvvəl göstər",
        "tog-previewonfirst": "İlkin redaktədə sınaq göstərişi",
        "tog-enotifwatchlistpages": "İzləmə siyahısında olan məqalə redaktə olunsa, mənə e-məktub göndər",
        "tog-enotifusertalkpages": "Müzakirə səhifəm redaktə olunsa, mənə e-məktub göndər",
        "confirmable-no": "Xeyr",
        "thisisdeleted": "$1 bax və ya bərpa et?",
        "viewdeleted": "$1 göstərilsin?",
-       "restorelink": "{{PLURAL:$1|bir silinmiş redaktəyə|$1 silinmiş redaktəyə}}",
+       "restorelink": "$1 silinmiş redaktəyə",
        "feedlinks": "Kanal növü:",
        "feed-invalid": "Yanlış qeydiyyat kanalı növü.",
        "feed-unavailable": "Sindikasiya xətləri etibarsızdır",
        "laggedslavemode": "'''Xəbərdarlıq:''' Səhifə son əlavələri əks etdirməyə bilər.",
        "readonly": "Verilənlər bazası bloklanıb",
        "enterlockreason": "Bloklamanın səbəbini və nəzərdə tutulan müddətini qeyd edin",
-       "readonlytext": "Verilənlər bazası ehtimal ki, adi təmir işləri ilə əlaqədar müvəqqəti olaraq yeni məqalələr və dəyişikliklər üçün bağlanmışdır.\nVerilənlər bazasını bloklayan operatorun izahatı: $1",
+       "readonlytext": "Verilənlər bazası ehtimal ki, planlaşdırılmış təmir işləri ilə əlaqədar müvəqqəti olaraq yeni məqalələr və dəyişikliklər üçün bağlanmışdır və təmirdən sonra açılacaqdır.\nVerilənlər bazasını bloklayan idarəçi bunu belə izah edib: $1",
        "missing-article": "Məlumat bazası, tapılması istənən \"$1\" $2 adlı səhifəyə aid mətni tapa bilmədi.\n\nBu vəziyyət səhifənin, silinmiş bir səhifənin keçmiş versiyası olmasından qaynaqlana bilər.\n\nƏgər niyə bu deyilsə, proqramda bir səhv ilə qarşılaşmış ola bilərsiniz.\nXahiş edirik bunu bir [[Special:ListUsers/sysop|İdarəçilərə]], URL not edərək göndərin.",
        "missingarticle-rev": "(versiya №: $1)",
        "missingarticle-diff": "(fərq: $1, $2)",
        "createacct-reason": "Səbəb",
        "createacct-reason-ph": "Niyə başqa bir hesab yaradırsınız",
        "createacct-submit": "İstifadəçi hesabı yarat",
-       "createacct-another-submit": "Başqa bir istifadəçi hesabı yarat",
+       "createacct-another-submit": "İstifadəçi hesabı yarat",
        "createacct-benefit-heading": "{{SITENAME}} sizin kimi insanlar tərəfindən yaradılır.",
        "createacct-benefit-body1": "$1 {{PLURAL:$1|redaktə}}",
        "createacct-benefit-body2": "{{PLURAL:$1|səhifə|səhifə}}",
        "passwordreset-emailtitle": "{{SITENAME}} hesabın yaradılması",
        "passwordreset-emailelement": "İstifadəçi adı: \n$1\n\nMüvəqqəti parol: \n$2",
        "passwordreset-emailsentemail": "Xəbərdarlıq məktubu e-maillə göndərildi.",
-       "changeemail": "E-məktub ünvanını dəyiş",
+       "changeemail": "E-məktub ünvanını dəyiş və ya sil",
        "changeemail-oldemail": "Hazırkı e-poçt ünvanı:",
        "changeemail-newemail": "Yeni e-poçt ünvanı:",
        "changeemail-none": "(yoxdur)",
        "sig_tip": "İmza və vaxt",
        "hr_tip": "Horizontal cizgi",
        "summary": "Qısa məzmun:",
-       "subject": "Mövzu/başlıq:",
+       "subject": "Mövzu:",
        "minoredit": "Kiçik redaktə",
        "watchthis": "Bu səhifəni izlə",
        "savearticle": "Səhifəni qeyd et",
        "prefs-watchlist-token": "İzləmə siyahısı nişanı:",
        "prefs-misc": "Digər seçimlər",
        "prefs-resetpass": "Parolu dəyiş",
-       "prefs-changeemail": "E-poçtu dəyiş",
+       "prefs-changeemail": "E-poçtu dəyiş və ya sil",
        "prefs-setemail": "E-poçt ünvanının nizamlanması",
        "prefs-email": "E-mailin parametrləri",
        "prefs-rendering": "Görünüş",
        "rows": "Sıralar:",
        "columns": "Sütunlar:",
        "searchresultshead": "Axtar",
-       "stub-threshold": "<a href=\"#\" class=\"stub\">Keçidsiz linki</a> format etmək üçün hüdud (baytlarla):",
-       "stub-threshold-disabled": "Kənarlaşdırılıb",
+       "stub-threshold": "Qaralama məqalələrə keçidlərin tərtibatını təyinetmə diapazonu ($1):",
+       "stub-threshold-sample-link": "nümunə",
+       "stub-threshold-disabled": "Yoxdur",
        "recentchangesdays": "Son dəyişiklərdə göstərilən günlərin miqdarı:",
        "recentchangesdays-max": "Maksimum $1 {{PLURAL:$1|gün|gün}}",
        "recentchangescount": "Son dəyişikliklərdə başlıq sayı:",
        "group-bot": "Botlar",
        "group-sysop": "İdarəçilər",
        "group-bureaucrat": "Bürokratlar",
-       "group-suppress": "Müfəttişlər",
+       "group-suppress": "Gizlədənlər",
        "group-all": "(bütün)",
        "group-user-member": "{{GENDER:$1|istifadəçi}}",
        "group-autoconfirmed-member": "Avtotəsdiqlənmiş istifadəçilər",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|idarəçi}}",
        "group-bureaucrat-member": "{{GENDER:$1|bürokrat}}",
-       "group-suppress-member": "{{GENDER:$1|oversight}}",
+       "group-suppress-member": "{{GENDER:$1|gizlədən}}",
        "grouppage-user": "{{ns:project}}:İstifadəçilər",
        "grouppage-autoconfirmed": "{{ns:project}}:Avtotəsdiqlənmiş istifadəçilər",
        "grouppage-bot": "{{ns:project}}:Botlar",
        "grouppage-sysop": "{{ns:project}}:İdarəçilər",
        "grouppage-bureaucrat": "{{ns:project}}:Bürokratlar",
-       "grouppage-suppress": "{{ns:project}}:Müfəttişlər",
+       "grouppage-suppress": "{{ns:project}}:Gizlədənlər",
        "right-read": "Səhifələrin oxunması",
        "right-edit": "Səhifələrin redaktəsi",
        "right-createpage": "Səhifələr yaratmaq (müzakirə səhifələrindən əlavə səhifələr nəzərdə tutulur)",
        "right-blockemail": "İstifadəçinin e-poçt göndərməsinə qadağa qoy",
        "right-hideuser": "İstifadəçi adına qadağa qoy və adın görünməsinin qarşısını al",
        "right-ipblock-exempt": "IP bloklanmalarını, avtobloklanmalarını və diapazon bloklanmalarını keç",
-       "right-proxyunbannable": "Proksilərin avtomatik bloklanmalarını keç",
        "right-unblockself": "Öz blokunuzun açılması",
        "right-protect": "Mühafizə səviyyəsinin dəyişilməsi və kaskad mühafizə olunan səhifələrin redaktə edilməsi",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" mühafizə səviyyəsinə malik səhifələrin redaktə edilməsi",
        "right-userrights-interwiki": "Digər vikilərdəki istifadəçilərin istifadəçi hüquqlarını dəyişdir",
        "right-siteadmin": "Məlumatlar bazasının bloklanması və blokun götürülməsi",
        "right-sendemail": "Digər istifadəçilərə elektron poçt göndər",
+       "grant-editmywatchlist": "İzləmə siyahınızda redaktə",
        "newuserlogpage": "Yeni istifadəçilərin qeydiyyatı",
        "newuserlogpagetext": "Yeni qeydiyyatdan keçmiş istifadəçilərin siyahısı.",
        "rightslog": "İstifadəçi hüquqları qeydləri",
        "recentchanges-label-minor": "Bu kiçik redaktədir",
        "recentchanges-label-bot": "Bu redaktə bot tərəfindən edilmişdir",
        "recentchanges-label-unpatrolled": "Bu redaktə hələ patrullanmayıb",
-       "recentchanges-label-plusminus": "Səhifənin ölçüsü bayt miqdarı ilə təyin edilir",
+       "recentchanges-label-plusminus": "Səhifənin ölçüsündəki dəyişiklik (baytlarla)",
        "recentchanges-legend-heading": "'''Legenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (həmçinin bax: [[Special:NewPages|yeni səhifələrin siyahısı]])",
        "rcnotefrom": "Aşağıda <strong>$2</strong>-dən bu yana olan dəyişikliklər göstərilib (<strong>$1</strong>-dən çox olmayaraq).",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 izləyən istifadəçi]",
        "rc_categories": "Kateqoriyalara limit qoy (\"|\" ilə ayır)",
-       "rc_categories_any": "Hər",
+       "rc_categories_any": "Seçilənlərdən hər hansı biri",
        "rc-change-size": "$1",
-       "rc-change-size-new": "$1 üçün dəyişiklikdən sonrakı həcm: {{PLURAL:$1|bayt|bayt|bayt}}",
+       "rc-change-size-new": "Dəyişiklikdən sonrakı ölçü: $1 bayt",
        "newsectionsummary": "/* $1 */ yeni bölmə",
        "rc-enhanced-expand": "Ətraflı göstər",
        "rc-enhanced-hide": "Redaktələri gizlət",
        "reuploaddesc": "Return to the upload form.",
        "upload-tryagain": "Dəyşdirilmiş fayl izahını göndər",
        "uploadnologin": "Daxil olmamısınız",
-       "uploadnologintext": "Fayl yükləmək üçün [[Special:UserLogin|daxil olmalısınız]].",
+       "uploadnologintext": "Fayl yükləmək üçün siz sistemə $1.",
        "upload_directory_missing": "($1) yükləmə qaydası axtarılır və vebserverdə yaradılması qeyri-mümkündür.",
        "upload_directory_read_only": "\"$1\" kataloqunun arxivi veb-server yazıları üçün qapalıdır.",
        "uploaderror": "Yükləmə xətası",
        "upload-recreate-warning": "'''Diqqət: Bu adda fayl silinib, yaxud adı dəyişdirilib.'''\n\nBu səhifənin silinmə və addəyişmə jurnalı aşağıda göstərilmişdir:",
        "uploadtext": "Fayl yükləmək üçün aşağıdakı formadan istifadə edin.\nƏvvəllər yüklənmiş fayllara baxmaq üçün [[Special:FileList|yüklənmiş fayllar siyahısına]] keçin, həmçinin (təkrar) yüklənmiş fayllara [[Special:Log/upload|yükləmə jurnalında]], silinmiş fayllara [[Special:Log/delete|silinmə jurnalında]] baxa bilərsiniz.\n\nMəqaləyə fayl yerləşdirmək üçün aşağıdaki formalardan birini istifadə edin:\n* Faylın tam versiyasını yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.jpg]]</nowiki></code>''';\n* Faylın 200 pikselədək kiçildilmiş versiyasını mətndən solda, altında izahla yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:File.png|200px|thumb|left|təsvir]]</nowiki></code>''';\n* Səhifədə faylın özünü göstərmədən ona birbaşa keçid yerləşdirmək üçün: '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''.",
-       "upload-permitted": "İcazə verilən fayl tipləri: $1.",
-       "upload-preferred": "İcazə verilən fayl tipləri: $1.",
-       "upload-prohibited": "İcazə verilməyən fayl tipləri: $1.",
+       "upload-permitted": "İcazə verilən fayl {{PLURAL:$2|tipi|tipləri}}: $1.",
+       "upload-preferred": "Üstünlük verilən fayl {{PLURAL:$2|tipi|tipləri}}: $1.",
+       "upload-prohibited": "Qadağan olunan fayl {{PLURAL:$2|tipi|tipləri}}: $1.",
        "uploadlogpage": "Yükləmə qeydləri",
        "uploadlogpagetext": "Aşağıda ən yeni yükləmə jurnal qeydləri verilmişdir.",
        "filename": "Fayl adı",
        "backend-fail-copy": "\"$1\" faylı \"$2\" faylına kopyalanmır.",
        "backend-fail-read": "\"$1\" faylı oxunmadı.",
        "backend-fail-create": "\"$1\" faylı yazıla bilmədi.",
+       "backend-fail-maxsize": "$1 faylının ölçüsü $2 baytdan çox olduğu üçün yazmaq mümkün olmadı.",
        "uploadstash": "Gizli yükləmə",
        "uploadstash-clear": "Müvəqqəti faylları təmizlə",
        "uploadstash-refresh": "Fayl siyahısını yenilə",
        "nopagetitle": "Belə hədəf səhifəsi yoxdur",
        "pager-newer-n": "{{PLURAL:$1|1 daha yeni|$1 daha yeni}}",
        "pager-older-n": "{{PLURAL:$1|1 daha köhnə|$1 daha köhnə}}",
-       "suppress": "Təftişçi",
+       "suppress": "Gizlət",
        "booksources": "Kitab mənbələri",
        "booksources-search-legend": "Kitab mənbələri axtar",
        "booksources-isbn": "ISBN:",
        "addwatch": "İzləmə siyahısına əlavə et",
        "addedwatchtext": "\"[[:$1]]\" səhifəsi [[Special:Watchlist|izlədiyiniz səhifələr]] siyahısına əlavə edildi. Bu səhifədə və əlaqəli müzakirə səhifəsindəki bütün dəyişikliklər orada göstəriləcək və səhifə asanlıqla seçiləbilmək üçün [[Special:RecentChanges|son dəyişikliklərdə]] qalın şriftlərlə görünəcəkdir. <p> Səhifəni izləmə siyahınızdan çıxarmaq üçün yan lövhədəki \"izləmə\" düyməsinə vurun.",
        "removewatch": "Bu səhifəni İzlədiyim səhifələr siyahısından çıxar",
-       "removedwatchtext": "\"[[:$1]]\" səhifəsi [[Special:Watchlist|izləmə siyahınızdan]] çıxarıldı.",
+       "removedwatchtext": "\"[[:$1]]\" səhifəsi sizin [[Special:Watchlist|izləmə siyahınızdan]] çıxarıldı.",
        "watch": "İzlə",
        "watchthispage": "Bu səhifəni izlə",
        "unwatch": "İzləmə",
        "notanarticle": "Səhifə boşdur",
        "notvisiblerev": "Başqa istifadıçinin son dəyişikliyi silinib",
        "watchlist-details": "İzləmə siyahınızda, müzakirə səhifələrini çıxmaq şərtilə, {{PLURAL:$1|$1 səhifə|$1 səhifə}} var.",
-       "wlheader-enotif": " E-məktubla bildiriş aktivdir.",
+       "wlheader-enotif": "E-məktubla bildiriş aktivdir.",
        "wlheader-showupdated": "Son ziyarətinizdən sonra edilən dəyişikliklər '''qalın şriftlərlə''' göstərilmişdir.",
        "wlnote": "Aşağıdakı {{PLURAL:$1|'''$1''' dəyişiklik|'''$1''' dəyişiklik}} son {{PLURAL:$2|saatda|'''$2''' saatda}} edilmişdir.",
-       "wlshowlast": "Bunları göstər: son $1 saatı $2 günü",
+       "wlshowlast": "Son $1 saatı $2 günü göstər",
        "watchlistall2": "hamısı",
        "wlshowhidemine": "mənimn redaktələrim",
        "watchlist-options": "İzləmə siyahısının nizamlamaları",
        "deletecomment": "Səbəb:",
        "deleteotherreason": "Digər/əlavə səbəb:",
        "deletereasonotherlist": "Digər səbəb",
-       "deletereason-dropdown": "*Əsas silmə səbəbi\n** Müəllif istəyi\n** Müəllif hüququ pozuntusu\n** Vandalizm",
+       "deletereason-dropdown": "*Əsas silmə səbəbləri\n** Spam\n** Vandalizm\n** Müəllif hüququ pozuntusu\n** Müəllif istəyi\n** Səhv yönləndirmə",
        "delete-edit-reasonlist": "Silmə səbəblərinin redaktəsi",
        "delete-toobig": "Bu səhifə $1-dən artıq redaktə ilə çox böyük redaktə tarixçəsinə malikdir.\n\"{{SITENAME}}\" saytının fəaliyyətində problemlər yaratmamaq üçün bu cür səhifələrin silinməsi qadağandır.",
        "rollback": "əvvəlki halına qaytar",
        "protect-locked-access": "Sizin hesabınızın mühafizə səviyyəsini dəyişməyə ixtiyarı yoxdur.\n'''$1''' səhifəsində hal-hazırda edə biləcəyiniz əməliyyatlar bunlardır:",
        "protect-cascadeon": "Bu səhifə mühafizəlidir, çünki bu səhifə {{PLURAL:$1|başqa bir|başqa bir}} səhifədən kaskad mühafizə edilmişdir. Siz bu səhifənin mühafizə səviyyəsini dəyişdirə bilərsiniz, bu kaskad mühafizəyə təsir etməyəcək.",
        "protect-default": "Bütün istifadəçilərə icazə ver",
-       "protect-fallback": "\"$1\" icazəsi tələb olunur",
-       "protect-level-autoconfirmed": "Yeni və anonim istifadəçiləri blokla",
+       "protect-fallback": "Yalnız \"$1\" hüquqları olan istifadəçilərə icazə verilir",
+       "protect-level-autoconfirmed": "Yalnız avtotəsdiqlənmiş istifadəçilərə icazə verilir",
        "protect-level-sysop": "Yalnız idarəçilərə icazə verilir",
        "protect-summary-cascade": "kaskad mühafizə",
        "protect-expiring": "$1 (UTC)- tarixində vaxtı bitir",
        "restriction-level": "Məhdudiyyət dərəcəsi:",
        "minimum-size": "Minimum həcm",
        "maximum-size": "Maksimum həcm",
-       "pagesize": "(baytlar)",
+       "pagesize": "(bayt)",
        "restriction-edit": "Redaktə",
        "restriction-move": "Adını dəyiş",
        "restriction-create": "Yarat",
        "djvu_no_xml": "DjVu üçün XML faylı almaq mümkün deyil.",
        "thumbnail_image-missing": "Belə görünür ki, $1 faylı yoxdur",
        "import": "Səhifələri idxal et",
-       "importinterwiki": "Vikilərarası çıxarma",
+       "importinterwiki": "Başqa vikidən idxal et",
        "import-interwiki-history": "Səhifənin dəyişmə tarixçələrinin hamısını köçür",
        "import-interwiki-templates": "Bütün şablonlarla birlikdə",
        "import-interwiki-submit": "İdxal",
        "importlogpagetext": "Səhifələrin idarəçilər tərəfindən digər vikilərdən dəyişiklik tarixçəsi ilə birlikdə köçürülməsi",
        "import-logentry-upload-detail": "$1 {{PLURAL:$1|dəyişiklik|dəyişiklik}} idxal edildi.",
        "import-logentry-interwiki-detail": "$2 vikidən $1 {{PLURAL:$1|dəyişiklik|dəyişiklik}} idxal edildi",
-       "tooltip-pt-userpage": "İstifadəçi səhifəniz",
+       "tooltip-pt-userpage": "{{GENDER:|İstifadəçi}} səhifəniz",
        "tooltip-pt-anonuserpage": "The user page for the ip you",
-       "tooltip-pt-mytalk": "Danışıq səhifəm",
+       "tooltip-pt-mytalk": "{{GENDER:|Sizin}} müzakirə səhifəniz",
        "tooltip-pt-anontalk": "Bu IP ünvanından redaktə olunmuş danışıqlar",
-       "tooltip-pt-preferences": "Mənim nizamlamalarım",
+       "tooltip-pt-preferences": "{{GENDER:|Sizin}} nizamlamalarınız",
        "tooltip-pt-watchlist": "İzləməyə götürdüyüm səhifələr",
-       "tooltip-pt-mycontris": "Etdiyim dəyişikliklərin siyahısı",
+       "tooltip-pt-mycontris": "{{GENDER:|Sizin}} töhfələrinizin siyahısı",
        "tooltip-pt-login": "Daxil olmanız tövsiyə olunur, amma bu məcburi tələb deyil.",
        "tooltip-pt-logout": "Sistemdən çıx",
        "tooltip-ca-talk": "Məqalə haqqındə müzakirə edib, münasibətivi bildir",
        "tooltip-feed-rss": "Bu səhifə üçün RSS yayımı",
        "tooltip-feed-atom": "Bu səhifə üçün Atom yayımı",
        "tooltip-t-contributions": "Bu istifadəçinin redaktə etdiyi səhifələrin siyahısı",
-       "tooltip-t-emailuser": "Bu istifadəçiyə e-məktub yolla",
+       "tooltip-t-emailuser": "{{GENDER:$1|Bu istifadəçiyə}} e-məktub göndər",
        "tooltip-t-upload": "Yeni şəkil və ya multimedia faylı yüklə",
        "tooltip-t-specialpages": "Xüsusi səhifələrin siyahısı",
        "tooltip-t-print": "Səhifənin çap versiyası",
        "tooltip-ca-nstab-main": "Məqalənin məzmununu göstər",
        "tooltip-ca-nstab-user": "İstifadəçi səhifəsinə bax",
        "tooltip-ca-nstab-media": "Media-fayl",
-       "tooltip-ca-nstab-special": "Bu xüsusi səhifə olduğu üçün redaktə edilə bilməz",
+       "tooltip-ca-nstab-special": "Bu xidməti səhifədir və redaktə edilə bilməz",
        "tooltip-ca-nstab-project": "Layihə səhifəsinə bax",
        "tooltip-ca-nstab-image": "Faylın səhifəsinə bax",
        "tooltip-ca-nstab-mediawiki": "Sistem məlumatına bax",
        "creditspage": "Səhifə kreditleri",
        "spamprotectiontitle": "Spam qoruma süzgəci",
        "spambot_username": "MediaViki spam təmizləməsi",
-       "simpleantispam-label": "Anti-spam yoxlanılması.\nBuranı <strong>DOLDURMAYIN</strong>!",
+       "simpleantispam-label": "Anti-spam yoxlaması.\nBuranı <strong>doldurmayın</strong>!",
        "pageinfo-title": "\"$1\" üçün məlumat",
        "pageinfo-header-basic": "Əsas məlumatlar",
        "pageinfo-header-edits": "Redaktə tarixçəsi",
        "exif-xresolution": "Üfiqi xətt",
        "exif-yresolution": "Şaquli xətt",
        "exif-rowsperstrip": "Hər blokdakı sətirlərin sayı",
-       "exif-jpeginterchangeformatlength": "JPEG məlumat bazasının baytları",
+       "exif-jpeginterchangeformatlength": "JPEG məlumatın ölçüsü",
        "exif-datetime": "Faylın dəyişməsi tarixi və vaxtı",
        "exif-imagedescription": "Şəkil başlığı",
        "exif-make": "Kamera istehsalçısı",
        "autosumm-replace": "Səhifənin məzmunu '$1' yazısı ilə dəyişdirildi",
        "autoredircomment": "[[$1]] səhifəsinə istiqamətləndirilir",
        "autosumm-new": "Səhifəni '$1' ilə yarat",
+       "size-bytes": "$1 bayt",
        "watchlistedit-normal-title": "İzlədiyim səhifələri redaktə et",
        "watchlistedit-normal-legend": "İzləmə siyahısından başlıqların silinməsi",
        "watchlistedit-normal-submit": "Başlığın silinməsi",
        "duration-millennia": "$1 {{PLURAL:$1|minillik|minillik}}",
        "limitreport-cputime": "CPU vaxt istifadəsi",
        "limitreport-walltime": "Real vaxt istifadəsi",
+       "limitreport-postexpandincludesize-value": "$1/$2 bayt",
        "expand_templates_output": "Nəticə",
        "expand_templates_ok": "OK",
        "pagelang-name": "Səhifə",
        "pagelang-language": "Dil",
+       "mediastatistics-nbytes": "$1 bayt ($2; $3%)",
        "mediastatistics-header-unknown": "Naməlum",
        "mediastatistics-header-audio": "Audio",
        "mediastatistics-header-video": "Videolar",
index a1df9e9..1cd8d58 100644 (file)
        "protectthispage": "بۇ صحیفه‌‌نی قوْرو",
        "unprotect": "قوْروماغی دَییشدیر",
        "unprotectthispage": "بۇ صحیفه‌نین قوْروماسینی دَییشدیر",
-       "newpage": "يئنی صحیفه‌‌",
+       "newpage": "يئنی صفحه‌‌",
        "talkpage": "بۇ صحیفه‌نی دانیش",
        "talkpagelinktext": "دانیشیق",
        "specialpage": "اؤزل صفحه",
        "articlepage": "ایچری‌لی صحیفه‌یه باخ",
        "talk": "دانیشیق",
        "views": "گؤرونوشلر",
-       "toolbox": "آراجلار",
+       "toolbox": "آلتلر",
        "userpage": "ایشلدن صفحه‌‌سینه باخ",
        "projectpage": "پروژه صحیفه‌سینه باخ",
        "imagepage": "فایل صحیفه‌سینه باخ",
        "aboutpage": "Project:گؤره",
        "copyright": "ایچینده‌کیلر $1 لیسانسی احاطه‌سینده‌دیلر.",
        "copyrightpage": "{{ns:project}}:کوْپی حاقلاری",
-       "currentevents": "اÛ\8cÙ\86دÛ\8cÚ©Û\8c Ø§Ù\88Ù\84اÛ\8cÙ\84ار",
+       "currentevents": "اÛ\8cÙ\86دÛ\8cÚ©Û\8c Ø­Ø§Ø¯Û\8cØ«Ù\87â\80\8cÙ\84ر",
        "currentevents-url": "Project:ایندیکی اولایلار",
        "disclaimers": "یالانلامالار",
        "disclaimerpage": "Project:گنل یالانلاما",
        "mainpage": "آنا صفحه",
        "mainpage-description": "آنا صفحه",
        "policy-url": "Project:قایدالار",
-       "portal": "ایشلدنلر پوْرتالی",
-       "portal-url": "Project:ایشلدنلر پوْرتالی",
+       "portal": "کند مئیدانی",
+       "portal-url": "Project:کند مئیدانی/دانیشیق",
        "privacy": "گیزلیلیک سیاستی",
        "privacypage": "Project:گیزلیلیک سیاستی",
        "badaccess": "ایجازه خطاسی",
        "password-change-forbidden": "بو ویکی‌ده رمزلری دَییشه بیلنمه‌سینیز.",
        "externaldberror": "بیر دیتابیس دوغرولاما خطاسی اولدو، یوخسا سیزین ائشیک حسابینیزی گونجل‌لدمگه ایجازه‌نیز یوخدور.",
        "login": "گیریش",
-       "nav-login-createaccount": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cاراد",
-       "userlogin": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cاراد",
+       "nav-login-createaccount": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cارات",
+       "userlogin": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cارات",
        "userloginnocreate": "گیریش",
        "logout": "چیخیش",
        "userlogout": "چیخیش",
        "templatesusedsection": "{{PLURAL:$1|شابلون}} بو بؤلمه‌ده ایشلنیب‌دیر:",
        "template-protected": "(قورونوب)",
        "template-semiprotected": "(یاریم‌قورونموش)",
-       "hiddencategories": "بو صحیفه {{PLURAL:$1|بیر گیزلی دسته‌یه|$1 گیزلی دسته‌لره}} عایددیر:",
+       "hiddencategories": "بۇ صفحه {{PLURAL:$1|بیر گیزلی بؤلمه‌یه|$1 گیزلی بؤلمه‌لره}} مربوط‌دور:",
        "nocreatetext": "{{SITENAME}} یئنی صحیفه یارادماق ایمکانی‌نی محدودلاشدیریب‌دیر.\nسیز دالی دؤنوب و اؤنجه‌دن اولان بیر صحیفه‌نی دَییشدیره بیلرسینیز، یا دا [[Special:UserLogin|گیریب یوخسا یئنی حساب آچین]].",
        "nocreate-loggedin": "سیزین یئنی صحیفه‌لر یاراتماغا ایجازه‌نیز یوخدور.",
        "sectioneditnotsupported-title": "بؤلوم دییشدیرمه‌سی دستک‌لنمیر",
        "edit-gone-missing": "صحیفنی یئنی لمک مومکون دئییل.\nچوخ گومان کی، صحیفه سیلینمیش‌دیر.",
        "edit-conflict": "سیزله برابر دییشدیرمه",
        "edit-no-change": "سیزین دییشدیر قئیده آلینمامیش‌دیر. بئله کی، متنده هئچ بیر دییشدیر ائدیلممیش‌دیر.",
-       "postedit-confirmation-created": "بÙ\87 ØµÙ\81Ø­Ù\87 Û\8cاراÙ\86Û\8cبâ\80\8cدÛ\8cر.",
+       "postedit-confirmation-created": "بÛ\87 ØµÙ\81Ø­Ù\87 Û\8cاراÙ\86Û\8cبâ\80\8cدÛ\8cر.",
        "postedit-confirmation-restored": "صفحه گئری یوکلندی.",
        "postedit-confirmation-saved": "سیزین دَییشدیرمه‌نیز قئید اولونوب‌دور.",
        "edit-already-exists": "یئنی صحیفنی یاراتماق مومکون دئییل.\nبئله کی، بو آددا صحیفه آرتیق مؤوجوددور.",
        "undo-failure": "دییشیک‌لیک‌لرین توققوشماسی نتیجه‌سینده گئرییه قایتارما ایشی اوغورسوز اولدو.",
        "undo-norev": "دوزلیش‌لر گئری قایتاریلا بیلینمیر، چونکی اونلار یا مؤوجود دئییل، یا دا سیلینیب.",
        "undo-nochange": "نظره گلیر دَییشدیرمه قاباغجادان قایتاریلیب.",
-       "undo-summary": "$1 دییشیک‌لیک [[Special:Contributions/$2|$2]] ([[User talk:$2|دانیشیق]]) طرفین‌دن گئری آلیندی​​.",
+       "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": "'''$1''' آی‌پی آدرس آرالیغیندان حساب یارانماق، [[User:$3|$3]] ایشلدنی طرفیندن یاساقلانیب‌دیر. سیزین‌ده آی‌پی آدرسیز ('''$4''') بو آرادادیر.\n\n$3 طرفین‌دن وئریلن سبب بودور: «$2»",
-       "viewpagelogs": "بو صحیفه‌نین قئیدلرینه باخ",
+       "viewpagelogs": "بۇ صفحه‌نین قئیدلرینه باخ",
        "nohistory": "بو صحیفه اوچون دَییشدیرمه گئچمیشی یوخدور.",
        "currentrev": "سون نوسخه",
        "currentrev-asof": "$1 تاریخینه کیمی سون حال",
        "currentrevisionlink": "سون نوسخه",
        "cur": "ایندی",
        "next": "سونراکی",
-       "last": "اؤنجه",
+       "last": "قاباقکی",
        "page_first": "ایلک",
        "page_last": "سون",
-       "histlegend": "فرقلری سئچمه: موقاییسه ائتمک ایسته‌دیگینیز دییشیک‌لیکلرین یانینداکی گیرده دویمه‌لره علامت قویون و سونرا Enter-ی ووروب یوخسا آشاغیداکی اویمه‌نی وورون.<br />\nآچیقلاما:'''({{int:cur}})''' =سون نوسخه ایله فرقلر ، '''({{int:last}})''' = اؤنجه‌کی نوسخه ایله فرقلر، '''{{int:minoreditletter}}''' = کیچیک دییشیک‌لیک.",
+       "histlegend": "فرقلری سئچمه: موقاییسه ائتمک ایسته‌دیگینیز دییشیک‌لیکلرین یانینداکی گیرده دۆیمه‌لره علامت قویون و سوْنرا Enter-ی وۇروب یوْخسا آشاغیداکی دۆیمه‌نی وورون.<br />\nآچیقلاما:'''({{int:cur}})''' =سون نوسخه ایله فرقلر ، '''({{int:last}})''' = قاباقکی نوسخه ایله فرقلر، '''{{int:minoreditletter}}''' = کیچیک دییشیک‌لیک.",
        "history-fieldset-title": "گئچمیشی آختار",
        "history-show-deleted": "یالنیز سیلینَنلر",
        "histfirst": "ان اسکی",
        "shown-title": "هر صفحه‌ده {{PLURAL:$1|بیر|$1}} نتیجه گؤرست",
        "viewprevnext": "گؤستر ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''بو ویکی‌ده «[[:$1]]» آدلی صحیفه واردیر.'''",
-       "searchmenu-new": "<strong>بو ویکی‌ده «[[:$1]]» صحیفه‌‌سینی يارات!</strong> {{PLURAL:$2|0=|هابئله تاپیلمیش صفحه نی آختاریشینیزلا گورون.|هابئله تاپیلمیش آختاریشین نتیجه سین گورون.}}",
+       "searchmenu-new": "<strong>بۇ ویکی‌ده «[[:$1]]» صفحه‌‌سینی يارات!</strong> {{PLURAL:$2|0=|هابئله تاپیلمیش صفحه‌نی آختاریشینیزلا گؤرون.|هابئله تاپیلمیش آختاریشین نتیجه‌سین گؤرون.}}",
        "searchprofile-articles": "مقاله‌لر",
        "searchprofile-images": "مولتی‌مئدیا",
        "searchprofile-everything": "هرشئی",
        "right-blockemail": "بیر ایستیفاده‌چینین ایمیل گؤندرمگینی باغلا",
        "right-hideuser": "بیر ایستیفاده‌چی آدینی باغلا و اونو عموم‌دان گیزلد",
        "right-ipblock-exempt": "آی‌پی باغلاماقلاری، اوتوماتیک باغلاماقلاری و سیرا باغلاماقلاریندان گئچ",
-       "right-proxyunbannable": "پروکسیلرین اوتوماتیک باغلاماقلاریندان گئچ",
        "right-unblockself": "اؤزلرینی آچسینلار",
        "right-protect": "قوروماق سطحینی و شلاله‌لی قورونموش صحیفه‌لری دَییشدیر",
        "right-editprotected": "«{{int:protect-level-sysop}}» جوره قورونموش صحیفه‌لری دَییشدیر",
        "right-override-export-depth": "ایچری باغلانتیلاری اولان صحیفه‌لری، چوخو ۵ درجه درینلیگی ایله، ایخراج ائت",
        "right-sendemail": "باشقا ایستیفاده‌چیلره ایمیل گؤندر",
        "right-passwordreset": "رمز یئنی‌له‌مه ایمیل‌لرینه باخ",
-       "newuserlogpage": "اÛ\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c یارادیلما قئیدی",
-       "newuserlogpagetext": "بÙ\88 Ø§Û\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c Û\8cارادÙ\85اÙ\82 Ø³Û\8cاÙ\87Û\8cسÛ\8cدÛ\8cر.",
+       "newuserlogpage": "اÛ\8cØ´Ù\84دÙ\86 یارادیلما قئیدی",
+       "newuserlogpagetext": "بÛ\87 Ø§Û\8cØ´Ù\84دÙ\86 Û\8cارادÙ\85اÙ\82 Ù\84Û\8cستÛ\8câ\80\8cدÛ\8cر.",
        "rightslog": "ایستیفاده‌چی حاقلاری سیاهی‌سی",
        "rightslogtext": "بو، ایستیفاده‌چی حاقلارینا اولان دَییشیکلیکلر سیاهی‌سی‌دیر.",
        "action-read": "بو صحیفه‌نی اوخو",
        "rc-enhanced-expand": "تفصیل‌لری گؤستر",
        "rc-enhanced-hide": "تفصیل‌لری گیزلت",
        "rc-old-title": "ایلک‌جه «$1» آدی‌له یارانمیشدیر",
-       "recentchangeslinked": "اÛ\8cÙ\84Ú¯Û\8cلی دَییشیکلیکلر",
-       "recentchangeslinked-feed": "اÛ\8cÙ\84Ú¯Û\8cلی دَییشیکلیکلر",
-       "recentchangeslinked-toolbox": "اÛ\8cÙ\84Ú¯Û\8cلی دَییشیکلیکلر",
+       "recentchangeslinked": "باغلی دَییشیکلیکلر",
+       "recentchangeslinked-feed": "باغلی دَییشیکلیکلر",
+       "recentchangeslinked-toolbox": "باغلی دَییشیکلیکلر",
        "recentchangeslinked-title": "''$1'' ایله ایلگی‌لی دییشیکلر",
        "recentchangeslinked-summary": "آشاغیداکی سیياهی، قئيد اوْلونان صحیفه‌‌يه (و يا قئيد اوْلونان کاتئقوْرياداکی صحیفه‌‌لره) داخیلی کئچید وئرن صحیفه‌‌لرده ائدیلمیش سوْن ديَیشیکلیکلرین سیياهیسیدیر. \n[[Special:Watchlist|ایزله‌مه سیياهینیزداکی]] صحیفه‌‌لر '''قالین''' شریفتله گؤستریلمیشدیر.",
        "recentchangeslinked-page": "صفحه آدی:",
        "randomredirect": "راست‌گله یول‌لاندیرما",
        "randomredirect-nopages": "«$1» آدفضاسیندا هئچ بیر یول‌لاندیرما یوخدور.",
        "statistics": "آمارلار",
-       "statistics-header-pages": "صحیفه آمارلاری",
+       "statistics-header-pages": "صفحه آمارلاری",
        "statistics-header-edits": "دَییشمه آمارلاری",
        "statistics-header-users": "ایشلدن‌لر آمارلاری",
        "statistics-header-hooks": "باشقا آمارلار",
        "statistics-articles": "مقاله‌لر",
-       "statistics-pages": "صحیفه‌لر:",
-       "statistics-pages-desc": "بو ویکی‌ده بوتون صحیفه‌لر، او جومله‌دن دانیشیق صحیفه‌لری، یول‌لاندیرمالار و سونرا.",
+       "statistics-pages": "صفحه‌لر:",
+       "statistics-pages-desc": "بۇ ویکی‌ده بۆتون صفحه‌لر، او جومله‌دن دانیشیق صفحه‌لری، یوْل‌لاندیرمالار و غیره.",
        "statistics-files": "یوکلنمیش فایل‌لار",
-       "statistics-edits": "{{SITENAME}} Û\8cÙ\88Ù\84ا Ø¯Ù\88Ø´Ù\86دÙ\86 Ø¨Ù\8eرÛ\8c ØµØ­Û\8cÙ\81ه دَییشیکلیکلری",
-       "statistics-edits-average": "هر صحیفه‌ده اورتا دَییشیکلیک سایی",
+       "statistics-edits": "{{SITENAME}} Û\8cÙ\88Ù\92Ù\84ا Ø¯Û\86Ø´Ù\86دÙ\86 Ø¨Û\87 Û\8cاÙ\86ا ØµÙ\81Ø­ه دَییشیکلیکلری",
+       "statistics-edits-average": "هر صفحه‌ده اوْرتا دَییشیکلیک سایی",
        "statistics-users": "یازیلمیش [[Special:ListUsers|ایستیفاده‌چیلر]]",
        "statistics-users-active": "چالیشقان ایستیفاده‌چیلر",
        "statistics-users-active-desc": "سون {{PLURAL:$1|بیر|$1}} گون‌ده بیر ایش گؤرن ایستیفاده‌چیلر",
        "booksources-search": "آختار",
        "booksources-text": "آشاغیدا یئنی و ایشلنمیش کیتاب‌لار ساتان خاریجی کئچیدلرده سیز آختاردیغینیز کیتاب حاقیندا علاوه معلومات آلا بیلرسیز:",
        "booksources-invalid-isbn": "وئریلن ISBN اعتبارسیز کیمی گؤرونور؛ اورجینال قایناق‌دان کوپیالاما اشکالار اوچون نظارت ائدین.",
-       "specialloguserlabel": "ایجراچی",
-       "speciallogtitlelabel": "مقصد (باش‌لیق و یا ایستیفاده‌چی):",
+       "specialloguserlabel": "ائدن:",
+       "speciallogtitlelabel": "مقصد(باشلیق یوْخسا {{ns:user}}:ایشلدن اۆچون ایشلدن آدی):",
        "log": "قئیدلر",
        "all-logs-page": "بوتون اجتماعی قئيدلر",
        "alllogstext": "{{SITENAME}} اوچون بوتون مؤوجود قئیدلرین بیرگه گؤستریشی.\nقئید نؤوو، ایستیفاده‌چی آدی و یا تأثیر ائدیلمیش صحیفنی سئچمکله داها ایسپئسیفیک اولا بیلرسینیز.",
        "linksearch-text": "«*.wikipedia.org» کیمی نیشانلاری ایشلده بیلرسینیز.\nآزی بیر دنه «*.org» کیمی یوخاری دامنه لازیم‌دیر.<br />\nدستلکلنمیش {{PLURAL:$2|پروتوکول|پروتوکول‌لار}}: $1 (بوش بیراخیلسا، http:// نظرده آلیناجاقدیر).",
        "linksearch-line": "$1، $2-دن باغلانیب‌دیر",
        "linksearch-error": "نیشانلار یالنیز آدی اولینده گله بیلرلر.",
-       "listusersfrom": "بÙ\88Ù\86داÙ\86 Ø¨Ø§Ø´Ù\84اÛ\8cاراÙ\82 Ø§Û\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8cÙ\84رÛ\8c Ú¯Ø¤Ø³ØªØ±:",
+       "listusersfrom": "بÛ\87Ù\86داÙ\86 Ø¨Ø§Ø´Ù\84اÛ\8cاÙ\86 Ø§Û\8cØ´Ù\84دÙ\86â\80\8cÙ\84رÛ\8c Ú¯Ø¤Ø³ØªØ±:",
        "listusers-submit": "گؤستر",
        "listusers-noresult": "هئچ ایستیفاده‌چی تاپیلمادی.",
        "listusers-blocked": "(باغلانیب)",
-       "activeusers": "چالیشان ایستیفاده‌چیلرین لیستی",
-       "activeusers-intro": "بوردا سون {{PLURAL:$1|بیر|$1}} گون‌ده بیر ایشلر گؤرن ایستیفاده‌چیلرین لیستی گؤستریلیر.",
+       "activeusers": "چالیشقان ایشلدنلرین لیستی",
+       "activeusers-intro": "بۇرادا سوْن {{PLURAL:$1|بیر|$1}} گۆن‌ده دَییشدیرمه ائدن ایشلدن‌لرین لیستی گؤروشور.",
        "activeusers-count": "سون {{PLURAL:$3|گون|$3 گون}}‌ده، {{PLURAL:$1|$1}} چالیشما",
-       "activeusers-from": "بÙ\88Ù\86داÙ\86 Ø¨Ø§Ø´Ù\84اÛ\8cاراÙ\82 Ø§Û\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8cÙ\84رÛ\8c Ú¯Ø¤Ø³ØªØ±:",
+       "activeusers-from": "بÛ\87Ù\86داÙ\86 Ø¨Ø§Ø´Ù\84اÛ\8cاÙ\86 Ø§Û\8cØ´Ù\84دÙ\86â\80\8cÙ\84رÛ\8c Ú¯Ø¤Ø³ØªØ±:",
        "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>",
        "logentry-contentmodel-change-revert": "قایتار",
        "protectlogpage": "قوروما قئیدلری",
        "protectlogtext": "آشاغی‌داکی، صحیفه قوروما‌لارینا دییشیک‌لیک‌لرین بیر سیاهی‌سی‌دیر.\nحال-حاضردا تطبیق اولونان صحیفه قوروما‌لاری اوچون [[Special:ProtectedPages| قوروما آلتینا آلینمیش صحیفه‌لر سیاهی‌سینا]] باخا بیلرسینیز.",
-       "protectedarticle": "«[[$1]]» قوروندو",
+       "protectedarticle": "«[[$1]]» قوْروندو",
        "modifiedarticleprotection": "\"[[$1]]\" صحیفه‌سی اوچون محافظه سویه‌سی دییشیلدی",
        "unprotectedarticle": "محافظه کنارلاشدیریلدی \"[[$1]]\"",
        "movedarticleprotection": "قوروما نیزام‌لاری \"[[$2]]\" صحیفه‌سین‌دن \"[[$1]]\" صحیفه‌سی داشیندی",
        "protect-cascadeon": "بو صحیفه محافظه‌لی‌دیر، چونکی بو صفحه {{PLURAL:$1|باشقا بیر}} صفحه‌دن کاسکاد محافظه ائدیلمیش‌دیر. سیز بو صفحه‌نین محافظه سویه‌سینی دییشدیره بیلرسینیز، بو کاسکاد محافظه‌یه تأثیر ائتمه‌یه‌جک.",
        "protect-default": "بوتون ایستیفاده‌چی‌لره ایجازه وئر",
        "protect-fallback": "یالنیز «$1» ایجازه‌سی اولان ایستیفاده‌چیلره ایجازه وئر",
-       "protect-level-autoconfirmed": "Û\8cاÙ\84Ù\86Û\8cز Ø§Ù\88تÙ\88Ù\85اتÛ\8cÚ© Ø¯Ù\88غرÙ\88Ù\84اÙ\86اÙ\86 Ø§Û\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8cÙ\84رÙ\87 Ø§Û\8cجازÙ\87 Ù\88ئر",
+       "protect-level-autoconfirmed": "تکجÙ\87 Ø§Ù\88Ù\92تÙ\88Ù\85اتÛ\8cÚ© ØªØ£Û\8cÛ\8cد Ø§Ù\88Ù\92Ù\84Ù\85Ù\88Ø´ Ø§Û\8cØ´Ù\84دÙ\86â\80\8cÙ\84رÙ\87 Ø§Û\8cجازÙ\87 Ù\88ئر",
        "protect-level-sysop": "یالنیز ایداره‌چیلره ایجازه وئر",
        "protect-summary-cascade": "پیلله‌لی",
        "protect-expiring": "$1 (UTC)- تاریخینده واختی بیتیر",
        "protect-expiring-local": "$1-ده بیتیر",
-       "protect-expiry-indefinite": "سÙ\88Ù\86 Ø³Ù\88ز",
+       "protect-expiry-indefinite": "سÙ\88Ù\92Ù\86â\80\8cسÛ\87ز",
        "protect-cascade": "بو صحیفه‌ده ایستیفاده ائدیلن بوتون صحیفه‌لری قوروماغا آل (پیلله‌لی قوروماق)",
        "protect-cantedit": "بو صحیفه‌نین محافظه درجه‌سینی دییش‌دیره بیلمزسینیز، چونکی بو دییشیک‌لیک اوچون حقوقونوز یوخ‌دور.",
        "protect-othertime": "آیری واخت",
        "contributions-userdoesnotexist": "«$1» ایشلدن حسابی ثبت اولونماییب‌دیر.",
        "nocontribs": "بو موشخصاتا اویغون دییشدیر تاپیلمادی",
        "uctop": "(ایندیکی)",
-       "month": "بو آی‌دان (و اؤنجه‌سی):",
-       "year": "بو ایل‌دن (و اؤنجه‌سی):",
-       "sp-contributions-newbies": "یالنیز یئنی ایستیفاده‌چیلرین چالیشمالارینی گؤستر",
+       "month": "بۇ آی‌دان (و قاباقجا):",
+       "year": "بۇ ایل‌دن (و قاباقجا):",
+       "sp-contributions-newbies": "تکجه یئنی ایشلدنلرین چالیشمالارینی گؤستر",
        "sp-contributions-newbies-sub": "یئنی ایستیفاده‌چی‌لر اوچون",
        "sp-contributions-newbies-title": "یئنی حساب‌لار اوچون ایستیفاده‌چی فالیت‌لری",
        "sp-contributions-blocklog": "باغلاما قئیدلری",
        "sp-contributions-blocked-notice": "بو ایستیفاده‌چی حال-حاضردا بلوکلانمیش‌دیر.\nبلوکلاما قئیدلری‌نین سونونجوسو آشاغیدا گؤستریلمیش‌دیر:",
        "sp-contributions-blocked-notice-anon": "بو آی پی-عنوان حال-حاضردا باغلانمیش ‌دیر.\nبالانما قئیدلری‌نین سونونجوسو آشاغیدا گؤستریلمیش‌دیر:",
        "sp-contributions-search": "چالیشمالاری آختار",
-       "sp-contributions-username": "آی‌پی آدرسی ویا ایستیفاده‌چی آدی:",
+       "sp-contributions-username": "آی‌پی آدرسی یوْخسا ایشلدن آدی:",
        "sp-contributions-toponly": "تکجه سون نوسخه اولان دییشیکلری گؤستر",
-       "sp-contributions-newonly": "یالنیز صفحه یاراتماق دَییشیکلیکلرینی گؤستر",
+       "sp-contributions-newonly": "تکجه صفحه یاراتماق دَییشیکلیکلرینی گؤستر",
        "sp-contributions-submit": "آختار",
        "whatlinkshere": "بو صفحه‌یه باغلانتیلار",
        "whatlinkshere-title": "«$1»-ه باغلانان صحیفه‌لر",
        "whatlinkshere-page": "صفحه:",
-       "linkshere": "آشاغیداکی صحیفه‌لر '''[[:$1]]'''-ه باغلانیب:",
+       "linkshere": "آشاغیداکی صفحه‌لر '''[[:$1]]'''-ه باغلانیب:",
        "nolinkshere": "'''[[:$1]]'''-ه هئچ بیر صحیفه باغلانماییب‌دیر.",
        "nolinkshere-ns": "سئچیلمیش آدفضاسیندا، هئچ صحیفه '''[[:$1]]'''-ه باغلانتی‌سی یوخدور.",
        "isredirect": "یوللاندیرما صفحه‌سی",
        "istemplate": "داخیل اولموش",
        "isimage": "فایلا باغلانتی",
-       "whatlinkshere-prev": "{{PLURAL:$1|اؤنجه‌کی|اؤنجه‌کی $1}}",
+       "whatlinkshere-prev": "{{PLURAL:$1|قاباقکی|قاباقکی $1}}",
        "whatlinkshere-next": "{{PLURAL:$1|سونراکی|سونراکی $1}}",
        "whatlinkshere-links": "← باغلانتیلار",
        "whatlinkshere-hideredirs": "یول‌لاندیرمالاری $1",
        "ipbenableautoblock": "مؤوجود اولان مانعه تؤرتمه‌لری گؤستر...",
        "ipbsubmit": "بو ایشلدنی باغلا",
        "ipbother": "باشقا واخت",
-       "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",
+       "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": "بو ایستیفاده‌چی باغلی اولارکن اؤز دانیشیق صحیفه‌سینده دییشدیر ائتمه‌سینی اوز الیله قاباقین آلما",
        "ipblocklist-submit": "آختار",
        "ipblocklist-localblock": "يئرلی بلوک",
        "ipblocklist-otherblocks": "باشقا {{PLURAL:$1|بلوکلاما|بلوکلامالار}}",
-       "infiniteblock": "سÙ\88Ù\86 Ø³Ù\88ز",
+       "infiniteblock": "سÙ\88Ù\92Ù\86â\80\8cسÛ\87ز",
        "expiringblock": "سون تاریخ $1 ساعت $2",
        "anononlyblock": "يالنیز تانیش‌سیزایستیفاده‌چی",
        "noautoblockblock": "آوتوبلوکلاما غيری مومکوندور",
        "databasenotlocked": "وئریلن‌لر بازاسی باغلانماییب.",
        "lockedbyandtime": "({{Gender: $1 | $1}} طرفین‌دن  $2 $3 اعتبار ایله)",
        "move-page": "$1 داشینیر",
-       "move-page-legend": "صحیفه‌نین آدینی دییش",
+       "move-page-legend": "صفحه‌نین آدینی دَییش",
        "movepagetext": "آشاغی‌داکی فورمدان ایستیفاده ائتمک، صحیفه‌نین آدینی، بوتون تاریخچه‌سینی ده کؤچورمک‌له، یئنی باشلیغا دییشه‌جک.\nاسکی باشلیق یئنی باشلیغا یول‌لاندیریلاجاق‌دیر.\nاسکی صحیفه‌یه اولان یول‌لاندیرماقلاری، اوتوماتیک گونجل‌له‌یه بیلرسینیز.\nبو سئچیمی ائتمه‌دیگینیز حالدا، [[Special:DoubleRedirects|تکرارلانان]] و یا [[Special:BrokenRedirects|قیریق یول‌لاندیرمالاری]] یوخلاماغی یاددان چیخارمایین.\nباغلانتیلاری اویغون یئره یول‌لاندیرماسیندان آرخایین اولماق، سیزین مسئولیتینیزده‌دیر.\n\nنظره آلین کی، هدف باشلیق آلتیندا بیر صحیفه مؤوجود اولسا، یئردییشمه '''باش توتمایاجاق'''، مگر بوکی او سونراکی صحیفه یول‌لاندیرما اولا و اؤنجه دَییشمه گئچمیشی ده اولمایا. بو او دئمک‌دیر کی، سهواً آدینی دییشدیگینیز صحیفه‌لری گئری قایتارا بیلمک اولار، بونونلا یاناشی آرتیق مؤوجود اولان صحیفه‌نین اوزرینه باشقا صحیفه یازا بیلمزسینیز.\n\n'''خبردارلیق!'''\nبو یئردییشمه مشهور صحیفه اوچون اساس‌لی و گؤزلنیلمز اولا بیلر؛ اونا گؤره ده بو دییشیک‌لیگی یئرینه یئتیرمزدن اول، بونون مومکون نتیجه‌لرینی باشا دوشدوگونوزدن آرخایین اولون.",
-       "movepagetext-noredirectfixer": "آشاغی‌داکی فورمو دول‌دورماق بیر صحیفنی یئنی‌دن آدلاندیریر، بوتون کئچمیشینی یئنی آدا داشیییر.\nکؤهنه مؤوزو یئنی باشلیغا بیر ایستیقامتلندیرمه صحیفه‌سی اولار.\n[[Special:DoubleRedirects|جوت]] یا دا [[Special:BrokenRedirects|نوزوک ایستیقامتلندیرمه‌لر]] صحیفه‌لرینی ایداره ائدین.\nعلاقه‌لرین گئتمه‌لری لازیم اولان یئرلره گئتدیک‌لرینی عمین اولماق سیزین سوروملولوغونوزدا‌دیر.\n\nیئنی باش‌لیقدا مؤوجود بیر صحیفه وارسا، بوش یا دا بیر ایستیقامتلندیرمه اولمادیقجا و دییشیک‌لیک کئچمیشی اولمادیغی تق‌دیرده، سهیف 'تاشینمایاجاکتیر.\nبو بو معنانی وئرر، بیر صحیفنی اشتباه ائتسه‌نیز صحیفنی کؤهنه آدییلا یئنی‌دن آدلان‌دیرا بیلریک، بو مؤوجود صحیفه‌نین اوزرینه یازماز.\n\n' 'خبردارلیق!'\nبو مشهور بیر صحیفه اوچون تأثیرلی و گؤزلنیلمز بیر دییشیک‌لیک اولا بیلر؛\nخاهیش ائدیریک راتیفیکاسیا ائتمه‌دن اول بونون نتیجه‌لرینی آنلادیغینیزدان امین اولون.",
+       "movepagetext-noredirectfixer": "آشاغی‌داکی فوْرمو دوْلدورماق بیر صفحه‌نی یئنی‌دن آدلاندیریر، بۆتون گئچمیشینی یئنی آدا داشیییر.\nکؤهنه موضوعو یئنی باشلیغا بیر یوْللاندیرما صفحه‌سی اوْلار.\n[[Special:DoubleRedirects|ایکی‌قات]] یوْخسا [[Special:BrokenRedirects|خطالی یوْللاندیرمالار]] صفحه‌لرینی یوْخلایین.\nباغلانتی‌لارین گئتمه‌لری گرک اولان یئرلره گئتدیک‌لرینی آرخایین اوْلماق سیزین مسعولیتینیزدیر.\n\nیئنی باشلیقدا مؤوجود بیر صفحه وارسا، بوْش یوْخسا بیر یوْللاندیرما اوْلمادیقجا و دییشیک‌لیک گئچمیشی اوْلمازسا، صفحه داشینمایاجاق.\nبۇ یانی، بیر صفحه‌نی اشتباه ائتسه‌نیز صفحه‌نی کؤهنه آدییلا یئنی‌دن آدلاندیرا بیلرسینیز، آنجاق بیر صفحه‌نی قاباقجادان مووجود اوْلان صفحه‌یه یوْللاندیرا بیلمزسینیز.\n\n' 'تذکور!'\nچوْخ گؤروشلو صفحه‌لرین یوْللاندیرماسی گؤزله‌نیلمز اثرلری اوْلا بیلر. لوطفا یوْللاندیرمادان قاباق، ایشینیزین نتیجه‌سیندن آرخایین اوْلون.",
        "movepagetalktext": "اویغون دانیشیق صحیفه‌سی آوتوماتیک حرکت ائده‌جک 'گر:'\n* بوش اولمایان دانیشیق صحیفه‌سی یئنی آدلا آرتیق مؤوجوددورسا، و یا\n* سیز بایراغی آشاغی‌دان گؤتورسه‌نیز.\n\nهمین حال‌لاردا ، احتیاج یارانارسا سیز صحیفه‌لری الله بیرلش‌دیرمک مجبوریتینده قالاجاقسینیز",
        "moveuserpage-warning": "' 'خبردارلیق:' بیر ایستیفاده‌چی صحیفه‌سینی داشیماق اوزرسینیز. خاهیش ائدیریک یالنیز صحیفه‌نین تاشیناجاغینا، آنجاق ایستیفاده‌چی‌نین یئنی‌دن آدلاندیریلمایاجاغینا دقت ائدین.",
        "movenologintext": "صحیفه‌نین آدینی دییشیک‌لیک اوچون قئیدیات‌لی و [[Special:UserLogin|سیستئمه]] داخیل اولمانیز لازیم‌دیر.",
        "cant-move-user-page": "ایستیفاده‌چی صحیفه‌لری‌نین آدینی دییشه بیلمزسینیز (باش‌لیق‌لاردان باشقا).",
        "cant-move-to-user-page": "بیر صحیفنی، بیر ایستیفاده‌چی صحیفه‌سینه داشیماغا ایجازه وئریلمیر (بیر ایستیفاده‌چی آلتسایفاسی خاریجینده).",
        "newtitle": "یئنی باش‌لیق",
-       "move-watch": "بو صحیفنی ایزله",
-       "movepagebtn": "صحیفه‌نین آدینی دییش",
+       "move-watch": "بۇ صفحه‌نی ایزله",
+       "movepagebtn": "صفحه‌نین آدینی دَییش",
        "pagemovedsub": "یئردییشمه ائدیلمیش‌دیر",
-       "movepage-moved": "'\"$1\" صحیفه‌سی \"$2\" صحیفه‌سینه یئرلشدیریلمیشدیر",
+       "movepage-moved": "'''«$1»، «$2» صفحه‌سینه داشیندی'''",
        "movepage-moved-redirect": "یؤنلندیرمه یارادیلدی.",
        "movepage-moved-noredirect": "یؤنلندیرمه‌نین یارادیلماسینین قارشییس آلیندی.",
        "articleexists": "بو آددا صحیفه آرتیق مؤوجوددور و یا سیزین سئچدیگینیز آد اویغون دئییل.\nزحمت اولماسا باشقا آد سئچین.",
        "cantmove-titleprotected": "بیر صحیفنی بو مؤوقئیه داشییا بیلمز، چونکی یئنی موضونون یارادیلماسی قورونور",
-       "movetalk": "بو صحیفه‌نین دانیشیق صحیفه‌سی‌نین ده آدینی دییش‌دیر.",
+       "movetalk": "بۇ صفحه‌نین دانیشیق صفحه‌سی‌نین ده آدینی دَییشدیر.",
        "move-subpages": "یاریم صحیفه‌لری کؤچور ($1-ا قدر)",
        "move-talk-subpages": "دانیشیق صحیفه‌لری‌نین آلت صحیفه‌لرینی کؤچور ($1-ا قدر)",
        "movepage-page-exists": "$1 مادده‌سی اونسوز دا وار اولماقدا‌دیر، و آوتوماتیک اولا‌راق یئنی‌دن یازیلا بیلمز.",
        "movepage-page-unmoved": "$1 صحیفه‌سی $2 صحیفه‌سینه کؤچوروله بیلینمیر.",
        "movepage-max-pages": "ان چوخ $1 {{PLURAL:$1|صحیفه |صحیفه}} داشیندی و داها چوخو آوتوماتیک اولا‌راق تاشیناماز.",
        "movelogpage": "آد ديیشدیرمک قئیدی",
-       "movelogpagetext": "آشاغیدا اولان سیاهی آدی دییشدیریلمیش صحیفه‌لری گؤستریر.",
+       "movelogpagetext": "آشاغی‌دا اوْلان لیست آدی دَییشدیریلمیش صفحه‌لری سیرالاییر.",
        "movesubpage": "{{PLURAL:$1|آلتینداکی صحیفه}}",
        "movesubpagetext": "بو صحیفه‌نین آشاغیدا گؤستریلن $1 {{PLURAL:$1 | آلت صحیفه سی | آلت صحیفه اسی}} وار.",
        "movenosubpage": "بو صحیفه‌نین آلت صحیفه‌سی یوخ‌دور.",
        "table_pager_limit_label": "هر صحیفه‌ده اولان موردلر سایی‌سی",
        "table_pager_limit_submit": "گئت",
        "table_pager_empty": "نتیجه سیز",
-       "autosumm-blank": "صحیفه‌‌نی بوشالتدی",
-       "autosumm-replace": "صحیفه‌‌نین مظمونو ' $1' يازیسی ایله ديَیشدیریلدی",
-       "autoredircomment": "[[$1]] صحیفه‌‌سینه ایستیقامتلندیریلیر",
+       "autosumm-blank": "صفحه‌‌نی بوْشالتدی",
+       "autosumm-replace": "صفحه‌‌نین مضمونونو ' $1' يازیسی ایله ديَیشدیریلدی",
+       "autoredircomment": "[[$1]] صفحه‌‌سینه یوْللاندیریلیر",
        "autosumm-new": "صفحه‌‌نی ' $1' ایله ياراتدی",
        "autosumm-newblank": "بوش صحفه یاراندی",
        "lag-warn-normal": "$1 {{PLURAL:$1 | سانیيه‌دن | سانیيه‌ده}} يئنی ديَیشیکلیکلر بو سیياهیدا گؤرولمه‌يه.",
        "fileduplicatesearch-noresults": "\"$1\" آدیندا فایل تاپیلمادی.",
        "specialpages": "اؤزل صفحه‌لر",
        "specialpages-note": "* نورمال اؤزل صفحه‌لر.\n* <span class=\"mw-specialpagerestricted\">محدودلاشدیریلمیش اؤزل صفحه‌لر.</span>",
-       "specialpages-group-maintenance": "جارÛ\8c Ù\85رÙ\88زÙ\87â\80\8cÙ\84ر",
+       "specialpages-group-maintenance": "ساخÙ\84اÙ\86Û\8cØ´ Ø±Ø§Ù¾Ù\88رتÙ\84ارÛ\8c",
        "specialpages-group-other": "آیری اؤزل صفحه‌لر",
-       "specialpages-group-login": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cاراد",
+       "specialpages-group-login": "Ú¯Û\8cرÛ\8cØ´ / Ø­Ø³Ø§Ø¨ Û\8cارات",
        "specialpages-group-changes": "سون دییشیک‌لیک‌لر و قئیدلر",
        "specialpages-group-media": "مئدیا مروزه‌لری و یوکلمه‌لر",
-       "specialpages-group-users": "اÛ\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c‌لر و حاقلار",
-       "specialpages-group-highuse": "ان چوْخ ایشله‌نن صفحه‌لر",
-       "specialpages-group-pages": "صحیفه‌لرین سیاهی‌لاری",
+       "specialpages-group-users": "اÛ\8cØ´Ù\84دÙ\86‌لر و حاقلار",
+       "specialpages-group-highuse": "ان چوْخ ایشلدیلمیش صفحه‌لر",
+       "specialpages-group-pages": "صفحه‌لرین لیست‌لری",
        "specialpages-group-pagetools": "صفحه آلتلری",
        "specialpages-group-wiki": "بیلگیلر و آلتلر",
-       "specialpages-group-redirects": "خصوصی ایستیقامتلندیرمه صحیفه‌لری",
+       "specialpages-group-redirects": "مخصوص یوْللاندیرما صفحه‌لری",
        "specialpages-group-spam": "هرزه یازماق آلت‌لری",
        "blankpage": "بوش صفحه",
        "intentionallyblankpage": "بو صحیفه خصوصیله بوش‌دور.",
        "htmlform-user-not-valid": "<strong>$1</strong> بیر دوزگون ایشلدن آدی دئییل.",
        "sqlite-has-fts": "$1 بوتون یازی آختارما دستگی‌له",
        "sqlite-no-fts": "$1 بوتون یازی آختارماماق‌لا",
-       "logentry-delete-delete": "$1، $3 صحیفه‌سینی {{GENDER:$2|سیلدی}}",
-       "logentry-delete-restore": "$1، $3 صحیفه‌سینی {{GENDER:$2|قایتاردی}}",
+       "logentry-delete-delete": "$1، $3 صفحه‌سینی {{GENDER:$2|سیلدی}}",
+       "logentry-delete-restore": "$1، $3 صفحه‌سینی {{GENDER:$2|قایتاردی}}",
        "logentry-delete-event": "$1، $3-ده $5 سیاهی اولایینین {{PLURAL:$5|گؤرونوشونو|گؤرونوشلرینی}} {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-delete-revision": "$1، $3 صحیفه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} {{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-delete": "$1، $3 صفحه‌سینی {{GENDER:$2|یاتیردی}}",
        "logentry-suppress-event": "$1، $3-ده $5 سیاهی اولایینین {{PLURAL:$5|گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-suppress-revision": "$1، $3 صحیفه‌سینده $5 نوسخه‌نین {{PLURAL:گؤرونوشونو|گؤرونوشلرینی}} گیزلینجه {{GENDER:$2|دَییشدیردی}}: $4",
        "logentry-suppress-event-legacy": "$1، $3-ده سیاهی اولایلارینین گؤرونوشلرینی گیزلینجه {{GENDER:$2|دَییشدیردی}}",
        "revdelete-unrestricted": "ایداره‌چیلرین محدودیتلرینی گؤتوردو",
        "logentry-block-block": "$1 {{GENDER:$4|$3}}-نی {{GENDER:$2|بلوکلادی}}. قورتارماق تاریخی: $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$4|$3}}-نین {{GENDER:$2|بلوکلاماغینی قالدیردی}}",
-       "logentry-move-move": "$1، $3 صحیفه‌سینی $4-ه {{GENDER:$2|آپاردی}}",
-       "logentry-move-move-noredirect": "$1، $3 صحیفه‌سینی، یول‌لاندیرما قویماماق‌لا، $4-ه {{GENDER:$2|آپاردی}}",
-       "logentry-move-move_redir": "$1، $3 صحیفه‌سینی، $4-ده یول‌لاندیرما اوستونه {{GENDER:$2|آپاردی}}",
-       "logentry-move-move_redir-noredirect": "$1، $3 صحیفه‌سینی، یول‌لاندیرما قویماماق‌لا، یول‌لاندیرما اولان $4 اوستونه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move": "$1، $3 صفحه‌سینی $4-ه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move-noredirect": "$1، $3 صفحه‌سینی، یوْل‌لاندیرما قوْیماماق‌لا، $4-ه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move_redir": "$1، $3 صفحه‌سینی، $4-ده یوْل‌لاندیرما اۆستونه {{GENDER:$2|آپاردی}}",
+       "logentry-move-move_redir-noredirect": "$1، $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|یاراتدی}}",
        "logentry-newusers-create": "$1 ایشلدن حسابی {{GENDER:$2|یارادیلدی}}",
        "logentry-newusers-create2": "$1 ایستیفاده‌چی، $3 حسابی {{GENDER:$2|یاراتدی}}",
        "logentry-newusers-byemail": "$3 ایستیفاده‌چی حسابی، $1 ایله {{GENDER:$2|یارادیلیب}} و رمز، ایمیل ایله گؤندریلیب‌دیر",
-       "logentry-newusers-autocreate": "$1 ایستیفاده‌چی حسابی اوتوماتیک {{GENDER:$2|یارادیلدی}}",
+       "logentry-newusers-autocreate": "$1 ایشلدن حسابی اوْتوماتیک {{GENDER:$2|یارادیلدی}}",
+       "logentry-protect-protect": "$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|آرتیریلدی}}",
index e2f6019..b00e6dd 100644 (file)
                        "Рустам Нурыев",
                        "ҒатаУлла",
                        "Ҡамыр Батыр",
-                       "아라"
+                       "아라",
+                       "Ләйсән"
                ]
        },
        "tog-underline": "Һылтанмалар аҫтына һыҙыу:",
        "tog-hideminor": "Әһәмиәте ҙур булмаған төҙәтеүҙәрҙе һуңғы үҙгәртеүҙәр исемлегендә күрһәтмәҫкә",
        "tog-hidepatrolled": "Һуңғы үҙгәртеүҙәр исемлегендә тикшерелгән үҙгәртеүҙәрҙе йәшер",
        "tog-newpageshidepatrolled": "Яңы биттәр исемлегендә тикшерелгән үҙгәртеүҙәрҙе йәшер",
+       "tog-hidecategorization": "Биттәр категоризацияһын йәшереү",
        "tog-extendwatchlist": "Барлыҡ үҙгәртеүҙәрҙе үҙ эсенә алған, киңәйтелгән күҙәтеү исемлеге",
        "tog-usenewrc": "Һуңғы төҙәтеүҙәр һәм күҙәтеү исемлегендәге үҙгәрештәрҙе төркөмдәргә бүлергә",
        "tog-numberheadings": "Башисемдәрҙе автоматик рәүештә номерланһын",
@@ -32,6 +34,7 @@
        "tog-watchdefault": "Мин үҙгәрткән биттәр һәм файлдар аңлатмаһын күҙәтеү исемлегенә өҫтәргә",
        "tog-watchmoves": "Мин исемен үҙгәрткән биттәрҙе һәм файлдарҙы күҙәтеү исемлегенә өҫтәргә",
        "tog-watchdeletion": "Мин юйған биттәрҙе һәм файлдарҙы күҙәтеү исемлегенә өҫтәргә",
+       "tog-watchrollback": "Мин ебәргән биттәрҙе күҙәтеү исемлегенә өҫтәргә",
        "tog-minordefault": "Бөтә үҙгәртеүҙәрҙе, ғәҙәттә, әҙ үҙгәреш тип билдәләргә",
        "tog-previewontop": "Алдан байҡау тәҙрәһен мөхәррирләү битенең өҫтөнә ҡуйырға",
        "tog-previewonfirst": "Мөхәррирләүгә күскәндә алдан ҡарау күрһәтелһен",
        "tog-watchlisthidebots": "Боттар  үҙгәртеүҙәрен күҙәтеү исемлегенән йәшерергә",
        "tog-watchlisthideminor": "Әҙ үҙгәрештәрҙе күҙәтеү исемлегенән йәшерергә",
        "tog-watchlisthideliu": "Танылған ҡулланыусыларҙың үҙгәртеүҙәрен күҙәтеү исемлегенән йәшерергә",
+       "tog-watchlistreloadautomatically": "Һөҙгөс үҙгәргән һайын күҙәтеү исемлеген автоматик рәүештә яңыртырға (JavaScript кәрәк)",
        "tog-watchlisthideanons": "Аноним ҡулланыусыларҙың үҙгәртеүҙерен күҙәтеү исемлегенән йәшерергә",
        "tog-watchlisthidepatrolled": "Тикшерелгән үҙгәртеүҙәрҙе күҙәтеү исемлегенән йәшерергә",
+       "tog-watchlisthidecategorization": "Биттәр категоризацияһын йәшереү",
        "tog-ccmeonemails": "Башҡа ҡулланыусыларға ебәргән хаттарымдың күсермәләрен үҙемә лә ебәрергә",
        "tog-diffonly": "Версия сағыштырыу аҫтында бит эстәлеге күрһәтелмәһен",
        "tog-showhiddencats": "Йәшерен категорияларҙы күрһәтергә",
        "morenotlisted": "Был исемлек тулы түгел",
        "mypage": "Бит",
        "mytalk": "Әңгәмә",
-       "anontalk": "Был IP-адресының фекер алышыу бите",
+       "anontalk": "Әңгәмә",
        "navigation": "Төп йүнәлештәр",
        "and": "&#32;һәм",
        "qbfind": "Эҙләү",
        "qbmyoptions": "Биттәрем",
        "faq": "ЙБҺ",
        "faqpage": "Project:ЙБҺ",
-       "actions": "Хәрәкәт",
+       "actions": "Ғәмәлдәр",
        "namespaces": "Исем арауыҡтары",
        "variants": "Варианттар",
        "navigation-heading": "Навигация",
        "tagline": "{{SITENAME}} проектынан",
        "help": "Белешмә",
        "search": "Эҙләү",
-       "searchbutton": "ТабÑ\8bÑ\83",
+       "searchbutton": "ЭÒ\99лÓ\99Ò¯",
        "go": "Күсеү",
        "searcharticle": "Күсеү",
-       "history": "ТаÑ\80иÑ\85",
+       "history": "Ð\91иÑ\82 Ñ\82аÑ\80иÑ\85Ñ\8b",
        "history_short": "Тарих",
-       "updatedmarker": "Ò»Ñ\83Ò£Ò\93Ñ\8b ÐºÐµÑ\80еүемдән һуң яңыртылған",
+       "updatedmarker": "Ò»Ñ\83Ò£Ò\93Ñ\8b Ð¸Ð½еүемдән һуң яңыртылған",
        "printableversion": "Баҫтырыу өлгөһө",
        "permalink": "Даими һылтанма",
        "print": "Баҫыу",
        "view-foreign": "$1 сайтында ҡарау",
        "edit": "Үҙгәртеү",
        "edit-local": "Локаль тасуирламаны үҙгәртергә",
-       "create": "ЯһаÑ\80Ò\93а",
+       "create": "ТөÒ\99Ó©Ñ\80гÓ\99",
        "create-local": "Локаль тасуирлама өҫтәргә",
        "editthispage": "Был мәҡәләне үҙгәртергә",
        "create-this-page": "Был битте яһарға",
        "delete": "Юҡ  итергә",
        "deletethispage": "Был битте юйырға",
-       "undeletethispage": "Юйылған был битте ҡабат тергеҙеү",
+       "undeletethispage": "Юйылған битте ҡабат тергеҙеү",
        "undelete_short": "$1 {{PLURAL:$1|үҙгәртеүҙе}} тергеҙергә",
        "viewdeleted_short": "{{PLURAL:$1|1=1 юйылған үҙгәртеүҙе|$1 юйылған үҙгәртеүҙе}} ҡарау",
        "protect": "Һаҡларға",
        "laggedslavemode": "'''Иғтибар:''' биттә һуңғы үҙгәртеүҙәр күрһәтелмәгән булырға мөмкин.",
        "readonly": "Мәғлүмәттәр базаһы бикләнгән",
        "enterlockreason": "Ябылыу сәбәбен һәм ваҡытын белдерегеҙ.",
-       "readonlytext": "ЯңÑ\8b Ð¼Ó\99Ò¡Ó\99лÓ\99лÓ\99Ñ\80 Ó©Ò«Ñ\82Ó\99Ò¯ Ò»Ó\99м Ð¼Ó\99Ò\93лүмÓ\99Ñ\82Ñ\82Ó\99Ñ\80 Ð±Ð°Ð·Ð°Ò»Ñ\8bндаÒ\93Ñ\8b Ð±Ð°Ñ\88ҡа Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99Ó\99Ñ\80 Ñ\85Ó\99Ò\99еÑ\80 Ñ\8fбÑ\8bлÒ\93ан. Ð\91Ñ\8bл Ð¿Ð»Ð°Ð½Ð»Ñ\8b Ñ\85еÒ\99мÓ\99Ñ\82лÓ\99ндеÑ\80еү Ñ\81Ó\99бÓ\99пле Ð±Ñ\83лÑ\8bÑ\83Ñ\8b Ð¼Ó©Ð¼ÐºÐ¸Ð½, Ð°Ò\99аҡÑ\82ан Ð½Ð¾Ñ\80малÑ\8c хәлгә ҡайтасаҡ.\n\nЯбыусы хаким ҡалдырған аңлатма:\n$1",
+       "readonlytext": "ЯңÑ\8b Ð¼Ó\99Ò¡Ó\99лÓ\99лÓ\99Ñ\80 Ó©Ò«Ñ\82Ó\99Ò¯ Ò»Ó\99м Ð¼Ó\99Ò\93лүмÓ\99Ñ\82Ñ\82Ó\99Ñ\80 Ð±Ð°Ð·Ð°Ò»Ñ\8bндаÒ\93Ñ\8b Ð±Ð°Ñ\88ҡа Ò¯Ò\99гÓ\99Ñ\80Ñ\82еүÒ\99Ó\99Ñ\80 Ñ\85Ó\99Ò\99еÑ\80 Ñ\82Ñ\8bйÑ\8bлÒ\93ан. Ð\91Ñ\8bл Ð¿Ð»Ð°Ð½Ð»Ñ\8b Ñ\85еÒ\99мÓ\99Ñ\82лÓ\99ндеÑ\80еү Ñ\81Ó\99бÓ\99пле Ð±Ñ\83лÑ\8bÑ\83Ñ\8b Ð¼Ó©Ð¼ÐºÐ¸Ð½, Ð°Ò\99аҡÑ\82ан Ò\93Ó\99Ò\99Ó\99Ñ\82и хәлгә ҡайтасаҡ.\n\nЯбыусы хаким ҡалдырған аңлатма:\n$1",
        "missing-article": "Мәғлүмәттәр базаһында «$1» $2 битенең һоралған тексты табылманы.\n\nБыл, ғәҙәттә, иҫкергән һылтанма буйынса юйылған биттең  үҙгәртеү тарихына күскәндә килеп сыға.\n\nӘгәр хатаның сәбәбе ул булмаһа, тимәк һеҙ программала хата тапҡанһығыҙ.\nБыл турала зинһар URL-ды күрһәтеп, [[Special:ListUsers/sysop|хакимгә]] белдерегеҙ.",
        "missingarticle-rev": "(версия № $1)",
        "missingarticle-diff": "(айырма: $1, $2)",
        "invalidtitle-knownnamespace": "\"$2\" исем арауығы һәм \"$3\"  тексты исем өсөн ярамай",
        "invalidtitle-unknownnamespace": "\"$2\" тексты һәм \"$1\" арауыҡ өсөн билдәһеҙ номерлы исем ярамай",
        "exception-nologin": "Танылмағанһығыҙ",
-       "exception-nologin-text": "Был битте ҡарау йәки һоратылған ғәмәлде башҡарыу өсөн системала [[Special:Userlogin|танылырға]] кәрәк.",
+       "exception-nologin-text": "Был битте ҡарау йәки һоратылған ғәмәлде башҡарыу өсөн системала [[Special:Userlogin|танылыр]] кәрәк.",
        "virus-badscanner": "Көйләү хатаһы: Билдәһеҙ вирустар сканеры: ''$1''",
        "virus-scanfailed": "сканлау хатаһы ($1 коды)",
        "virus-unknownscanner": "беленмәгән антивирус:",
        "logouttext": "'''Һеҙ эш сеансын тамамланығыҙ.'''\n\nҠайһы бер биттәр һеҙ системаға танылмаған кеүек күренеүен дауам итер. Быны бөтөрөү өсөн браузер кэшын таҙартығыҙ.",
+       "cannotlogoutnow-title": "Хәҙер үк сығып булмай",
        "welcomeuser": "Рәхим итегеҙ $1!",
        "welcomecreation-msg": "Иҫәп яҙыуығыҙ яһалды.\nШәхси [[Special:Preferences|{{SITENAME}} көйләүҙәрен]] үҙегеҙгә уңайлы итеп үҙгәртергә онотмағыҙ.",
        "yourname": "Ҡатнашыусы исеме",
        "remembermypassword": "Был браузерҙа (иң күбендә $1 {{PLURAL:$1|көнгә}}) иҫәп яҙыуым хәтерләнһен",
        "userlogin-remembermypassword": "Системала ҡалырға",
        "userlogin-signwithsecure": "Һаҡланыулы тоташыу",
+       "cannotloginnow-title": "Хәҙер үк инеп булмай",
        "yourdomainname": "Һеҙҙең домен",
        "password-change-forbidden": "Был викила серһүҙегеҙҙе үҙгәртә алмайһығыҙ.",
        "externaldberror": "Тышҡы мәғлүмәт базаһы менән танылғанда хата барлыҡҡа килде йәки тышҡы үҙ көйләүҙәрегеҙҙе үҙгәртер өсөн хоҡуҡтарығыҙ етәрле түгел.",
        "createaccountreason": "Сәбәп:",
        "createacct-reason": "Сәбәп",
        "createacct-reason-ph": "Икенсе иҫәп яҙмаһы һеҙгә ни өсөн кәрәк?",
-       "createacct-captcha": "Һаҡлылыҡты тикшереү",
-       "createacct-imgcaptcha-ph": "Өҫтәге тексты индерегеҙ",
        "createacct-submit": "Иҫәп яҙмаһын булдырырға",
-       "createacct-another-submit": "ТаÒ\93Ñ\8b Ð±ÐµÑ\80 Ð¸ҫәп яҙмаһын булдырырға",
+       "createacct-another-submit": "Ð\98ҫәп яҙмаһын булдырырға",
        "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": "Иҫәп яҙмаһын булдырғандағы хата",
        "passwordtooshort": "Серһүҙ кәмендә $1 {{PLURAL:$1|символдан}} торорға тейеш.",
        "password-name-match": "Керетелгән серһүҙ ҡулланыусы исеменән айырылырға тейеш.",
        "password-login-forbidden": "Был ҡатнашыусы исемен һәм серһүҙҙе ҡулланыу тыйылған",
-       "mailmypassword": "ЯңÑ\8b Ñ\81еÑ\80Ò»Ò¯Ò\99 ÐµÐ±Ó\99Ñ\80еÑ\80гÓ\99",
+       "mailmypassword": "СеÑ\80Ò»Ò¯Ò\99Ò\99е Ñ\82аÑ\88лаÑ\82Ñ\8bÑ\83",
        "passwordremindertitle": "{{SITENAME}} өсөн яңы ваҡытлыса серһүҙ",
        "passwordremindertext": "Кемдер (бәлки, һеҙ, IP-адресы: $1) {{SITENAME}} ($4) өсөн яңы серһүҙ һоратты. $2 ҡатнашыусыһы өсөн ваҡытлыса яңы серһүҙ яһалды: $3. Әгәр был һеҙ булһағыҙ, системага керегеҙ һәм серһүҙ алмаштырығыҙ. Яңы серһүҙ $5 {{PLURAL:$5|көн}} ғәмәлдә буласаҡ.\n\nӘгәр һеҙ серһүҙҙе алмаштырыуҙы һоратмаған йәки онотоп кире иҫләгән булһағыҙ һәм үҙгәртергә теләмәһәгеҙ, был хәбәргә иғтибар итмәгеҙ һәм элекке серһүҙҙе ҡулланыуҙы дауам итегеҙ.",
        "noemail": "$1 исемле ҡулланыусы өсөн электрон почта адресы белдерелмәгән.",
        "retypenew": "Серһүҙҙе яңынан керетегеҙ:",
        "resetpass_submit": "Серһүҙ ҡуйырға һәм танышырға",
        "changepassword-success": "Серһүҙегеҙ уңышлы үҙгәртелде!",
+       "botpasswords-label-appid": "Бот исеме:",
+       "botpasswords-label-create": "Төҙөргә",
+       "botpasswords-label-update": "Яңыртырға",
+       "botpasswords-label-cancel": "Кире алырға",
+       "botpasswords-label-delete": "Юйырға",
+       "botpasswords-label-resetpassword": "Серһүҙҙе ташлатыу",
+       "botpasswords-label-grants": "Ҡулланылған рөхсәттәр:",
        "resetpass_forbidden": "Серһүҙҙе үҙгәртеп булмай",
        "resetpass-no-info": "Был битте туранан ҡарау өсөн һеҙгә системала танылырға кәрәк.",
        "resetpass-submit-loggedin": "Серһүҙҙе үҙгәртергә",
        "passwordreset-emailtext-ip": "Берәү (бәлки һәҙ, $1 IP-адресынан ) {{SITENAME}} ($4) проектындағы иҫәп яҙыуығыҙҙы хәтерләтеүҙе һоратты.\nКиләһе ҡулланыусы {{PLURAL:$3|1=иҫәп яҙыуы|иҫәп яҙыуҙары}} был электрон почта адресы менән бәйле:\n\n$2\n\nБыл ваҡытлыса {{PLURAL:$3|1=серһүҙ|серһүҙҙәр}} {{PLURAL:$5|$5 көн}} ғәмәлдә буласаҡ.\nҺеҙ системала танылырға һәм яңы серһүҙ һайларға тейешһегеҙ.\nӘгәр, һеҙ быны һоратмаған булһағыҙ йәки элекке серһүҙегеҙҙе киренән иҫләһәгеҙ һәм уны үҙгәртергә теләмәһәгеҙ, был хатҡа иғтибар итмәгеҙ һәм элекке серһүҙегеҙҙе ҡулланыуҙы дауам итегеҙ.",
        "passwordreset-emailtext-user": "{{SITENAME}} проектындағы $1 ҡулланыусыһы {{SITENAME}} ($4) проектындағы иҫәп яҙыуығыҙҙы хәтерләтеүҙе һоратты. Киләһе ҡулланыусы {{PLURAL:$3|1=иҫәп яҙыуы|иҫәп яҙыуҙары}} был электрон почта адресы менән бәйле:\n\n$2\n\nБыл ваҡытлыса {{PLURAL:$3|1=серһүҙ|серһүҙҙәр}} {{PLURAL:$5|$5 көн}} ғәмәлдә буласаҡ.\nҺеҙ системала танылырға һәм яңы серһүҙ һайларға тейешһегеҙ.\nӘгәр, һеҙ быны һоратмаған булһағыҙ йәки элекке серһүҙегеҙҙе киренән иҫләһәгеҙ һәм уны үҙгәртергә теләмәһәгеҙ, был хатҡа иғтибар итмәгеҙ һәм элекке серһүҙеҙҙе ҡулланыуҙы дауам итегеҙ.",
        "passwordreset-emailelement": "Ҡулланыусы исеме: \n$1\n\nВаҡытлыса серһүҙ: \n$2",
-       "passwordreset-emailsent": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон почта аша хат ебәрелде.",
+       "passwordreset-emailsentemail": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон почта аша хат ебәрелде.",
        "passwordreset-emailsent-capture": "Серһүҙҙе ташлау тураһындағы мәғлүмәт менән электрон хат ебәрелде, уның тексы түбәндә бирелә:",
        "passwordreset-emailerror-capture": "Серһүҙҙе ташлау тураһында хәбәр итеүсе электрон хат булдырылғайны, ләкин уны  {{GENDER:$2|kullanıcıya}} түбәндәге сәбәп арҡаһында ебәреп булманы: $1",
        "changeemail": "Электрон почта адресын үҙгәртергә",
-       "changeemail-text": "Электрон почта адресығыҙҙы үҙгәртеү өсөн түбәндәге форманы тултырығыҙ. Үҙгәртеүҙәрҙе раҫлау өсөн серһүҙегеҙҙе керетеү кәрәк буласаҡ.",
+       "changeemail-header": "Электрон почта адресын үҙгәртеү",
        "changeemail-no-info": "Был биткә туранан ирешеү өсөн һеҙгә системала танылыу кәрәк.",
        "changeemail-oldemail": "Хәҙерге электрон почта адресы:",
        "changeemail-newemail": "Яңы электрон почта адресы:",
        "columns": "Бағаналар:",
        "searchresultshead": "Эҙләү",
        "stub-threshold": "<a href=\"#\" class=\"stub\">Материалдарға һылтанмалар </a> форматлау сиге (байттарҙа)",
+       "stub-threshold-sample-link": "миҫал",
        "stub-threshold-disabled": "Һүндерелгән",
        "recentchangesdays": "Күҙәтеү исемлегендә күренгән көндәр һаны:",
        "recentchangesdays-max": "Иң күбендә $1 {{PLURAL:$1|көн}}",
        "prefs-help-recentchangescount": "Һуңғы үҙгәртеүҙәрҙе, биттәр тарихын, журналдарҙы үҙ эсенә ала.",
        "prefs-help-watchlist-token2": "Был - күҙәтеүҙәрегеҙ исемлегенең веб-каналы өсөн йәшерен асҡыс.\nУны белеүселәр күҙәтеүҙәрегеҙ исемлеген уҡый аласаҡ, шуға уны бер кемгә лә әйтмәгеҙ. [[Special:ResetTokens|Уны ташларға теләһәгеҙ, ошонда баҫығыҙ]].",
        "savedprefs": "Һеҙҙең көйләүҙәрегеҙ һаҡланды.",
+       "savedrights": "{{GENDER:$1|$1}} ҡулланыусының хоҡуҡтары һаҡланды.",
        "timezonelegend": "Ваҡыт бүлкәте:",
        "localtime": "Урындағы ваҡыт:",
        "timezoneuseserverdefault": "Сервер көйләүҙәрен ҡулланырға $1",
        "badsig": "Хаталы имза. HTML-тегдарҙың дөрөҫлөгөн тикшерегеҙ.",
        "badsiglength": "Бигерәк оҙон имза. \nИмза оҙонлоғо $1 {{PLURAL:$1|символдан}} артыҡ булмаҫҡа тейеш.",
        "yourgender": "Ҡайһы тасуирлама һеҙгә ҡулайыраҡ?",
-       "gender-unknown": "Ð\9aÒ¯Ñ\80Ò»Ó\99Ñ\82кем ÐºÐ¸Ð»Ð¼Ó\99й",
+       "gender-unknown": "Ð\9fÑ\80огÑ\80амма Ò»ÐµÒ\99Ò\99е Ñ\82елгÓ\99 Ð°Ð»Ò\93анда, Ð¼Ó©Ð¼ÐºÐ¸Ð½ Ð±Ñ\83лһа, ÐµÐ½Ñ\81и-нейÑ\82Ñ\80алÑ\8c Ò»Ò¯Ò\99Ò\99Ó\99Ñ\80 Ò¡Ñ\83лланаÑ\81аҡ",
        "gender-male": "Ул вики биттәрен мөхәррирләй",
        "gender-female": "Ул вики биттәрен мөхәррирләй",
        "prefs-help-gender": "Был көйләүҙе ҡуйыу мотлаҡ түгел.\nПрограмма тәьминәте был мәғлүмәтте һеҙгә грамматика йәһәтенән дөрөҫ мөрәжәғәт итеү өсөн ҡулланасаҡ. \nБыл мәғлүмәт бөтәһенә лә күренәсәк.",
        "email": "Электрон почта",
-       "prefs-help-realname": "Ысын исемегеҙ (теләк буйынса).\nӘгәр уны күрһәтһәгеҙ, битте кемдең төҙәткәнен күрһәткәндә ҡулланыласаҡ.",
+       "prefs-help-realname": "Ысын исемегеҙҙе индереү мотлаҡ түгел.\nӘгәр уны күрһәтһәгеҙ, һеҙҙең эштәрегеҙҙең авторлығын күрһәткәндә, ҡулланыласаҡ.",
        "prefs-help-email": "Электрон почта (теләк буйынса). Күрһәтелгән булһа, ғәмәлдә булған серһүҙегеҙҙе онотҡан осраҡта адресығыҙға яңы серһүҙ ебәреләсәк.\nШулай уҡ башҡа ҡатнашыусылар менән үҙ битегеҙ аша, электрон почтағыҙҙың адресын күрһәтмәйенсә, тура бәйләнешкә инергә мөмкинлек бирә.",
        "prefs-help-email-others": "Ул шулай уҡ башҡа ҡулланыусыларға, шәхси битегеҙҙәге һылтанма аша, һеҙҙән менән бәйләнешкә инергә рөхсәт бирәсәк.\nҺеҙҙең почта адресығыҙ уларға күрһәтелмәйәсәк.",
        "prefs-help-email-required": "Электрон почта адресы кәрәк.",
        "prefs-advancedwatchlist": "Киңәйтелгән көйләүҙәр",
        "prefs-displayrc": "Күренеш көйләүҙәре",
        "prefs-displaywatchlist": "Күренеш көйләүҙәре",
+       "prefs-tokenwatchlist": "Тамға",
        "prefs-diffs": "Айырмалар",
        "prefs-help-prefershttps": "Был көйләү системаға киләһе танылыуҙан һуң ҡулланыласаҡ.",
-       "email-address-validity-valid": "E-mail адрес дөрөҫ булғанға оҡшаған",
-       "email-address-validity-invalid": "Дөрөҫ e-mail адресын керетегеҙ",
+       "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>[[User:$1|$1]]</strong> $2 хоҡуҡтарын үҙгәртеү",
        "userrights-editusergroup": "Ҡулланыусы төркөмдәрен идараларға",
-       "saveusergroups": "Ҡулланыусы төркөмдәрен һаҡларға",
+       "saveusergroups": "{{GENDER:$1|user}} ҡатнашыусының төркөмдәрен һаҡлау",
        "userrights-groupsmember": "Ағза булған төркөмдәр:",
        "userrights-groupsmember-auto": "Йәшерен ағза булған төркөмдәр:",
        "userrights-groups-help": "Был ҡулланыусы кергән төркөмдәрҙе үҙгәртә алаһығыҙ.\n* Әгәр төркөм исеме эргәһендә билдә булһа, ҡулланыусы төркөмгә кергән була.\n* Әгәр билдә булмаһа, ҡулланыусы ул төркөмгә кермәй тимәк.\n* * билдәһе, әгәр төркөмдән ҡулланыусыны юйһағыҙ кире ҡуя алмаясағығыҙҙы аңлата һәм киреһенсә.",
        "group-bot": "Боттар",
        "group-sysop": "Хакимдәр",
        "group-bureaucrat": "Бюрократтар",
-       "group-suppress": "Тикшереүселәр",
+       "group-suppress": "Ð\99Ó\99шереүселәр",
        "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|күÒ\99Ó\99Ñ\82еүсе}}",
+       "group-suppress-member": "{{GENDER:$1|йÓ\99Ñ\88еÑ\80еүсе}}",
        "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}}:Ð\99Ó\99шереүселәр",
        "right-read": "Биттәрҙе ҡарау",
        "right-edit": "Биттәрҙә мөхәррирләү",
        "right-createpage": "Биттәр булдырыу (фекер алышыу биттәре түгел)",
        "right-createtalk": "Фекер алышыу битен яһау",
        "right-createaccount": "Ҡатнашыусыларҙың яңы иҫәп яҙыуҙарын булдырыу",
+       "right-autocreateaccount": "Ҡатнашыусының тышҡы иҫәп яҙмаһы менән инергә",
        "right-minoredit": "Үҙгәртеүҙәрҙе \"Әҙ үҙгәрештәр\" тип билдәләү",
        "right-move": "Биттәрҙең исемен үҙгәртеү",
        "right-move-subpages": "Ҡушымталары менән бергә биттәрҙең исемен алыштырыу",
        "right-deletedtext": "Биттең юйылған өлгөләре араһындағы юйылған текстты һәм үҙгәртеүҙәрҙе ҡарау",
        "right-browsearchive": "Юйылған биттәрҙе эҙләү",
        "right-undelete": "Юйылған биттәрҙе кире ҡайтарыу",
-       "right-suppressrevision": "Биттәрҙең хакимдәрҙән йәшерелгән өлгөләрен ҡарау һәм тергеҙеү",
+       "right-suppressrevision": "Биттең йәшерелгән өлгөләрен ҡарау, йәшереү һәм тергеҙеү",
+       "right-viewsuppressed": "Бөтә ҡатнашыусыларҙан йәшерелгән өлгөләрҙе ҡарау",
        "right-suppressionlog": "Шәхси журналдарҙы ҡарау",
        "right-block": "Башҡа ҡатнашыусыларға мөхәррирләүҙе тыйыу",
        "right-blockemail": "Электрон почтаға хат ебәреүҙе тыйыу",
        "right-hideuser": "Ҡатнашыусы исемен тыйыу һәм йәшереү",
        "right-ipblock-exempt": "IP адрестарҙы бикләүҙе, авто-бикләүҙәрҙе, арауыҡтарҙы бикләүҙе урап үтеү",
-       "right-proxyunbannable": "Прокси серверҙарҙы авто-бикләүҙе урап үтеү",
        "right-unblockself": "Үҙ бигеңде асырға",
        "right-protect": "Биттәрҙең һаҡланыу кимәлен үҙгәртеү һәм баҫҡыслап һаҡланған биттәрҙе төҙәтеү",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" булараҡ һаҡланған биттәрҙе төҙәтеү",
        "right-editsemiprotected": "\"{{int:protect-level-autoconfirmed}}\" булараҡ һаҡланған биттәрҙе төҙәтеү",
+       "right-editcontentmodel": "Биттең контент моделен мөхәррирләү",
        "right-editinterface": "Ҡулланыусы интерфейсын үҙгәртеү",
        "right-editusercssjs": "Башҡа ҡатнашыусыларҙың CSS һәм JS файлдарын мөхәррирләү",
        "right-editusercss": "Башҡа ҡатнашыусыларҙың CSS файлдарын мөхәррирләү",
        "right-override-export-depth": "5-се тәрәнлеккә тиклем бәйле биттәре менән бергә биттәрҙе сығарыу",
        "right-sendemail": "Башҡа ҡатнашыусыларға электрон почта аша хат ебәреү",
        "right-passwordreset": "Серһүҙҙе яңыртыу осраҡтарын ҡарау",
+       "right-managechangetags": "[[Special:Tags|билдәләр]] мәғлүмәттәр базаһында төҙөү һәм юйыу",
+       "grant-group-email": "Электрон хат ебәреү",
+       "grant-editmyoptions": "Һеҙҙең ҡулланыусы көйләүҙәрен мөхәррирләү",
        "newuserlogpage": "Яңы ҡулланыусы яҙмалары",
        "newuserlogpagetext": "Яңы теркәлгән ҡатнашыусылар яҙмалары журналы.",
        "rightslog": "Ҡулланыусының хоҡуҡтары көндәлеге",
        "wlheader-showupdated": "Һеҙҙең аҙаҡҡы кереүегеҙҙән һуң үҙгәргән биттәр '''ҡалын''' шрифт менән күрһәтелгән.",
        "wlnote": "Түбәндә $3 $4 ваҡытына тиклем аҙаҡҡы {{PLURAL:$2|1=сәғәт|'''$2''' сәғәт}} эсендә эшләнгән {{PLURAL:$1|1=үҙгәртеү|'''$1''' үҙгәртеү}} күрһәтелгән.",
        "wlshowlast": "Һуңғы $1 сәғәт $2 көн өсөн күрһәт",
+       "watchlistall2": "бөтә",
        "watchlist-options": "Күҙәтеү исемлеге көйләүҙәре",
        "watching": "Күҙәтеү исемлегенә өҫтәү...",
        "unwatching": "Күҙәтеү исемлегенән сығарыу...",
        "movenosubpage": "Был биткә бер бит тә кермәгән.",
        "movereason": "Сәбәп:",
        "revertmove": "кирегә",
-       "delete_and_move": "Юйырға һәм исемен үҙгәртергә",
        "delete_and_move_text": "==Юйыу талап ителә==\n[[:$1|«$1»]] исемле бит бар инде. Исем үҙгәртеүҙе дауам итеү өсөн, уны юйырға теләйһегеҙме?",
        "delete_and_move_confirm": "Эйе, битте юйырға",
        "delete_and_move_reason": "Исем үҙгәртеүҙе дауам итеү өсөн юйылды «[[$1]]»",
index 95b6bfb..9a66ee9 100644 (file)
@@ -15,7 +15,8 @@
                        "✓",
                        "아라",
                        "Matthias Klostermayr",
-                       "Macofe"
+                       "Macofe",
+                       "George Animal"
                ]
        },
        "tog-underline": "Links unterstreichen:",
        "wlheader-showupdated": "Seiten mid noh néd gseengne Änderrungen wern '''fett''' dorgstöd.",
        "wlnote": "Es {{PLURAL:$1|fóigt d' létzde Änderrung|fóing d' létzden '''$1''' Änderrungen}} voh da/dé {{PLURAL:$2|Stund| '''$2''' Stunden}}. Staund: $3, $4 Uar.",
        "wlshowlast": "Zoag dé Änderrungen voh dé létzden $1 Stunden, $2 Dog óder  (in dé létzden 30 Dog).",
+       "watchlistall2": "olle",
        "watchlist-options": "Mei Beobochta: Optiona",
        "watching": "Beówochten ...",
        "unwatching": "Néd Beówochten",
        "movelogpage": "Vaschiabungs-Logbuach",
        "movereason": "Grund:",
        "revertmove": "zruck vaschiabm",
-       "delete_and_move": "Löschn und vaschiam",
        "delete_and_move_reason": "glöscht, um Plåtz fia Vaschiam zum macha",
        "selfmove": "Ursprungs- und Zielname sand gleich; a Seitn kann net auf sich selber verschom wern.",
        "export": "Seitn exportian",
        "importlogpage": "Import-Logbuach",
        "tooltip-pt-userpage": "Dei Nutzaseitn",
        "tooltip-pt-mytalk": "Dei Dischkriaseitn",
-       "tooltip-pt-preferences": "Deine Preferenzn",
+       "tooltip-pt-preferences": "Deine Preferenzen",
        "tooltip-pt-watchlist": "A Listn vo Seitn, wos du beobochtest",
        "tooltip-pt-mycontris": "A Listn vo de oagna Beidreg",
        "tooltip-pt-login": "Warad schee, wensd di omejdn dadast, es is oba ned zwingend nedig.",
index 2cdbc82..5f51ba9 100644 (file)
        "october-date": "$1 кастрычніка",
        "november-date": "$1 лістапада",
        "december-date": "$1 сьнежня",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|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|налады ў {{GRAMMAR:месны|{{SITENAME}}}}]], калі пажадаеце.",
        "yourname": "Імя ўдзельніка:",
        "remembermypassword": "Запомніць мяне на гэтым кампутары (ня больш за $1 {{PLURAL:$1|дзень|дні|дзён}})",
        "userlogin-remembermypassword": "Запомніць мяне",
        "userlogin-signwithsecure": "Скарыстацца бясьпечным злучэньнем",
+       "cannotloginnow-title": "Цяпер немагчыма ўвайсьці",
+       "cannotloginnow-text": "Уваход у сыстэму немагчымы пры выкарыстаньні $1.",
        "yourdomainname": "Ваш дамэн:",
        "password-change-forbidden": "Вы ня можаце зьмяняць паролі ў гэтай вікі.",
        "externaldberror": "Адбылася памылка аўтэнтыфікацыі з дапамогай вонкавай базы зьвестак, ці Вам не дазволена абнаўляць свой рахунак.",
        "resetpass_submit": "Захаваць пароль і ўвайсьці",
        "changepassword-success": "Ваш пароль быў пасьпяхова зьменены!",
        "changepassword-throttled": "Вы зрабілі зашмат спробаў увайсьці ў сыстэму.\nКалі ласка, пачакайце $1 перад наступнай спробай.",
+       "botpasswords": "Паролі робатаў",
+       "botpasswords-summary": "<em>Паролі робатаў</em> дазваляюць доступ да рахунку ўдзельніка праз API без выкарыстаньня лагіну і паролю асноўнага рахунку. Правы ўдзельніка пры выкарыстаньні паролю робата могуць быць абмежаваныя.\n\nКалі вы ня ведаеце, навошта вам гэта, мабыць, не рабіце гэтага. Ніхто не павінен прасіць вас згенэраваць такі пароль і перадаць гэты пароль яму.",
+       "botpasswords-disabled": "Паролі робатаў адключаныя.",
+       "botpasswords-no-central-id": "Для ўжываньня пароляў робатаў вы мусіце ўвайсьці ў свой глябальны рахунак.",
+       "botpasswords-existing": "Існыя паролі робатаў",
+       "botpasswords-createnew": "Стварыць новы пароль робата",
+       "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-ip": "Нехта (магчыма Вы, з IP-адрасу $1) зрабіў запыт на скіданьне вашага паролю ў {{GRAMMAR:месны|{{SITENAME}}}} ($4). {{PLURAL:$3|1=Наступны рахунак удзельніка зьвязаны|Наступныя рахункі ўдзельнікаў зьвязаныя}} з гэтым адрасам электроннай пошты:\n\n$2\n\n{{PLURAL:$3|1=Гэты часовы пароль будзе|Гэтыя часовыя паролі будуць}} дзейнічаць $5 {{PLURAL:$5|дзень|дні|дзён}}.\nЦяпер Вам неабходна ўвайсьці і выбраць новы пароль. Калі нехта іншы зрабіў гэты запыт, ці Вы ўспомнілі Ваш пачатковы пароль, які ня хочаце мяняць, Вы можаце праігнараваць гэтае паведамленьне, і працягваць выкарыстоўваць стары пароль.",
        "passwordreset-emailtext-user": "Удзельнік $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": "Ð\9aалÑ\96 Ð³Ñ\8dÑ\82Ñ\8b Ð°Ð´Ñ\80аÑ\81 Ñ\8dлекÑ\82Ñ\80оннай Ð¿Ð¾Ñ\88Ñ\82Ñ\8b Ð·Ð°Ñ\80Ñ\8dгÑ\96Ñ\81Ñ\82Ñ\80аванÑ\8b Ð´Ð»Ñ\8f вашага рахунку, тады будзе дасланы ліст пра скідваньне паролю.",
-       "passwordreset-emailsentusername": "Калі зарэгістраваны адпаведны адрас электроннай пошты, тады будзе дасланы ліст пра скідваньне паролю.",
+       "passwordreset-emailsentemail": "Ð\9aалÑ\96 Ð³Ñ\8dÑ\82Ñ\8b Ð°Ð´Ñ\80аÑ\81 Ñ\8dлекÑ\82Ñ\80оннай Ð¿Ð¾Ñ\88Ñ\82Ñ\8b Ð´Ð°Ð»Ñ\83Ñ\87анÑ\8b Ð´Ð° вашага рахунку, тады будзе дасланы ліст пра скідваньне паролю.",
+       "passwordreset-emailsentusername": "Калі ёсьць адрас электроннай пошты, злучаны з гэтым імем удзельніка, тады будзе дасланы ліст пра скідваньне паролю.",
        "passwordreset-emailsent-capture": "Ліст пра скіданьне паролю быў дасланы, што паказана ніжэй.",
        "passwordreset-emailerror-capture": "Ліст пра скіданьне паролю быў створаны і паказаны ніжэй, але не ўдалося адправіць яго {{GENDER:$2|ўдзельніку|ўдзельніцы}}: $1",
        "changeemail": "Зьмяніць або выдаліць адрас электроннай пошты",
        "userrights": "Кіраваньне правамі ўдзельнікаў і ўдзельніц",
        "userrights-lookup-user": "Кіраваньне групамі ўдзельнікаў і ўдзельніц",
        "userrights-user-editname": "Увядзіце імя ўдзельніка:",
-       "editusergroup": "Рэдагаваць групы ўдзельнікаў і ўдзельніц",
+       "editusergroup": "Рэдагаваць групы {{GENDER:$1|ўдзельнікаў і ўдзельніц}}",
        "editinguser": "Зьмена правоў {{GENDER:$1|удзельніка|удзельніцы}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Рэдагаваць групы ўдзельнікаў і ўдзельніц",
-       "saveusergroups": "Захаваць групы ўдзельнікаў і ўдзельніц",
+       "saveusergroups": "Захаваць групы {{GENDER:$1|ўдзельнікаў і ўдзельніц}}",
        "userrights-groupsmember": "Уваходзіць у:",
        "userrights-groupsmember-auto": "Няяўны чалец:",
        "userrights-groups-help": "Тут можна зьмяняць групы, да якіх належыць гэты ўдзельнік:\n* Адзначанае поле побач з назвай групы пазначае прыналежнасьць удзельніка да групы.\n* Пустое поле азначае, што ўдзельнік не належыць да групы.\n* Знак * азначае, што Вы ня зможаце выдаліць удзельніка з групы, калі дададзіце яго да яе, і наадварот.",
        "right-blockemail": "блякаваньне іншых ўдзельнікаў ад дасылкі электроннай пошты",
        "right-hideuser": "блякаваньне рахунку ўдзельніка і яго хаваньне",
        "right-ipblock-exempt": "абход блякаваньняў IP-адрасоў, аўта-блякаваньняў і блякаваньняў дыяпазонаў",
-       "right-proxyunbannable": "абход аўтаматычных блякаваньняў проксі",
        "right-unblockself": "разблякаваньне самога сябе",
        "right-protect": "зьмена ўзроўню абароны старонак і рэдагаваньне каскадна абароненых старонак",
        "right-editprotected": "рэдагаваньне старонак, абароненых у рэжыме «{{int:protect-level-sysop}}»",
        "right-managechangetags": "ствараць і выдаляць [[Special:Tags|меткі]] з базы зьвестак",
        "right-applychangetags": "дадаваць [[Special:Tags|меткі]] пры рэдагаваньні",
        "right-changetags": "дадаваць і выдаляць адвольныя [[Special:Tags|меткі]] да асобных вэрсіяў і запісаў у журнале падзеяў",
+       "grant-createaccount": "Стварыць рахункі",
+       "grant-createeditmovepage": "Ствараць, рэдагаваць і пераносіць старонкі",
+       "grant-delete": "Выдаляць старонкі, вэрсіі і запісы журналу",
+       "grant-editinterface": "Рэдагаваць прасторы назваў МэдыяВікі і CSS/JavaScript удзельніка",
+       "grant-editmycssjs": "Рэдагаваць Ваш CSS/JavaScript",
+       "grant-editmyoptions": "Рэдагаваць Вашыя налады ўдзельніка",
+       "grant-editmywatchlist": "Рэдагаваць ваш сьпіс назіраньня",
+       "grant-editpage": "Рэдагаваць існыя старонкі",
+       "grant-editprotected": "Рэдагаваць абароненыя старонкі",
        "newuserlogpage": "Журнал стварэньня рахункаў",
        "newuserlogpagetext": "Гэта журнал стварэньня рахункаў удзельнікаў і ўдзельніц.",
        "rightslog": "Журнал правоў удзельнікаў",
        "upload-form-label-select-file": "Абраць файл",
        "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": "Назва файлу",
        "foreign-structured-upload-form-label-own-work": "Гэта мая ўласная праца",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Вы таксама можаце скарыстацца [[Special:Upload|старонкай загрузкі {{GRAMMAR:родны|{{SITENAME}}}}]], калі правілы сайту дазваляюць загрузку такога файлу.",
        "foreign-structured-upload-form-2-label-intro": "Дзякуем за ахвяраваньне выявы, якая будзе выкарыстаная ў {{GRAMMAR:месны|{{SITENAME}}}}. Вам варта працягваць, калі яна адпавядае наступным умовам:",
        "foreign-structured-upload-form-2-label-ownwork": "Гэта мусіць быць цалкам <strong>вашая ўласная праца</strong>, а ня проста выява з Інтэрнэту",
+       "foreign-structured-upload-form-2-label-noderiv": "Яна <strong>не павінна ўтрымліваць чужой працы</strong> або быць натхнёнай ёю",
+       "foreign-structured-upload-form-2-label-useful": "Яна павінна быць <strong>адукацыйнай і карыснай</strong> для навучаньня іншых",
+       "foreign-structured-upload-form-2-label-ccbysa": "Вы мусіце быць <strong>згодныя апублікаваць яе ў Інтэрнэце назаўсёды</strong> паводле ліцэнзіі [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
+       "foreign-structured-upload-form-2-label-alternative": "Калі ня ўсё з вышэйпералічанага праўда, вы ўсё яшчэ можаце загрузіць гэты файл з дапамогай [https://commons.wikimedia.org/wiki/Special:UploadWizard майстару загрузкі Вікісховішча], калі файл даступны паводле свабоднай ліцэнзіі.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Калі вы загружаеце файл, вы пацьвярджаеце, што валодаеце аўтарскімі правамі на яго, і згодныя незваротна перадаць гэты файл у Вікісховішча паводле ліцэнзіі Creative Commons Attribution-ShareAlike 4.0, а таксама, што вы згодныя з [https://wikimediafoundation.org/wiki/Terms_of_Use умовамі выкарыстаньня].",
+       "foreign-structured-upload-form-3-label-question-website": "Вы спампавалі гэтую выяву зь нейкага сайту або знайшлі яе праз пошук выяваў?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Вы стварылі гэтую выяву (зрабілі фота, накід малюнку і г. д.) самі?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Ці ўтрымлівае яна або яна натхнёная працай, якой валодае нехта іншы, як прыклад, лягатып?",
+       "foreign-structured-upload-form-3-label-yes": "Так",
+       "foreign-structured-upload-form-3-label-no": "Не",
+       "foreign-structured-upload-form-3-label-alternative": "На жаль, у гэтым выпадку інструмэнт не падтрымлівае загрузку такога файлу. Вы ўсё яшчэ можаце загрузіць яго з дапамогай [https://commons.wikimedia.org/wiki/Special:UploadWizard майстару загрузкі Вікісховішча], пры ўмове, што файл даступны паводле вольнай ліцэнзіі.",
+       "foreign-structured-upload-form-4-label-good": "З дапамогай гэтага інструмэнту вы можаце загрузіць адукацыйную графіку, створаную вамі, а таксама зробленыя вамі фотаздымкі, якія ня ўтрымліваюць працы, што належаць некаму іншаму.",
+       "foreign-structured-upload-form-4-label-bad": "Вы ня можаце загружаць выявы, знойдзеныя ў пошукавых сыстэмах або спампаваныя зь іншых сайтаў.",
        "backend-fail-stream": "Немагчыма накіраваць файл $1.",
        "backend-fail-backup": "Немагчыма зрабіць рэзэрвовую копію файла $1.",
        "backend-fail-notexists": "Файл $1 не існуе.",
        "protectedtitles": "Забароненыя старонкі",
        "protectedtitles-summary": "На гэтай старонцы знаходзіцца сьпіс назваў, якія абароненыя ад стварэньня. Дзеля сьпісу старонак, якія ў цяперашні час абароненыя, глядзіце [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Цяпер няма абароненых назваў з пазначанымі парамэтрамі.",
+       "protectedtitles-submit": "Паказаць загалоўкі",
        "listusers": "Сьпіс удзельнікаў і ўдзельніц",
        "listusers-editsonly": "Паказаць толькі ўдзельнікаў, якія маюць рэдагаваньні",
        "listusers-creationsort": "Адсартаваць па даце стварэньня",
        "usereditcount": "$1 {{PLURAL:$1|рэдагаваньне|рэдагаваньні|рэдагаваньняў}}",
        "usercreated": "{{GENDER:$3|Створаны|Створаная}} $1 у $2",
        "newpages": "Новыя старонкі",
+       "newpages-submit": "Паказаць",
        "newpages-username": "Імя ўдзельніка:",
        "ancientpages": "Найстарэйшыя старонкі",
        "move": "Перанесьці",
        "querypage-disabled": "Гэта спэцыяльная старонка адключаная для падвышэньня прадукцыйнасьці",
        "apihelp": "Даведка API",
        "apihelp-no-such-module": "Модуль «$1» ня знойдзены.",
-       "booksources": "Ð\9fоÑ\88Ñ\83к кніг",
+       "booksources": "Ð\9aÑ\80Ñ\8bнÑ\96Ñ\86Ñ\8b кніг",
        "booksources-search-legend": "Пошук кніг",
        "booksources-isbn": "ISBN:",
        "booksources-search": "Шукаць",
        "specialloguserlabel": "Выканаўца:",
        "speciallogtitlelabel": "Мэта (назва ці {{ns:user}}:імя_ўдзельніка для ўдзельніка):",
        "log": "Журналы падзеяў",
+       "logeventslist-submit": "Паказаць",
        "all-logs-page": "Усе публічныя журналы падзеяў",
        "alllogstext": "Сумесны паказ усіх журналаў падзеяў {{GRAMMAR:родны|{{SITENAME}}}}.\nВы можаце адфільтраваць вынікі па тыпе журналу, удзельніку ці старонцы.",
        "logempty": "Падобных запісаў у журнале няма.",
        "log-title-wildcard": "Шукаць назвы, якія пачынаюцца з гэтага тэксту",
        "showhideselectedlogentries": "Паказаць/схаваць выбраныя запісы ў журнале",
        "log-edit-tags": "Рэдагаваць меткі да абраных запісаў у журнале падзеяў",
+       "checkbox-all": "Усе",
        "allpages": "Усе старонкі",
        "nextpage": "Наступная старонка ($1)",
        "prevpage": "Папярэдняя старонка ($1)",
        "cachedspecial-viewing-cached-ts": "Вы праглядаеце закэшаваную вэрсію старонкі, якая можа быць неактуальнай.",
        "cachedspecial-refresh-now": "Пабачыць апошнюю вэрсію.",
        "categories": "Катэгорыі",
+       "categories-submit": "Паказаць",
        "categoriespagetext": "{{PLURAL:$1|1=Наступная катэгорыя зьмяшчае|Наступныя катэгорыі зьмяшчаюць}} старонкі альбо мэдыяфайлы.\nТут не паказаныя [[Special:UnusedCategories|катэгорыі, якія не выкарыстоўваюцца]].\nГлядзіце таксама [[Special:WantedCategories|сьпіс запатрабаваных катэгорыяў]].",
        "categoriesfrom": "Паказаць катэгорыі, пачынаючы з:",
        "special-categories-sort-count": "сартаваць паводле колькасьці",
        "activeusers-hidebots": "Схаваць робатаў",
        "activeusers-hidesysops": "Схаваць адміністратараў",
        "activeusers-noresult": "Удзельнікі ня знойдзеныя.",
+       "activeusers-submit": "Паказаць актыўных удзельнікаў",
        "listgrouprights": "Правы групаў удзельнікаў",
        "listgrouprights-summary": "Ніжэй пададзены сьпіс групаў удзельнікаў {{GRAMMAR:родны|{{SITENAME}}}}, разам зь іх правамі.\nТаксама можна паглядзець [[{{MediaWiki:Listgrouprights-helppage}}|дадатковую інфармацыю]] пра асабістыя правы.",
        "listgrouprights-key": "Легенда:\n* <span class=\"listgrouprights-granted\">Прызначаныя правы</span>\n* <span class=\"listgrouprights-revoked\">Адабраныя правы</span>",
        "wlshowlast": "Паказаць за апошнія $1 гадзінаў, $2 дзён",
        "watchlistall2": "усё",
        "watchlist-hide": "Схаваць",
+       "watchlist-submit": "Паказаць",
        "wlshowtime": "Пэрыяд часу для паказу:",
        "wlshowhideminor": "дробныя праўкі",
        "wlshowhidebots": "робатаў",
        "wlshowhideanons": "ананімных удзельнікаў",
        "wlshowhidepatr": "патруляваныя праўкі",
        "wlshowhidemine": "мае праўкі",
+       "wlshowhidecategorization": "катэгарызацыю старонак",
        "watchlist-options": "Налады сьпісу назіраньня",
        "watching": "Дадаецца ў сьпіс назіраньня…",
        "unwatching": "Выдаляецца са сьпісу назіраньня…",
        "delete-confirm": "Выдаліць «$1»",
        "delete-legend": "Выдаліць",
        "historywarning": "<strong>Папярэджаньне</strong>: старонка, якую Вы зьбіраецеся выдаліць, мае гісторыю з $1 {{PLURAL:$1|вэрсіі|вэрсіяў|вэрсіяў}}:",
+       "historyaction-submit": "Паказаць",
        "confirmdeletetext": "Зараз Вы выдаліце старонку разам з усёй гісторыяй зьменаў.\nКалі ласка, пацьвердзіце, што Вы зьбіраецеся гэта зрабіць і што Вы разумееце ўсе наступствы, а таксама робіце гэта ў адпаведнасьці з [[{{MediaWiki:Policy-url}}|правіламі]].",
        "actioncomplete": "Дзеяньне выкананае",
        "actionfailed": "Дзеяньне ня выкананае",
        "whatlinkshere-hidelinks": "$1 спасылкі",
        "whatlinkshere-hideimages": "$1 спасылкі на выявы",
        "whatlinkshere-filters": "Фільтры",
+       "whatlinkshere-submit": "Перайсьці",
        "autoblockid": "Аўтаматычнае блякаваньне №$1",
        "block": "Заблякаваць удзельніка",
        "unblock": "Разблякаваць удзельніка",
        "blockip": "Заблякаваць {{GENDER:$1|удзельніка|удзельніцу}}",
        "blockip-legend": "Заблякаваць удзельніка",
-       "blockiptext": "Наступная форма дазваляе заблякаваць магчымасьць рэдагаваньня з пэўнага IP-адрасу альбо імя ўдзельніка. Гэта трэба рабіць толькі дзеля прадухіленьня вандалізму і згодна з [[{{MediaWiki:Policy-url}}|правіламі]]. Пазначце ніжэй дакладную прычыну (напрыклад, пералічыце асобныя старонкі, на якіх былі парушэньні).",
+       "blockiptext": "Наступная форма дазваляе заблякаваць магчымасьць рэдагаваньня з пэўнага IP-адрасу альбо імя ўдзельніка. Гэта трэба рабіць толькі дзеля прадухіленьня вандалізму і згодна з [[{{MediaWiki:Policy-url}}|правіламі]]. Пазначце ніжэй дакладную прычыну (напрыклад, пералічыце асобныя старонкі, на якіх былі парушэньні).\nВы можаце блякаваць IP-дыяпазоны з дапамогай [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-сынтаксысу; найбольшы дазволены дыяпазоны — гэта /$1 для IPv4 і /$2 для IPv6.",
        "ipaddressorusername": "IP-адрас альбо імя ўдзельніка/ўдзельніцы:",
        "ipbexpiry": "Тэрмін:",
        "ipbreason": "Прычына:",
        "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немагчыма перанесьці старонку саму на сябе.",
        "export-download": "Захаваць як файл",
        "export-templates": "Разам з шаблёнамі",
        "export-pagelinks": "Уключыць зьвязаныя старонкі да глыбіні:",
+       "export-manual": "Дадаць старонкі ўручную:",
        "allmessages": "Сыстэмныя паведамленьні",
        "allmessagesname": "Назва",
        "allmessagesdefault": "Тэкст па змоўчаньні",
        "javascripttest-pagetext-frameworks": "Калі ласка, выберыце адну з прапанаваных бібліятэка тэставаньня: $1",
        "javascripttest-pagetext-skins": "Выберыце афармленьне для тэставаньня:",
        "javascripttest-qunit-intro": "Глядзіце [$1 дакумэнтацыю па тэставаньні] на mediawiki.org.",
-       "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-mycontris": "{{GENDER:|Ваш}} унёсак",
        "tooltip-pt-anoncontribs": "Сьпіс рэдагаваньняў, зробленых з гэтага IP-адрасу",
        "tooltip-pt-login": "Вас запрашаюць увайсьці, хаця гэта і неабавязкова.",
        "tooltip-pt-logout": "Выйсьці",
        "tooltip-t-recentchangeslinked": "Апошнія зьмены ў старонках, на якія спасылаецца гэтая старонка",
        "tooltip-feed-rss": "RSS-стужка для гэтай старонкі",
        "tooltip-feed-atom": "Atom-стужка для гэтай старонкі",
-       "tooltip-t-contributions": "Унёсак гэтага ўдзельніка/гэтай удзельніцы",
-       "tooltip-t-emailuser": "Даслаць ліст гэтаму ўдзельніку/гэтай удзельніцы па электроннай пошце",
+       "tooltip-t-contributions": "Унёсак {{GENDER:$1|гэтага ўдзельніка|гэтай удзельніцы}}",
+       "tooltip-t-emailuser": "Даслаць ліст {{GENDER:$1|гэтаму ўдзельніку|гэтай удзельніцы}} па электроннай пошце",
        "tooltip-t-info": "Болей інфармацыі пра гэтую старонку",
        "tooltip-t-upload": "Загрузіць файлы",
        "tooltip-t-specialpages": "Сьпіс усіх спэцыяльных старонак",
        "pageinfo-category-files": "Колькасьць файлаў",
        "markaspatrolleddiff": "Пазначыць як «патруляваную»",
        "markaspatrolledtext": "Пазначыць гэтую старонку як «патруляваную»",
+       "markaspatrolledtext-file": "Пазначыць гэтую вэрсію файлу як патруляваную",
        "markedaspatrolled": "Пазначаная як «патруляваная»",
        "markedaspatrolledtext": "Выбраная вэрсія [[:$1]] пазначаная як «патруляваная».",
        "rcpatroldisabled": "Патруляваньне апошніх зьменаў адключанае",
        "newimages-legend": "Фільтар",
        "newimages-label": "Назва файла (альбо яе частка):",
        "newimages-showbots": "Паказаць загружаныя робатамі",
+       "newimages-hidepatrolled": "Схаваць патруляваныя загрузкі",
        "noimages": "Выявы адсутнічаюць.",
        "ilsubmit": "Шукаць",
        "bydate": "па даце",
        "exif-compression-4": "CCITT Група 4 факсымільнае кадаваньне",
        "exif-copyrighted-true": "Ахоўваецца аўтарскім правам",
        "exif-copyrighted-false": "Статус аўтарскіх правоў ня вызначаны",
+       "exif-photometricinterpretation-1": "Чорны і белы (чорны — 0)",
        "exif-unknowndate": "Невядомая дата",
        "exif-orientation-1": "Звычайная",
        "exif-orientation-2": "Адлюстраваная па гарызанталі",
        "scarytranscludefailed-httpstatus": "[Памылка атрыманьня шаблёну $1: HTTP $2]",
        "scarytranscludetoolong": "[Занадта даўгі URL-адрас]",
        "deletedwhileediting": "'''Увага''': Гэтая старонка была выдаленая пасьля таго, як Вы пачалі яе рэдагаваньне!",
-       "confirmrecreate": "{{GENDER:$1|Удзельнік|Удзельніца}} [[User:$1|$1]] ([[User talk:$1|гутаркі]]) {{GENDER:$1|выдаліў|выдаліла}} гэтую старонку, перад тым як Вы пачалі яе рэдагаваць, з прычыны:\n: ''$2''\nКалі ласка, пацьвердзіце, што Вы сапраўды жадаеце стварыць нанава гэтую старонку.",
+       "confirmrecreate": "{{GENDER:$1|Удзельнік|Удзельніца}} [[User:$1|$1]] ([[User talk:$1|гутаркі]]) {{GENDER:$1|выдаліў|выдаліла}} гэтую старонку, перад тым як Вы пачалі яе рэдагаваць, з прычыны:\n: <em>$2</em>\nКалі ласка, пацьвердзіце, што Вы сапраўды жадаеце стварыць нанава гэтую старонку.",
        "confirmrecreate-noreason": "{{GENDER:$1|Удзельнік|Удзельніца}} [[User:$1|$1]] ([[User talk:$1|гутаркі]]) {{GENDER:$1|выдаліў|выдаліла}} гэтую старонку, пасьля таго як Вы пачалі яе рэдагаваць. Калі ласка, пацьвердзіце, што Вы сапраўды жадаеце стварыць гэтую старонку ізноў.",
        "recreate": "Стварыць ізноў",
        "unit-pixel": "пкс",
        "watchlisttools-raw": "Рэдагаваць як тэкст",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|гутаркі]])",
        "timezone-utc": "UTC",
+       "timezone-local": "Мясцовы",
        "duplicate-defaultsort": "Папярэджаньне: Ключ сартыроўкі па змоўчваньні «$2» замяняе папярэдні ключ сартыроўкі па змоўчваньні «$1».",
        "duplicate-displaytitle": "<strong>Папярэджаньне:</strong> назва для адлюстраваньня «$2» перапісвае ранейшую назву для адлюстраваньня «$1».",
        "invalid-indicator-name": "<strong>Памылка:</strong> атрыбут <code>name</code> індыкатараў статусу старонкі ня мусіць быць пустым.",
        "tags-deactivate": "адключыць",
        "tags-hitcount": "$1 {{PLURAL:$1|зьмена|зьмены|зьменаў}}",
        "tags-manage-no-permission": "Вы ня маеце правоў на зьмену метак.",
+       "tags-manage-blocked": "Вы ня можаце мяняць меткі, калі заблякаваныя.",
        "tags-create-heading": "Стварэньне новай меткі",
        "tags-create-explanation": "Па змоўчаньні, наваствораныя меткі будуць даступныя для выкарыстаньня ўдзельнікамі і робатамі.",
        "tags-create-tag-name": "Назва меткі:",
        "tags-deactivate-not-allowed": "Немагчыма дэактываваць метку «$1».",
        "tags-deactivate-submit": "Адключыць",
        "tags-apply-no-permission": "Вы ня маеце права прымяняць меткі да вашых рэдагаваньняў.",
+       "tags-apply-blocked": "Вы ня можаце мяняць меткі да вашых зьменаў, калі заблякаваныя.",
        "tags-apply-not-allowed-one": "Метка «$1» ня можа быць прызначаная ўручную.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Наступную метку|Наступныя меткі}} нельга дадаваць уручную: $1",
        "tags-update-no-permission": "Вы ня маеце права на дадаваньне ці выдаленьне метак зьменаў для асобных вэрсіяў ці запісаў журналаў.",
+       "tags-update-blocked": "Пакуль Вы заблякаваныя, Вы ня можаце дадаваць і выдаляць меткі зьменаў.",
        "tags-update-add-not-allowed-one": "Метка «$1» ня можа быць дададзеная ўручную.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|1=Наступную метку|Наступныя меткі}} нельга дадаваць уручную: $1",
        "tags-update-remove-not-allowed-one": "Метка «$1» ня можа быць выдаленая.",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "старонка яшчэ не існуе",
        "mw-widgets-titleinput-description-redirect": "перанакіраваньне на $1",
-       "api-error-blacklisted": "Калі ласка, выбярыце іншую, апісальную назву."
+       "api-error-blacklisted": "Калі ласка, выбярыце іншую, апісальную назву.",
+       "randomrootpage": "Выпадковая карэнная старонка"
 }
index 4ae53f9..73012b5 100644 (file)
                        "Mikalai Udodau",
                        "Artificial123",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Goshaproject"
                ]
        },
        "tog-underline": "Падкрэсліваць спасылкі:",
        "tog-hideminor": "Не паказваць дробныя праўкі",
        "tog-hidepatrolled": "Без паказу ўхваленых правак у нядаўніх змяненнях",
        "tog-newpageshidepatrolled": "Без паказу ўхваленых правак у пераліку новых старонак",
+       "tog-hidecategorization": "Схаваць катэгорызацыю старонак",
        "tog-extendwatchlist": "Паказваць усе змяненні, а не толькі апошнія",
        "tog-usenewrc": "Групаваць змены па старонках у апошніх зменах і спісе назірання",
        "tog-numberheadings": "Аўта-нумараваць падзагалоўкі",
        "tog-watchlisthidebots": "Не паказваць праўкі ботаў са спіса назірання",
        "tog-watchlisthideminor": "Не паказваць дробных правак са спіса назірання",
        "tog-watchlisthideliu": "Не паказваць правак зарэгістраваных удзельнікаў у артыкулах са спіса назірання",
+       "tog-watchlistreloadautomatically": "Аўтаматычна перачытваць спіс назірання пры змене фільтра (патрэбен JavaScript)",
        "tog-watchlisthideanons": "Не паказваць ананімных правак у артыкулах са спіса назірання",
        "tog-watchlisthidepatrolled": "Не паказваць ухваленых правак у артыкулах са спіса назірання",
+       "tog-watchlisthidecategorization": "Схаваць катэгорызацыю старонак",
        "tog-ccmeonemails": "Слаць мне копіі маіх лістоў",
        "tog-diffonly": "Не паказваць рэшты старонкі пад розніцай",
        "tog-showhiddencats": "Паказаць схаваныя катэгорыі",
        "october-date": "$1 кастрычніка",
        "november-date": "$1 лістапада",
        "december-date": "$1 снежня",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Катэгорыя|Катэгорыі}}",
        "category_header": "Складнікі ў катэгорыі “$1”",
        "subcategories": "Падкатэгорыі",
        "morenotlisted": "Гэты спіс не поўны.",
        "mypage": "Старонка",
        "mytalk": "Размовы",
-       "anontalk": "Размова для гэтага IP",
+       "anontalk": "Размовы",
        "navigation": "Навігацыя",
        "and": "&#32;і",
        "qbfind": "Знайсці",
        "databaseerror-query": "Запыт: $1",
        "databaseerror-function": "Функцыя: $1",
        "databaseerror-error": "Памылка: $1",
+       "transaction-duration-limit-exceeded": "Каб пазбегнуць вялікай затрымкі пры рэплікацыі, гэта транзакцыя была спынена, бо працягласць запісу ($1) перавысіла ліміт у {{PLURAL:$2|секунду|секунды|секундаў}}.\nКалі вы змяняеце многа элементаў за адзін раз, паспрабуйце замест гэтага зрабіць некалькі невялікіх аперацый.",
        "laggedslavemode": "<strong>Увага:</strong> Старонка можа не ўтрымліваць апошніх змен.",
        "readonly": "База звестак зачынена",
        "enterlockreason": "Упішыце прычыну зачынення, а таксама меркаваны час адчынення",
-       "readonlytext": "Ð\91аза Ð´Ð°Ð½Ñ\8bÑ\85 Ð·Ð°Ñ\80аз Ð·Ð°Ð±Ð»Ð°ÐºÑ\96Ñ\80авана Ð°Ð´ Ð´Ð°Ð±Ð°Ñ\9eленнÑ\8f Ð½Ð¾Ð²Ñ\8bÑ\85 Ð·Ð°Ð¿Ñ\96Ñ\81аÑ\9e Ñ\96 Ñ\96нÑ\88Ñ\8bÑ\85 Ð·Ð¼ÐµÐ½, Ð²ÐµÑ\80агодна, Ð´Ð·ÐµÐ»Ñ\8f Ð¿Ð»Ð°Ð½Ð°Ð²Ð°Ð³Ð° Ð°Ð±Ñ\81лÑ\83гоÑ\9eваннÑ\8f, Ð¿Ð°Ñ\81лÑ\8f Ñ\8fкога Ñ\8fна Ð±Ñ\83дзе Ð²ÐµÑ\80нÑ\83Ñ\82а Ð´Ð° Ð½Ð°Ñ\80малÑ\8cнай Ð¿Ñ\80аÑ\86Ñ\8b.\n\nÐ\90дміністратар, які заблакіраваў базу, растлумачыў гэта так: $1",
+       "readonlytext": "Ð\91аза Ð´Ð°Ð½Ñ\8bÑ\85 Ð·Ð°Ñ\80аз Ð·Ð°Ð±Ð»Ð°ÐºÑ\96Ñ\80авана Ð°Ð´ Ð´Ð°Ð±Ð°Ñ\9eленнÑ\8f Ð½Ð¾Ð²Ñ\8bÑ\85 Ð·Ð°Ð¿Ñ\96Ñ\81аÑ\9e Ñ\96 Ñ\96нÑ\88Ñ\8bÑ\85 Ð·Ð¼ÐµÐ½, Ð²ÐµÑ\80агодна, Ð´Ð·ÐµÐ»Ñ\8f Ð¿Ð»Ð°Ð½Ð°Ð²Ð°Ð³Ð° Ð°Ð±Ñ\81лÑ\83гоÑ\9eваннÑ\8f, Ð¿Ð°Ñ\81лÑ\8f Ñ\8fкога Ñ\8fна Ð±Ñ\83дзе Ð²ÐµÑ\80нÑ\83Ñ\82а Ð´Ð° Ð½Ð°Ñ\80малÑ\8cнай Ð¿Ñ\80аÑ\86Ñ\8b.\n\nСÑ\96Ñ\81Ñ\82Ñ\8dмнÑ\8b Ð°дміністратар, які заблакіраваў базу, растлумачыў гэта так: $1",
        "missing-article": "Не ўдалося знайсці тэксту старонкі ў базе даных, хаця ён мусіць там быць, з назвай \"$1\" $2.\n\nЗвычайна так бывае, калі адкрываюць састарэлую розніцу (diff) або спасылку з гісторыі сцёртай старонкі.\n\nКалі гэта не так, то, магчыма, гэта памылка ў праграмах.\nПаведамце пра гэта, разам з праблемным URL, аднаму з [[Special:ListUsers/sysop|адміністратараў]].",
        "missingarticle-rev": "(версія #: $1)",
        "missingarticle-diff": "(розн.: $1, $2)",
        "readonly_lag": "База даных была аўтаматычна зачынена, каб з ёй маглі ўзгадніцца яе базы-паслядоўнікі",
+       "nonwrite-api-promise-error": "Быў дасланы HTTP-загаловак 'Promise-Non-Write-API-Action', але запыт быў да модуля запісу API.",
        "internalerror": "Унутраная памылка",
        "internalerror_info": "Унутраная памылка: $1",
        "internalerror-fatal-exception": "Фатальнае выключэнне тыпу \"$1\"",
        "title-invalid-empty": "Назва запытанай старонкі пустая ці змяшчае толькі назву прасторы назваў.",
        "title-invalid-utf8": "Назва запытанай старонкі ўтрымлівае недапушчальную ў UTF-8 паслядоўнасць.",
        "title-invalid-interwiki": "Запытаны загаловак зьмяшчае інтэрвікі-спасылку, якую нельга ўжываць у назвах.",
+       "title-invalid-talk-namespace": "Запытаная назва старонкі адпавядае старонцы размоў, якая не можа існаваць.",
        "perfcached": "Гэта ўзятыя з кэшу звесткі, і яны могуць не быць актуальнымі. У кэшы захоўваецца не больш за {{PLURAL:$1|адзін вынік|$1 вынікі|$1 вынікаў}}.",
        "perfcachedts": "Наступныя звесткі кэшаваныя і апошні раз былі абноўленыя $1. У кэшы {{PLURAL:$4|даступны|даступныя}} не больш за $4 {{PLURAL:$4|вынік|вынікі|вынікаў}}.",
        "querypage-no-updates": "Абнаўленне гэтай старонкі цяпер адключана.\nПаказаныя тут звесткі зараз не абновяцца.",
        "createacct-reason": "Прычына",
        "createacct-reason-ph": "Чаму вы ствараеце іншы ўліковы запіс",
        "createacct-submit": "Стварыць уліковы запіс",
-       "createacct-another-submit": "СÑ\82ваÑ\80Ñ\8bÑ\86Ñ\8c Ñ\8fÑ\88Ñ\87Ñ\8d Ð°Ð´Ð·Ñ\96н Ñ\83лÑ\96ковÑ\8b Ð·Ð°Ð¿Ñ\96Ñ\81",
+       "createacct-another-submit": "Стварыць уліковы запіс",
        "createacct-benefit-heading": "{{SITENAME}} зроблены такімі ж людзьмі, як вы.",
        "createacct-benefit-body1": "{{PLURAL:$1|праўка|праўкі|правак}}",
        "createacct-benefit-body2": "{{PLURAL:$1|старонка|старонкі|старонак}}",
        "right-blockemail": "Забараняць удзельніку адсыланне эл.пошты",
        "right-hideuser": "Забараняць імя ўдзельніка і рабіць яго нябачным",
        "right-ipblock-exempt": "Перамагаць забароны на IP, аўта- і дыяпазонныя забароны",
-       "right-proxyunbannable": "Перамагаць аўтаматычныя забароны на проксі",
        "right-unblockself": "Разблакаваць сябе",
        "right-protect": "Мяняць узроўні аховы і правіць старонкі пад каскаднай аховай",
        "right-editprotected": "Правіць старонкі пад аховай \"{{int:protect-level-sysop}}\"",
        "upload-too-many-redirects": "Занадта шмат перасылак за гэтым адрасам (URL)",
        "upload-http-error": "Памылка HTTP: $1",
        "upload-copy-upload-invalid-domain": "Капіраванне загрузак не дазволенае ў гэтым дамене.",
+       "upload-form-label-infoform-description": "Апісанне",
+       "foreign-structured-upload-form-label-infoform-date": "Дата",
        "backend-fail-stream": "Не атрымалася трансляваць файл $1.",
        "backend-fail-backup": "Немагчыма зрабіць рэзервную копію $1.",
        "backend-fail-notexists": "Файл $1 не існуе.",
        "protect-level-sysop": "Толькі для адміністратараў",
        "protect-summary-cascade": "каскад",
        "protect-expiring": "скончыцца $1 (UTC)",
-       "protect-expiring-local": "канчацца $1",
+       "protect-expiring-local": "канчаецца $1",
        "protect-expiry-indefinite": "бясконца",
        "protect-cascade": "Каскад - ахоўваць таксама і ўсе тыя старонкі, які ўлучаюцца ў гэтую.",
        "protect-cantedit": "Вы не можаце змяніць узровень аховы гэтай старонкі, таму што не маеце дазволу правіць яе.",
index 8e03ee5..e7bfb22 100644 (file)
                        "Лорд Бъмбъри",
                        "Matma Rex",
                        "Xð",
-                       "Miroslav35232"
+                       "Miroslav35232",
+                       "Ket"
                ]
        },
        "tog-underline": "Подчертаване на препратките:",
        "tog-hideminor": "Скриване на малки редакции в последните промени",
        "tog-hidepatrolled": "Скриване на патрулираните редакции от списъка с последните промени",
        "tog-newpageshidepatrolled": "Скриване на патрулираните редакции от списъка на новите страници",
+       "tog-hidecategorization": "Скриване на категоризацията на статии",
        "tog-extendwatchlist": "Разширяване на списъка за наблюдение, така че да показва всички промени, не само последните",
        "tog-usenewrc": "Групиране по страници на промените на Последни промени и в списъка за наблюдение",
        "tog-numberheadings": "Автоматично номериране на заглавията",
        "tog-watchlisthidebots": "Скриване на редакциите на ботове в списъка ми за наблюдение",
        "tog-watchlisthideminor": "Скриване на малките промени в списъка ми за наблюдение",
        "tog-watchlisthideliu": "Скриване на редакциите от влезли потребители от списъка за наблюдение",
+       "tog-watchlistreloadautomatically": "Обновяване на списъка за наблюдение всеки път, когато е сменен филтър (изисква се JavaScript)",
        "tog-watchlisthideanons": "Скриване на редакциите от анонимни потребители в списъка за наблюдение",
        "tog-watchlisthidepatrolled": "Скриване на патрулираните редакции от списъка за наблюдение",
+       "tog-watchlisthidecategorization": "Скриване на категоризацията на статии",
        "tog-ccmeonemails": "Получаване на копия на писмата, които пращам на другите потребители",
        "tog-diffonly": "Без показване на съдържанието на страницата при преглед на разлики",
        "tog-showhiddencats": "Показване на скритите категории",
        "morenotlisted": "Този списък не е пълен.",
        "mypage": "Страница",
        "mytalk": "Беседа",
-       "anontalk": "Беседа за адреса",
+       "anontalk": "Беседа",
        "navigation": "Навигация",
        "and": "&#32;и",
        "qbfind": "Търсене",
        "laggedslavemode": "Внимание: Страницата може да не съдържа последните обновявания.",
        "readonly": "Базата от данни е затворена за промени",
        "enterlockreason": "Посочете причина за затварянето, като дадете и приблизителна оценка кога базата от данни ще бъде отново отворена",
-       "readonlytext": "Ð\91азаÑ\82а Ð¾Ñ\82 Ð´Ð°Ð½Ð½Ð¸ Ðµ Ð²Ñ\80еменно Ð·Ð°Ñ\82воÑ\80ена Ð·Ð° Ð¿Ñ\80омени â\80\94 Ð²ÐµÑ\80оÑ\8fÑ\82но Ð·Ð° Ñ\80Ñ\83Ñ\82инна Ð¿Ð¾Ð´Ð´Ñ\80Ñ\8aжка, Ñ\81лед ÐºÐ¾Ñ\8fÑ\82о Ñ\89е Ð±Ñ\8aде Ð¾Ñ\82ново Ð½Ð° Ñ\80азположение.\nАдминистраторът, който я е затворил, дава следното обяснение:\n$1",
+       "readonlytext": "Ð\91азаÑ\82а Ð´Ð°Ð½Ð½Ð¸ Ðµ Ð²Ñ\80еменно Ð·Ð°Ñ\82воÑ\80ена Ð·Ð° Ð¿Ñ\80омени â\80\94 Ð²ÐµÑ\80оÑ\8fÑ\82но Ð·Ð° Ñ\80Ñ\83Ñ\82инна Ð¿Ð¾Ð´Ð´Ñ\80Ñ\8aжка, Ñ\81лед ÐºÐ¾Ñ\8fÑ\82о Ñ\89е Ð±Ñ\8aде Ð¾Ñ\82ново Ð´Ð¾Ñ\81Ñ\82Ñ\8aпна.\nАдминистраторът, който я е затворил, дава следното обяснение:\n$1",
        "missing-article": "В базата от данни не беше открит текста на страницата „$1“ $2.\n\nТова обикновено се случва при последване на остаряла разликова връзка или връзка към историята на междувременно изтрита страница.\n\nАко все пак случаят не е такъв, причината вероятно е софтуерен бъг.\nМоля, докладвайте на [[Special:ListUsers/sysop|администратор]] за проблема, като предоставите уеб адреса за връзка.",
        "missingarticle-rev": "(версия#: $1)",
        "missingarticle-diff": "(Разлика: $1, $2)",
        "directorynotreadableerror": "Директория \"$1\" не може да бъде четена.",
        "filenotfound": "Файлът „$1“ не беше намерен.",
        "unexpected": "Неочаквана стойност: „$1“=„$2“.",
-       "formerror": "Възникна грешка при изпращане на формуляра",
+       "formerror": "Възникна грешка при изпращане на формуляра.",
        "badarticleerror": "Действието не може да бъде изпълнено на тази страница.",
        "cannotdelete": "Указаната страница или файл \"$1\" не можа да бъде изтрит(а). Възможно е вече да е бил(а) изтрит(а) от някой друг.",
        "cannotdelete-title": "Страницата „$1“ не може да бъде изтрита",
-       "delete-hook-aborted": "Изтриването беше прекъснато от кука.\nНе беше посочена причина за това.",
+       "delete-hook-aborted": "Изтриването беше прекъснато от софтуерно прехващане.\nНе беше посочена причина за това.",
        "no-null-revision": "Не може да бъде създадена празна версия на страницата „$1“",
        "badtitle": "Невалидно заглавие",
        "badtitletext": "Желаното заглавие на страница е невалидно, празно или неправилна препратка към друго уики. Възможно е да съдържа знаци, които не са позволени в заглавия.",
+       "title-invalid-empty": "Желаното заглавие на статия е празно или съдържа единствено името на именното пространство.",
        "title-invalid-utf8": "Желаната страница съдържа невалиден низ с кодиране UTF-8",
        "title-invalid-interwiki": "Желаното заглавие на страница съдържа препратка към друго уики, което не може да бъде ползвано в заглавия.",
        "title-invalid-talk-namespace": "Желаното заглавие на страница се отнася към беседа, която не съществува",
        "title-invalid-characters": "Желаното заглавие на статия съдържа невалидни знаци: „$1“",
        "title-invalid-relative": "Заглавието съдържа относителен път. Относителни заглавия на статии (./,../) са невалидни, защото често ще са недостижимо, когато биват извиквани от браузъра на потребителя.",
-       "title-invalid-magic-tilde": "Желаното заглавие на статия съдържа невалидна поредица от вълчнички (<nowiki>~~~</nowiki>).",
+       "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 резултата са допустими и налични}} в складираното копие.",
        "querypage-no-updates": "Обновяването на тази страница в момента е изключено. Засега данните тук няма да бъдат обновявани.",
        "viewsource": "Преглед на кода",
        "viewsource-title": "Преглеждане на кода на $1",
        "actionthrottled": "Ограничение в скоростта",
-       "actionthrottledtext": "Като част от защитата против спам, многократното повтаряне на това действие за кратък период от време е ограничено и вие вече сте надвишили лимита си. Опитайте отново след няколко минути.",
+       "actionthrottledtext": "Като част от защитата против спам, многократното повтаряне на това действие за кратък период от време е ограничено и вие вече сте надвишили лимита. Моля опитайте отново след няколко минути.",
        "protectedpagetext": "Тази страница е защитена, за да се предотвратят редактиране или други действия.",
-       "viewsourcetext": "Можете да разгледате и да копирате кодa на страницата:",
-       "viewyourtext": "Можете да прегледате и копирате изходния код на '''вашите редакции''' на тази страница:",
+       "viewsourcetext": "Можете да разгледате и да копирате кодa на страницата.",
+       "viewyourtext": "Можете да прегледате и копирате изходния код на <strong>вашите редакции</strong> на тази страница.",
        "protectedinterface": "Тази страница съдържа текст, нужен за работата на системата. Тя е защитена против редактиране, за да се предотвратят възможни злоупотреби.\nЗа извършване на промяна за всички уикита, посетете [//translatewiki.net/ translatewiki.net], проектът за локализация на MediaWiki.",
        "editinginterface": "<strong>Внимание:</strong> Редактирате страница, която се използва за текстовия интерфейс на софтуера. Промяната й ще повлияе на външния вид на уикито.",
        "translateinterface": "За да добавите или промените преводи за всички уикита, моля, използвайте [//translatewiki.net/ translatewiki.net], локализиращия проект на МедияУики.",
        "mypreferencesprotected": "Нямате права да редактирате настройките си.",
        "ns-specialprotected": "Специалните страници не могат да бъдат редактирани.",
        "titleprotected": "Тази страница е била защитена срещу създаване от [[User:$1|$1]].\nПосочената причина е ''$2''.",
-       "filereadonlyerror": "ФайлÑ\8aÑ\82 â\80\9e$1â\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¿Ñ\80оменен, Ñ\82Ñ\8aй ÐºÐ°Ñ\82о Ñ\84айловоÑ\82о Ñ\85Ñ\80анилиÑ\89е â\80\9e$2â\80\9c Ðµ Ð² Ñ\80ежим Ñ\81амо Ð·Ð° Ñ\87еÑ\82ене.\n\nÐ\90дминиÑ\81Ñ\82Ñ\80аÑ\82оÑ\80Ñ\8aÑ\82, който го е заключил, е посочил следната причина: „$3“.",
+       "filereadonlyerror": "ФайлÑ\8aÑ\82 â\80\9e$1â\80\9c Ð½Ðµ Ð¼Ð¾Ð¶Ðµ Ð´Ð° Ð±Ñ\8aде Ð¿Ñ\80оменен, Ñ\82Ñ\8aй ÐºÐ°Ñ\82о Ñ\84айловоÑ\82о Ñ\85Ñ\80анилиÑ\89е â\80\9e$2â\80\9c Ðµ Ð² Ñ\80ежим Ñ\81амо Ð·Ð° Ñ\87еÑ\82ене.\n\nСиÑ\81Ñ\82емниÑ\8fÑ\82 Ð°Ð´Ð¼Ð¸Ð½Ð¸Ñ\81Ñ\82Ñ\80аÑ\82оÑ\80, който го е заключил, е посочил следната причина: „$3“.",
        "invalidtitle-knownnamespace": "Невалидно заглавие с именно пространство „$2“ и текст „$3“",
        "invalidtitle-unknownnamespace": "Невалидно заглавие с неразпознато именно пространство номер $1 и текст „$2“",
        "exception-nologin": "Не сте влезли в системата",
        "yourname": "Потребителско име:",
        "userlogin-yourname": "Потребителско име",
        "userlogin-yourname-ph": "Въведете вашето потребителско име",
-       "createacct-another-username-ph": "Ð\92Ñ\8aвежда Ñ\81е Ð¿Ð¾Ñ\82Ñ\80ебиÑ\82елÑ\81коÑ\82о име",
+       "createacct-another-username-ph": "Ð\92Ñ\8aведеÑ\82е Ð¿Ð¾Ñ\82Ñ\80ебиÑ\82елÑ\81ко име",
        "yourpassword": "Парола:",
        "userlogin-yourpassword": "Парола",
        "userlogin-yourpassword-ph": "Въведете вашата парола",
        "createacct-emailrequired": "Адрес за електронна поща",
        "createacct-emailoptional": "Адрес за електронна поща (незадължително)",
        "createacct-email-ph": "Въведете Вашия адрес за електронна поща",
-       "createacct-another-email-ph": "Ð\92Ñ\8aвежда Ñ\81е електронна поща",
+       "createacct-another-email-ph": "Ð\92Ñ\8aведеÑ\82е електронна поща",
        "createaccountmail": "Използване на случайна временна парола, която се изпраща на електронната поща, посочена по-долу",
        "createacct-realname": "Истинско име (незадължително)",
        "createaccountreason": "Причина:",
        "createacct-reason": "Причина",
        "createacct-reason-ph": "Защо създавате друга сметка",
        "createacct-submit": "Създаване на сметката",
-       "createacct-another-submit": "Създаване на друга сметка",
+       "createacct-another-submit": "Създаване на сметка",
        "createacct-benefit-heading": "{{SITENAME}} се създава от хора като вас.",
        "createacct-benefit-body1": "{{PLURAL:$1|редакция|редакции}}",
        "createacct-benefit-body2": "{{PLURAL:$1|страница|страници}}",
        "wrongpassword": "Въведената парола е невалидна. Опитайте отново.",
        "wrongpasswordempty": "Не е въведена парола. Опитайте отново.",
        "passwordtooshort": "Необходимо е паролата да съдържа поне {{PLURAL:$1|1 знак|$1 знака}}.",
+       "passwordtoopopular": "Често използвани пароли не могат да бъдат ползвани. Моля, изберете по-уникална парола.",
        "password-name-match": "Паролата ви трябва да се различава от потребителското ви име.",
        "password-login-forbidden": "Използването на това потребителско име и парола е забранено.",
        "mailmypassword": "Възстановяване на парола",
        "passwordreset-emailtext-ip": "Някой (вероятно вие, от IP адрес $1) поиска възстановяване на паролата за сметката в {{SITENAME}} ($4). За {{PLURAL:$3|следната сметка|следните сметки}}\nе посочен този адрес за електронна поща:\n\n$2\n\n{{PLURAL:$3|Тази временна парола ще бъде активна|Тези временни пароли ще бъдат активни}} {{PLURAL:$5|един ден|$5 дни}}.\nСега би трябвало да влезете в системата и да си изберете нова парола. Ако заявката е направена от друг или пък сте си спомнили паролата и не искате да я променяте, можете да пренебрегнете това съобщение и да продължите да използвате старата си парола.",
        "passwordreset-emailtext-user": "Потребител $1 от {{SITENAME}} поиска възстановяване на паролата за сметката в {{SITENAME}}\n($4). За {{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-emailsentemail": "Ако електронната Ви поща е свързана със сметката Ви, на нея е изпратено писмо за възстановяване на паролата.",
+       "passwordreset-emailsentusername": "Ако това потребителско име е свързано с електронна поща, е изпратено писмо за възстановяване на паролата.",
        "passwordreset-emailsent-capture": "По-долу е показано електронното писмо за възстановяване на паролата, което беше изпратено.",
        "passwordreset-emailerror-capture": "По-долу е показано създадено електронно писмо за възстановяване на паролата, което не беше изпратено на {{GENDER:$2|потребителя}}: $1",
-       "changeemail": "Промяна на адреса за е-поща",
+       "changeemail": "Ð\9fÑ\80омÑ\8fна Ð¸Ð»Ð¸ Ð¿Ñ\80емаÑ\85ване Ð½Ð° Ð°Ð´Ñ\80еÑ\81а Ð·Ð° Ðµ-поÑ\89а",
        "changeemail-header": "Промяна на адреса за е-поща на сметката",
+       "changeemail-passwordrequired": "Трябва да въведете паролата си, за да потвърдите тази промяна.",
        "changeemail-no-info": "За да достъпвате тази страница директно, необходимо е да влезете в системата.",
        "changeemail-oldemail": "Текущ адрес за е-поща:",
        "changeemail-newemail": "Нов адрес за е-поща:",
        "changeemail-password": "Парола за {{SITENAME}}:",
        "changeemail-submit": "Промяна на е-пощата",
        "changeemail-throttled": "Направили сте твърде много опити за влизане в системата. \nМоля, изчакайте $1 преди следващия опит.",
+       "changeemail-nochange": "Моля, въведете различен нов адрес на електронна поща.",
        "resettokens": "Изчистване на маркерите",
        "resettokens-no-tokens": "Няма маркери за изчистване.",
        "resettokens-tokens": "Маркери:",
        "sig_tip": "Вашият подпис заедно с времева отметка",
        "hr_tip": "Хоризонтална линия (използвайте пестеливо)",
        "summary": "Резюме:",
-       "subject": "Тема/заглавие:",
+       "subject": "Ð\97аглавие:",
        "minoredit": "Това е малка промяна",
        "watchthis": "Наблюдаване на страницата",
        "savearticle": "Съхраняване",
        "preview": "Предварителен преглед",
        "showpreview": "Предварителен преглед",
        "showdiff": "Показване на промените",
+       "blankarticle": "<strong>Предупреждение:</strong> Статията, която създавате е празна.\nАко щракнете на „\"{{int:savearticle}}“ отново, статията ще бъде създадена без никакво съдържание.",
        "anoneditwarning": "<strong>Внимание:</strong> Не сте влезли в системата. Ако направите редакция IP-адресът Ви ще бъде публично видим. Ако <strong>[$1 влезете]</strong> или си <strong>[$2 създадете акаунт]</strong>, редакциите Ви ще бъдат свързани с потребителското Ви име, заедно с други преимущества.",
        "anonpreviewwarning": "Внимание: Не сте влезли в системата. Ако съхраните редакцията си, тя ще бъде записана в историята на страницата с вашият IP-адрес.",
        "missingsummary": "'''Напомняне:''' Не е въведено кратко описание на промените. При повторно натискане на бутона „Съхраняване“, редакцията ще бъде съхранена без резюме.",
        "missingcommenttext": "По-долу въведете вашето съобщение.",
-       "missingcommentheader": "'''Напомняне:''' Не е въведено заглавие на коментара.\nПри повторно натискане на \"{{int:savearticle}}\", редакцията ще бъде записана без такова.",
+       "missingcommentheader": "<strong>Напомняне:</strong> Не е въведено заглавие на коментара.\nПри повторно натискане на \"{{int:savearticle}}\", редакцията ще бъде записана без такова.",
        "summary-preview": "Предварителен преглед на резюмето:",
        "subject-preview": "Предварителен преглед на заглавието:",
+       "previewerrortext": "Възникна грешка при опита за преглед на промените.",
        "blockedtitle": "Потребителят е блокиран",
        "blockedtext": "'''Вашето потребителско име (или IP-адрес) беше блокирано.'''\n\nБлокирането е извършено от $1. Посочената причина е: ''$2''\n\n*Начало на блокирането: $8\n*Край на блокирането: $6\n*Блокирането се отнася за: $7\n\nМожете да се свържете с $1 или с някой от останалите [[{{MediaWiki:Grouppage-sysop}}|администратори]], за да обсъдите блокирането.\n\nМожете да използвате услугата „Пращане писмо на потребител“ само ако не ви е забранена употребата й и ако сте посочили валидна електронна поща в [[Special:Preferences|настройките]] си.\n\nВашият IP адрес е $3, а номерът на блокирането е $5. Включвайте едно от двете или и двете във всяко запитване, което правите.",
        "autoblockedtext": "IP-адресът ви беше блокиран автоматично, защото е бил използван от друг потребител, който е бил блокиран от $1.\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, а номерът на блокирането ви е $5. Включвайте ги във всяко питане, което правите.",
        "copyrightwarning": "Обърнете внимание, че всички приноси към {{SITENAME}} се публикуват при условията на $2 (за подробности вижте $1).\nАко не сте съгласни вашата писмена работа да бъде променяна и разпространявана без ограничения, не я публикувайте.<br />\n\nСъщо потвърждавате, че '''вие''' сте написали материала или сте използвали '''свободни ресурси''' — <em>обществено достояние</em> или друг свободен източник.\nАко сте ползвали чужди материали, за които имате разрешение, непременно посочете източника.\n\n<div style=\"font-variant:small-caps\">'''Не публикувайте произведения с авторски права без разрешение!'''</div>",
        "copyrightwarning2": "Обърнете внимание, че всички приноси към {{SITENAME}} могат да бъдат редактирани, променяни или премахвани от останалите сътрудници.\nАко не сте съгласни вашата писмена работа да бъде променяна без ограничения, не я публикувайте.<br />\nСъщо потвърждавате, че '''вие''' сте написали материала или сте използвали '''свободни ресурси''' — <em>обществено достояние</em> или друг свободен източник (за подробности вижте $1).\nАко сте ползвали чужди материали, за които имате разрешение, непременно посочете източника.\n\n<div style=\"font-variant:small-caps\">'''Не публикувайте произведения с авторски права без разрешение!'''</div>",
        "longpageerror": "'''ГРЕШКА: Изпратеният текст е с големина {{PLURAL:$1|един килобайт|$1 килобайта}}, което надвишава позволения максимум от {{PLURAL:$2|един килобайт|$2 килобайта}}.'''\nПоради тази причина той не може да бъде съхранен.",
-       "readonlywarning": "'''ВНИМАНИЕ: Базата от данни беше затворена за поддръжка, затова в момента промените няма да могат да бъдат съхранени.'''\n\nАко желаете, можете да съхраните страницата като текстов файл и да се опитате да я публикувате по-късно.\n\nАдминистраторът, който е затворил базата от данни, е посочил следната причина: $1",
+       "readonlywarning": "<strong>ВНИМАНИЕ: Базата данни беше затворена за поддръжка, затова в момента промените няма да могат да бъдат съхранени.</strong>\n\nАко желаете, можете да съхраните страницата като текстов файл и да се опитате да я публикувате по-късно.\n\nСистемният администратор, който е затворил базата данни, е посочил следната причина: $1",
        "protectedpagewarning": "'''Внимание: Страницата е защитена и само потребители със статут на администратори могат да я редактират.'''\nЗа справка по-долу е показан последният запис от дневниците.",
        "semiprotectedpagewarning": "'''Забележка:''' Тази страница е защитена и само регистрирани потребители могат да я редактират.\nЗа справка по-долу е показан последният запис от дневниците.",
-       "cascadeprotectedwarning": "'''Внимание:''' Страницата е защитена, като само потребители с администраторски права могат да я редактират. Тя е включена в {{PLURAL:$1|следната страница|следните страници}} с каскадна защита:",
+       "cascadeprotectedwarning": "<strong>Внимание:</strong> Страницата е защитена, като само потребители с администраторски права могат да я редактират. Тя е включена в {{PLURAL:$1|следната страница|следните страници}} с каскадна защита:",
        "titleprotectedwarning": "'''Внимание: Тази страница е защитена и са необходими [[Special:ListGroupRights|специални права]], за да бъде създадена.'''\nЗа справка по-долу е показан последният запис от дневниците.",
        "templatesused": "{{PLURAL:$1|Шаблон, използван|Шаблони, използвани}} на страницата:",
        "templatesusedpreview": "{{PLURAL:$1|Шаблон, използван|Шаблони, използвани}} в предварителния преглед:",
        "revdelete-show-file-submit": "Да",
        "logdelete-selected": "{{PLURAL:$1|Избрано събитие|Избрани събития}}:",
        "revdelete-confirm": "Необходимо е да потвърдите, че желаете да извършите действието, разбирате последствията и го правите според [[{{MediaWiki:Policy-url}}|политиката]].",
-       "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n*Неподходяща или неприемлива лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
+       "revdelete-suppress-text": "Премахването трябва да се използва '''само''' при следните случаи:\n* Потенциално уязвима в правно отношение информация\n* Неподходяща лична информация\n*: ''домашни адреси и телефонни номера, номера за социално осигуряване и др.''",
        "revdelete-legend": "Задаване на ограничения:",
        "revdelete-hide-text": "Текст на версията",
        "revdelete-hide-image": "Скриване на файловото съдържание",
        "diff-multi-sameuser": "({{PLURAL:$1|Не е показана една междинна версия|Не са показани $1 междинни версии}} от същия потребител)",
        "diff-multi-otherusers": "({{PLURAL:$1|Не е показана една междинна версия|Не са показани $1 междинни версии}} от {{PLURAL:$2|друг потребител|{{PLURAL:$2|2=двама|3=трима|4=четирима|$2}} потребители}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Не е показана една междинна версия|Не са показани $1 междинни версии}} от повече от {{PLURAL:$2|един потребител|$2 потребители}})",
-       "difference-missing-revision": "{{PLURAL:$2|Не беше открита|Не бяха открити}} {{PLURAL:$2|една версия|$2 версии}} от тази разликова препратка ($1).\n\nТова обикновено се случва, когато е последвана остаряларазликова препратка на страница,коятоебила изтрита.\nПовече подробности могат да бъдат открити в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневника на изтриванията].",
+       "difference-missing-revision": "{{PLURAL:$2|Не беше открита|Не бяха открити}} {{PLURAL:$2|една версия|$2 версии}} от тази разликова препратка ($1).\n\nТова обикновено се случва, когато е последвана остаряла разликова препратка на страница, която е била изтрита.\nПовече подробности могат да бъдат открити в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневника на изтриванията].",
        "searchresults": "Резултати от търсенето",
        "searchresults-title": "Резултати от търсенето за „$1“",
        "titlematches": "Съответствия в заглавията на страници",
        "prefs-watchlist-token": "Уникален идентификатор на списъка за наблюдение:",
        "prefs-misc": "Други",
        "prefs-resetpass": "Промяна на паролата",
-       "prefs-changeemail": "Промяна на е-поща",
+       "prefs-changeemail": "Ð\9fÑ\80омÑ\8fна Ð¸Ð»Ð¸ Ð¿Ñ\80емаÑ\85ване Ð½Ð° Ðµ-поÑ\89а",
        "prefs-setemail": "Настройка на адрес за е-поща",
        "prefs-email": "Настройки за електронната поща",
        "prefs-rendering": "Облик",
        "columns": "Колони:",
        "searchresultshead": "Търсене",
        "stub-threshold": "Праг за форматиране на <a href=\"#\" class=\"stub\">препратки към мъничета</a>:",
+       "stub-threshold-sample-link": "пример",
        "stub-threshold-disabled": "Изключено",
        "recentchangesdays": "Брой дни в последни промени:",
        "recentchangesdays-max": "(най-много $1 {{PLURAL:$1|ден|дни}})",
        "gender-female": "Тя редактира уики страниците",
        "prefs-help-gender": "По желание: използва се за коректно обръщение по род в системните съобщения на софтуера. Тази информация е публично достъпна.",
        "email": "Е-поща",
-       "prefs-help-realname": "* <strong>Истинско име</strong> <em>(незадължително)</em>: Ако го посочите, на него ще бъдат приписани вашите приноси.",
+       "prefs-help-realname": "* Истинското име не е задължително. Ако го посочите, вашите приноси ще бъдат приписани на него.",
        "prefs-help-email": "Електронната поща е незадължителна, но позволява възстановяване на забравена или загубена парола.",
        "prefs-help-email-others": "Можете да изберете да позволите на другите да се свързват с вас по електронна поща, като щракват на препратка от вашата лична потребителска страница или беседа. \nАдресът на електронната ви поща не се разкрива на потребителите, които се свързват с вас по този начин.",
        "prefs-help-email-required": "Изисква се адрес за електронна поща.",
        "right-upload_by_url": "качване на файл от URL адрес",
        "right-purge": "изчистване на складираното съдържание на страниците без показване на страница за потвърждение",
        "right-autoconfirmed": "редактиране на полузащитени страници",
-       "right-bot": "третиране като авоматизиран процес",
+       "right-bot": "третиране като автоматизиран процес",
        "right-nominornewtalk": "Малките промени по дискусионните страници не предизвикват известието за ново съобщение",
        "right-apihighlimits": "използване на крайните предели в API заявките",
        "right-writeapi": "Употреба на API за писане",
        "right-bigdelete": "изтриване на страници с големи редакционни истории",
        "right-deleterevision": "изтриване и възстановяване на отделни версии на страниците",
        "right-deletedhistory": "преглеждане на записи от изтрити редакционни истории без асоциирания към тях текст",
-       "right-deletedtext": "Преглед на изтрития текст и промените между изтрите версии",
+       "right-deletedtext": "Ð\9fÑ\80еглед Ð½Ð° Ð¸Ð·Ñ\82Ñ\80иÑ\82иÑ\8f Ñ\82екÑ\81Ñ\82 Ð¸ Ð¿Ñ\80омениÑ\82е Ð¼ÐµÐ¶Ð´Ñ\83 Ð¸Ð·Ñ\82Ñ\80иÑ\82иÑ\82е Ð²ÐµÑ\80Ñ\81ии",
        "right-browsearchive": "търсене на изтрити страници",
        "right-undelete": "възстановяване на страници",
        "right-suppressrevision": "преглед и възстановяване на версии, скрити от администраторите",
        "right-blockemail": "блокиране на потребители да изпращат писма по е-поща",
        "right-hideuser": "блокиране и скриване на потребителско име",
        "right-ipblock-exempt": "пренебрегване на блокирания по IP blocks, автоматични блокирания и блокирани IP интервали",
-       "right-proxyunbannable": "пренебрегване на автоматичното блокиране на проксита",
        "right-unblockself": "Собствено отблокиране",
        "right-protect": "променяне на нивото на защита и редактиране на защитени страници",
        "right-editprotected": "редактиране на защитени страници (без каскадна защита)",
-       "right-editinterface": "Ñ\80едакÑ\82иÑ\80ане Ð½Ð° Ð¸Ð½Ñ\82еÑ\80Ñ\84ейÑ\81а",
+       "right-editinterface": "Ñ\80едакÑ\82иÑ\80ане Ð½Ð° Ð¿Ð¾Ñ\82Ñ\80ебиÑ\82елÑ\81киÑ\8f Ð¸Ð½Ñ\82еÑ\80Ñ\84ейÑ\81",
        "right-editusercssjs": "редактиране на CSS и JS файловете на други потребители",
        "right-editusercss": "редактиране на CSS файловете на други потребители",
        "right-edituserjs": "редактиране на JS файловете на други потребители",
        "boteditletter": "б",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|наблюдаващ потребител|наблюдаващи потребители}}]",
        "rc_categories": "Само от категории (разделител „|“)",
-       "rc_categories_any": "Която и да е",
+       "rc_categories_any": "Която и да е от избраните",
        "rc-change-size-new": "$1 {{PLURAL:$1|байт|байта}} след редакцията",
        "newsectionsummary": "Нова тема /* $1 */",
        "rc-enhanced-expand": "Показване на детайли",
        "upload-misc-error-text": "Неизвестна грешка при качване. Убедете се, че адресът е верен и опитайте отново. Ако отново имате проблем, обърнете се към [[Special:ListUsers/sysop|администратор]].",
        "upload-too-many-redirects": "Адресът съдържа твърде много пренасочвания",
        "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-select-file": "Избиране на файл",
+       "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": "Име на файл",
+       "foreign-structured-upload-form-label-own-work": "Това е моя собствена творба",
+       "foreign-structured-upload-form-label-infoform-categories": "Категории",
+       "foreign-structured-upload-form-label-infoform-date": "Дата",
+       "foreign-structured-upload-form-label-own-work-message-local": "Потвърждавам, че качвам този файл в съответствие с правилата и лицензионната политика на сайта {{SITENAME}}.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "Ако не можете да заредите този файл в съответствие с правилата на сайта {{SITENAME}}, моля, затворете този прозорец и опитайте друг метод.",
        "foreign-structured-upload-form-3-label-yes": "Да",
        "foreign-structured-upload-form-3-label-no": "Не",
        "backend-fail-notexists": "Файлът $1 не съществува.",
        "uploadnewversion-linktext": "Качване на нова версия на файла",
        "shared-repo-from": "от $1",
        "shared-repo": "споделено хранилище",
-       "upload-disallowed-here": "Ð\9dе Ð¼Ð¾Ð¶ÐµÑ\82е Ð´Ð° Ð¿Ñ\80епокÑ\80иете файла.",
+       "upload-disallowed-here": "Ð\9dе Ð¼Ð¾Ð¶ÐµÑ\82е Ð´Ð° Ð¿Ñ\80езапиÑ\88ете файла.",
        "filerevert": "Възвръщане на $1",
        "filerevert-legend": "Възвръщане на файла",
        "filerevert-intro": "Възвръщане на '''[[Media:$1|$1]]''' към [$4 версията от $3, $2].",
        "mywatchlist": "Списък за наблюдение",
        "watchlistfor2": "За $1 $2",
        "nowatchlist": "Списъкът ви за наблюдение е празен.",
-       "watchlistanontext": "За преглеждане и редактиране на списъка за наблюдение се изисква $1 в системата.",
+       "watchlistanontext": "За преглеждане и редактиране на списъка за наблюдение се изисква влизане в системата.",
        "watchnologin": "Не сте влезли",
        "addwatch": "Добавяне към списъка за наблюдение",
-       "addedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e'''[[:$1]]'''â\80\9c Ð±ÐµÑ\88е Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð° ÐºÑ\8aм [[Special:Watchlist|Ñ\81пиÑ\81Ñ\8aка Ð²Ð¸ Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение]].\nÐ\9dейниÑ\82е Ð±Ñ\8aдеÑ\89и Ð¿Ñ\80омени, ÐºÐ°ÐºÑ\82о Ð¸ Ð½Ð° Ñ\81Ñ\8aоÑ\82веÑ\82наÑ\82а Ð¹ Ð´Ð¸Ñ\81кÑ\83Ñ\81ионна Ñ\81Ñ\82Ñ\80аниÑ\86а, Ñ\89е Ñ\81е Ð¾Ð¿Ð¸Ñ\81ваÑ\82 Ñ\82ам.",
+       "addedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e'''[[:$1]]'''â\80\9c Ð¸ Ð±ÐµÑ\81едаÑ\82а Ð¹ Ð±Ñ\8fÑ\85а Ð´Ð¾Ð±Ð°Ð²ÐµÐ½Ð¸ ÐºÑ\8aм [[Special:Watchlist|Ñ\81пиÑ\81Ñ\8aка Ð²Ð¸ Ð·Ð° Ð½Ð°Ð±Ð»Ñ\8eдение]].",
        "addedwatchtext-short": "Страницата „$1“ беше добавена към списъка Ви за наблюдение.",
        "removewatch": "Премахване от списъка за наблюдение",
-       "removedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e[[:$1]]â\80\9c Ð±ÐµÑ\88е Ð¿Ñ\80емаÑ\85наÑ\82а от [[Special:Watchlist|списъка ви за наблюдение]].",
+       "removedwatchtext": "СÑ\82Ñ\80аниÑ\86аÑ\82а â\80\9e[[:$1]]â\80\9c Ð¸ Ð±ÐµÑ\81едаÑ\82а Ð¹ Ð±Ñ\8fÑ\85а Ð¿Ñ\80емаÑ\85наÑ\82и от [[Special:Watchlist|списъка ви за наблюдение]].",
        "removedwatchtext-short": "Страницата „$1“ беше премахната от списъка Ви за наблюдение.",
        "watch": "Наблюдение",
        "watchthispage": "Наблюдаване на страницата",
        "wlshowlast": "Показване на последните $1 часа $2 дни",
        "watchlistall2": "всички",
        "watchlist-hide": "Скриване",
+       "watchlist-submit": "Показване",
        "wlshowtime": "Период от време за показване:",
+       "wlshowhideminor": "малки промени",
+       "wlshowhidebots": "ботове",
+       "wlshowhideliu": "регистрирани потребители",
+       "wlshowhideanons": "анонимни потребители",
+       "wlshowhidepatr": "патрулирани редакции",
+       "wlshowhidemine": "моите редакции",
        "watchlist-options": "Опции на списъка за наблюдение",
        "watching": "Наблюдение…",
        "unwatching": "Спиране на наблюдение…",
        "deletepage": "Изтриване",
        "confirm": "Потвърждаване",
        "excontent": "съдържанието беше: „$1“",
-       "excontentauthor": "съдържанието беше: „$1“ (като единственият автор беше [[Special:Contributions/$2|$2]])",
+       "excontentauthor": "съдържанието беше: „$1“, като единственият автор беше \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|беседа]])",
        "exbeforeblank": "премахнато преди това съдържание: „$1“",
        "delete-confirm": "Изтриване на „$1“",
        "delete-legend": "Изтриване",
-       "historywarning": "'''Внимание:''' Страницата, която възнамерявате да изтриете, има история с приблизително $1 {{PLURAL:$1|редакция|редакции}}:",
+       "historywarning": "<strong>Внимание:</strong> Страницата, която възнамерявате да изтриете, има история с приблизително $1 {{PLURAL:$1|редакция|редакции}}:",
        "historyaction-submit": "Показване",
        "confirmdeletetext": "На път сте безвъзвратно да изтриете страница или файл, заедно с цялата прилежаща редакционна история, от базата от данни.\nПотвърдете, че искате това, разбирате последствията и правите това в съответствие с [[{{MediaWiki:Policy-url}}|линията на поведение]].",
        "actioncomplete": "Действието беше изпълнено",
        "rollback-success": "Отменени редакции на $1; възвръщане към последната версия на $2.",
        "sessionfailure-title": "Прекъсната сесия",
        "sessionfailure": "Изглежда има проблем със сесията ви; действието беше отказано като предпазна мярка срещу крадене на сесията. Натиснете бутона за връщане на браузъра, презаредете страницата, от която сте дошли, и опитайте отново.",
+       "changecontentmodel-title-label": "Заглавие на страницата",
+       "changecontentmodel-reason-label": "Причина:",
        "protectlogpage": "Дневник на защитата",
        "protectlogtext": "Списък на промените в защитата за страницата.\nМожете да прегледате и [[Special:ProtectedPages|списъка на текущо защитените страници]].",
        "protectedarticle": "защити „[[$1]]“",
        "unblocked": "[[User:$1|$1]] беше отблокиран.",
        "unblocked-range": "$1 беше отблокиран",
        "unblocked-id": "Блок № $1 беше премахнат",
+       "unblocked-ip": "[[Special:Contributions/$1|$1]] е отблокиран.",
        "blocklist": "Блокирани потребители",
        "ipblocklist": "Блокирани потребители",
        "ipblocklist-legend": "Откриване на блокиран потребител",
        "movenotallowedfile": "Нямате права да премествате файлове.",
        "cant-move-user-page": "Нямате нужните права на достъп, за да местите потребителски страници (можете да местите само подстраници).",
        "cant-move-to-user-page": "Нямате нужните права на достъп, за да извършвате преместване на страници върху потребителски страници (можете да местите само върху подстраници от потребителското пространство).",
-       "newtitle": "Ð\9aÑ\8aм Ð½ово заглавие:",
+       "newtitle": "Ð\9dово заглавие:",
        "move-watch": "Наблюдаване на страницата",
        "movepagebtn": "Преместване",
        "pagemovedsub": "Преместването беше успешно",
        "export-download": "Съхраняване като файл",
        "export-templates": "Включване на шаблоните",
        "export-pagelinks": "Включване на свързаните страници с дълбочина до:",
+       "export-manual": "Добавяне на страниците ръчно:",
        "allmessages": "Системни съобщения",
        "allmessagesname": "Име",
        "allmessagesdefault": "Текст по подразбиране",
        "tooltip-pt-anontalk": "Дискусия относно редакциите от този адрес",
        "tooltip-pt-preferences": "Вашите настройки",
        "tooltip-pt-watchlist": "Списък на страници, чиито промени сте избрали да наблюдавате",
-       "tooltip-pt-mycontris": "Списък на вашите приноси",
+       "tooltip-pt-mycontris": "Списък на {{GENDER:|вашите}} приноси",
+       "tooltip-pt-anoncontribs": "Списък на промените, направени от този IP адрес",
        "tooltip-pt-login": "Насърчаваме Ви да влезете, въпреки че не е задължително.",
        "tooltip-pt-logout": "Излизане от {{SITENAME}}",
        "tooltip-pt-createaccount": "Насърчаваме Ви да си създадете сметка и да влезете, въпреки че не е задължително.",
        "tooltip-n-currentevents": "Информация за текущи събития",
        "tooltip-n-recentchanges": "Списък на последните промени в уикито",
        "tooltip-n-randompage": "Зареждане на случайна страница",
-       "tooltip-n-help": "Място където може да се информирате",
+       "tooltip-n-help": "Място, където може да се информирате",
        "tooltip-t-whatlinkshere": "Списък на всички страници, сочещи насам",
        "tooltip-t-recentchangeslinked": "Последните промени на страници, сочени от тази страница",
        "tooltip-feed-rss": "RSS feed за страницата",
        "tooltip-feed-atom": "Atom feed за страницата",
-       "tooltip-t-contributions": "Показване на приносите на потребителя",
-       "tooltip-t-emailuser": "Изпращане на писмо до потребителя",
+       "tooltip-t-contributions": "Показване на приносите на {{GENDER:$1|потребителя}}",
+       "tooltip-t-emailuser": "Изпращане на писмо до {{GENDER:$1|потребителя}}",
        "tooltip-t-info": "Повече за тази страница",
        "tooltip-t-upload": "Качи файлове",
        "tooltip-t-specialpages": "Списък на всички специални страници",
        "version-libraries": "Инсталирани библиотеки",
        "version-libraries-library": "Библиотека",
        "version-libraries-version": "Версия",
+       "version-libraries-license": "Лиценз",
+       "version-libraries-description": "Описание",
+       "version-libraries-authors": "Автори",
        "redirect-submit": "Отваряне",
        "redirect-value": "Стойност:",
        "redirect-user": "Потребителски номер",
        "revdelete-uname-hid": "скрито потребителско име",
        "revdelete-restricted": "добавени ограничения за администраторите",
        "revdelete-unrestricted": "премахнати ограничения за администраторите",
+       "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-move-move-noredirect": "$1 {{GENDER:$2|премести}} страницата „$3“ като „$4“ без пренасочване",
        "logentry-move-move_redir": "$1 {{GENDER:$2|премести}} страницата $3 като $4 (върху пренасочване)",
        "mediastatistics-table-mimetype": "MIME тип",
        "mediastatistics-header-audio": "Аудио",
        "mediastatistics-header-video": "Видео",
+       "mediastatistics-header-total": "Всички файлове",
        "json-error-syntax": "Синтактична грешка",
        "headline-anchor-title": "Препратка към този раздел",
        "special-characters-group-latin": "Латиница",
        "special-characters-title-endash": "средно тире",
        "special-characters-title-emdash": "дълго тире",
        "special-characters-title-minus": "знак минус",
+       "mw-widgets-dateinput-no-date": "Нищо не е избрано",
        "mw-widgets-dateinput-placeholder-day": "ГГГГ-ММ-ДД",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
-       "api-error-blacklisted": "Моля, изберете различно, описателно заглавие."
+       "mw-widgets-titleinput-description-new-page": "страницата все още не съществува",
+       "mw-widgets-titleinput-description-redirect": "пренасочване към $1",
+       "api-error-blacklisted": "Моля, изберете различно, описателно заглавие.",
+       "randomrootpage": "Случайна начална страница"
 }
index ac0204f..959ef9f 100644 (file)
@@ -5,7 +5,8 @@
                        "Ibrahim khashrowdi",
                        "Rachitrali",
                        "Mjbmr",
-                       "Macofe"
+                       "Macofe",
+                       "Hosseinblue"
                ]
        },
        "tog-underline": "لینکانی جهلگا خط کشیتین",
        "passwordreset-email": "ایمیل ادرس:",
        "passwordreset-emailtitle": "حسابئ مئلومات بی {{SITENAME}}",
        "passwordreset-emailelement": "کار زوروکئ نام: \n$1\n\nموقت ئین چیهرگال: \n$2",
-       "passwordreset-emailsent": "یک ایمیل په چیهر گالئ  پاک بوتین  خاتیرا دیم داته بوت.",
+       "passwordreset-emailsentemail": "یک ایمیل په چیهر گالئ  پاک بوتین  خاتیرا دیم داته بوت.",
        "passwordreset-emailsent-capture": "یک ایمیلئ په بیئرگردینتین ئا پاسوردئ خاتیرا، دیم داته بوت.",
        "changeemail": "ایمیل ادرسئ تغیر داتین یا پاک کورتین",
        "changeemail-no-info": "په ای تاکدیمی دسترسی ئی خاتیرا داخل بئیت.",
        "prefs-diffs": "تفاوت‌ئان",
        "prefs-help-prefershttps": "ای تنظیمی تاثیر پد شه لوگین بوتینا بی شما اعمال ئه بیئت.",
        "prefswarning-warning": "شمی تغیرات بی تنظیماتانی تا تا انون ذخیره نه بوته انت.\nاگه ای تاکدیما بي شه کلیک کورتین به  «$1» ئا بیلیئت شمی تنظیمات ذخیره ئه نه بیئنت.",
-       "email-address-validity-valid": "شمی ایمیلئ ادرس موتبر بی نظر ئه رسیت",
-       "email-address-validity-invalid": "موتبرین ایمیل ادرسی داخل بکنیت",
        "userrights": "کار گیروکی اختیارانی مدیریت",
        "userrights-lookup-user": "کار گیروکی گروپانی مدیریت",
        "userrights-user-editname": "کار زوروکین ناما داخل بکنیت:",
        "imagelinks": "بی کار گیپتین فایلئ",
        "linkstoimage": "{{PLURAL:$1|تاکدیم|تاکدیمان}} جهلگین بی اکسا لینک {{PLURAL:$1|داریت|دارنت}}:",
        "linkstoimage-more": "گیشتیر شه $1 تاکدیم گۆ ای فایلا لینک {{PLURAL:$1|داریت|دارنت}}.\nجهلگی لڑ تانا {{PLURAL:$1|اولین لینک|اولین $1 لینک}} گۆ ای دیما نشان ئا دنت.\n[[Special:WhatLinksHere/$2|کامیلین لیست]] هم موجود اینت.",
-       "nolinkstoimage": "اÛ\8c Ù\81اÛ\8cÙ\84 Ø¨Ø¦ Ù\87Û\8cÚ\86 ØªØ§Ú©Ø¯Û\8cÙ\85Û\8c ØªØ§ Ø¨Ø¦ کار گیپته نه بوته.",
+       "nolinkstoimage": "Ø´Ù\87 Ø§Û\8c Ù\81اÛ\8cÙ\84ا Ø¨Ù\90Ù\87 Ù\87Û\8cÚ\86 ØªØ§Ú©Ø¯Û\8cÙ\85ئ ØªØ§ کار گیپته نه بوته.",
        "morelinkstoimage": "ای فایلئ [[Special:WhatLinksHere/$1|دیگرین لینکانا]] بگیندیت.",
        "linkstoimage-redirect": "$1 (فایلی تغیرمسیر) $2",
        "duplicatesoffile": "جهلگی{{PLURAL:$1|فایل|فایلان}} تکرارین نخسه شه ای فایلا  {{PLURAL:$1|است|هستنت}} ([[Special:FileDuplicateSearch/$2|گیشتیرین مئلومات]]):",
        "statistics-users-active": "پئال ئین کار زوروکان",
        "statistics-users-active-desc": "آ کار زوروکان که بئ {{PLURAL:$1|رۆچ|$1 رۆچ}} دیما پئالیت شه وت نشان داته انت",
        "pageswithprop": "تاکدیم گۆ تاکدیمانئ خاصیتا",
-       "pageswithprop-legend": "تاکدیم گۆ تاکدیمانئ خاصیتا",
+       "pageswithprop-legend": "تاکدیمان گۆ تاکدیمئ خاصیتان",
        "pageswithprop-prop": "خاصیت ئی نام:",
        "pageswithprop-submit": "برا",
        "pageswithprop-prophidden-long": "($1) ئی تچکین متنی چیهرین جزییات",
        "brokenredirectstext": "جهلگین تغییرمسیرئان بئ یک ناموجودین دیمی لینک دارنت:",
        "brokenredirects-edit": "ایڈیٹ",
        "brokenredirects-delete": "پاک کورتین",
-       "withoutinterwiki": "تاکدیمان بی شه زبانئ لینک ئان",
+       "withoutinterwiki": "تاکدیمان که مانجین ویکی ئي لینک ندارنت",
        "withoutinterwiki-legend": "دیموند",
        "withoutinterwiki-submit": "نشان داتین",
        "fewestrevisions": "مقاله ئان گۆ کم ئین ایڈیٹ ئی اندازگ ئا",
        "allmessages-prefix": "فیلتر کورتین بئ اساس پدوند:",
        "allmessages-language": "زبان:",
        "allmessages-filter-submit": "برا",
-       "allmessages-filter-translate": "ترجمه",
+       "allmessages-filter-translate": "چاوواشەکِردن زوون",
        "thumbnail-more": "ٹُوه کورتین",
        "filemissing": "فایل وجود نداریت",
        "thumbnail_error": "خطا بئ ناحُنی ئی جۆڑ کورتین ئی وختا: $1",
        "thumbnail-temp-create": "نتوان که موقتین ناحُنی ئین فایلی جۆڑ کورت",
        "thumbnail-dest-create": "نه توان که ناحُنی ئین اکس ئا بئ وتي مخصد ئی جاه تا ذخیره کورت",
        "thumbnail_image-missing": "بی نظر ئه رسیت فایل زیان بوته: $1",
-       "import": "تاکدیمانێ بێ تێ کورتین",
+       "import": "تاکدیماني تها کورتین",
        "importinterwiki": "بي تئ رییتین ترانس ویکی ئی",
        "import-interwiki-sourcewiki": "ویکی زێ منشا:",
        "import-interwiki-sourcepage": "تاکدیمئ منشا:",
        "filedeleteerror-short": "خطا بئ فایلی پاک کورتین: $1",
        "filedeleteerror-long": "بی پدا  پاک کورتین ئی وختا خطا رخ دات:\n\n$1",
        "previousdiff": "→دیمتیرین ئی فرق",
-       "nextdiff": "نۆکتیرین ئی فرق ←",
+       "nextdiff": "نۆکتیرین فرق ←",
        "mediawarning": "'''هشدار''': ای فایل ممکن اینت که شه خراب ئین کودئان داشته بئیت .\nگۆ آوانی اجرا کورتین ئا ممکن اینت که بئ شمی کمپیوترا تاوان برسیت.",
        "thumbsize": "ناهُنی ئین بند ئی اندازه گ:",
        "widthheightpage": "$1×$2، $3 {{PLURAL:$3|تاکدیم|تاکدیم}}",
        "sp-newimages-showfrom": "نشان‌داتین نۆکین اکسانی شه $2، $1 بئ بعد",
        "seconds": "{{PLURAL:$1|$1ثانیه| $1  ثانیه}}",
        "minutes": "{{PLURAL:$1|دقیقه|دقیقه}}",
-       "hours": "{{PLURAL:$1|سائت|سائت}}",
+       "hours": "{{PLURAL:$1|ساعت|ساعت}}",
        "days": "{{PLURAL:$1|روچ|روچ}}",
        "weeks": "{{PLURAL:$1|$1 هپتگ|$1 هپتگ ئان}}",
        "months": "{{PLURAL:$1|$1 ماه|}}",
        "confirm-unwatch-top": "ای دیم شه شمئ دیدارلیست ئا پاک بیئت؟",
        "semicolon-separator": "؛&#32;",
        "quotation-marks": "\"$1\"",
-       "imgmultipageprev": "&rarr; دیمتیری تاکدیم",
+       "imgmultipageprev": "&rarr; دیمتیرین تاکدیم",
        "imgmultipagenext": "بئدین تاکدیم &larr;",
        "imgmultigo": "برا!",
        "imgmultigoto": "شوتین بی $1 تاکدیمی تا",
        "version-skins": "نصب بوته ئین پوسته ئانی",
        "version-specialpages": "خاصین تاکدیمان",
        "version-parserhooks": "تجزیه گرین چنگک ئان",
-       "version-variables": "موته غیرئان",
+       "version-variables": "موتغیرئان",
        "version-antispam": "دیمگیری شه سپم ئان",
        "version-other": "دیگرین",
        "version-mediahandlers": "می\tڈیایی بئ دست گیروک ئان",
index 7aaaff9..a46373b 100644 (file)
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण",
        "createacct-reason-ph": "रउआ एगो अन्य खाता काहे बना रहल बानी",
-       "createacct-captcha": "सुरक्षा जाँच",
-       "createacct-imgcaptcha-ph": "उपर लिखल पाठ लिखीं",
        "createacct-submit": "आपन खाता बनाईं",
        "createacct-another-submit": "एगो दोसर खाता बनाईं",
        "createacct-benefit-heading": "{{SITENAME}} रउआ जइसन लोगन द्वारा बनावल गईल बा।",
        "passwordreset-emailtext-ip": "केहु (शायद रउए, $1 आइ॰पी पता से) {{SITENAME}} ($4) पर आपन {{PLURAL:$3|गुप्तशब्द}} के रीसेट करे के अनुरोध कईले बानी। इ ई-मेल पता से निम्न {{PLURAL:$3|खाता जुड़ल बा}}:\n\n$2\n\n{{PLURAL:$3|इ}} अस्थायी गुप्तशब्द {{PLURAL:$5|एक दिन|$5 दिन}} के बाद काम ना करी। रउआ खाता में प्रवेश करके एगो नया गुप्तशब्द अभी चुन लेवे के चाहीं। यदि इ अनुरोध केहु अउर कइले बा, या फिर रउआ आपन मूल गुप्तशब्द याद आ गईल बा, अउर आप {{PLURAL:$3|आपन}} गुप्तशब्द नइखी बदले के चाहत त, रउआ इ संदेश के अनदेखा कर के आपन पुरानका गुप्तशब्द के प्रयोग जारी रख सकत बानी।",
        "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-emailsent": "एगो गुप्तशब्द रिसेट ई-मेल भेजल जा चुकल बा।",
+       "passwordreset-emailsentemail": "एगो गुप्तशब्द रिसेट ई-मेल भेजल जा चुकल बा।",
        "passwordreset-emailsent-capture": "नीचे दिखावल गईल गुप्तशब्द पुनर्स्थापना ई-मेल भेज दिहल गईल बा।",
        "passwordreset-emailerror-capture": "नीचे दिखावल गईल गुप्तशब्द पुनर्स्थापना ई-मेल उत्पन्न करल गईल रहल, परंतु उ के {{GENDER:$2|सदस्य}} के भेजे के क्रिया असफल रहल।\nत्रुटि: $1",
        "changeemail": "ई-मेल पता बदलीं",
-       "changeemail-text": "आपन ई-मेल पता बदले खातिर इ फॉर्म के भरीं। इ बदलाव के पुष्टे करे खातिर रउआ आपन गुप्तशब्द पुनः लिखे के पड़ी।",
+       "changeemail-header": "खाता के ई-मेल पता बदलीं",
        "changeemail-no-info": "इ पन्ना के सिधे प्रयोग करे खातिर रउआ पहिले खाता में प्रवेश करे के पड़ी।",
        "changeemail-oldemail": "वर्तमान ई-मेल पता:",
        "changeemail-newemail": "नया ई-मेल पता:",
        "content-json-empty-object": "खाली चीज (Empty object)",
        "content-json-empty-array": "खाली अरे (Empty array)",
        "duplicate-args-warning": "<strong>चेतावनी:</strong> [[:$1]], [[:$2]] के \"$3\" पैरामीटर खातिर एक से अधिका वैल्यू की संघे काल करत बाटे। दिहल गइल वैल्यू में से खाली सबसे आखिरी वैल्यू के प्रयोग कइल जाई।",
-       "duplicate-args-category": "à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f à¤\95à¥\89ल à¤®à¥\87à¤\82 à¤¡à¥\81पà¥\8dलिà¤\95à¥\87à¤\9f à¤¤à¤°à¥\8dà¤\95 à¤\95à¥\87 à¤\89पयà¥\8bà¤\97 à¤\95रतà¥\87 à¤¹à¥\81à¤\8f à¤ªà¤¨à¥\8dनासभ",
+       "duplicate-args-category": "à¤\9fà¥\87मà¥\8dपलà¥\87à¤\9f à¤\95à¥\89ल à¤®à¥\87à¤\82 à¤¡à¥\81पà¥\8dलिà¤\95à¥\87à¤\9f à¤¤à¤°à¥\8dà¤\95 à¤\87सà¥\8dतà¥\87माल à¤µà¤¾à¤²à¤¾ à¤ªà¤¨à¥\8dना",
        "duplicate-args-category-desc": "पन्ना पर अइसन टेम्पलेट काल मौजूद बा जेवन डुप्लीकेट (दोहरा) आर्गुमेंट इस्तेमाल करत बाटे, जइसे की <code><nowiki>{{foo|bar=1|bar=2}}</nowiki></code> या <code><nowiki>{{foo|bar|1=baz}}</nowiki></code>.",
        "expensive-parserfunction-warning": "<strong>चेतावनी:</strong> ई पन्ना बहुत ढेर सारा  खर्चीला पार्सर फंक्शन काल के इस्तेमाल करत बा।\n\nए पन्ना पर $2 {{PLURAL:$2|काल|काल कुल}} से कम संख्या में काल होखे के चाहीं, बाकी इहाँ ए समय {{PLURAL:$1|$1 काल बा|$1 काल कुल बाड़ी}}।",
        "expensive-parserfunction-category": "बहुत ढेर खर्चीला पार्सर फंक्शन काल वाला पन्ना सभ",
        "upload-dialog-button-done": "पूरा भइल",
        "upload-dialog-button-save": "सहेजीं",
        "upload-dialog-button-upload": "अपलोड",
-       "upload-process-error": "कौनो खराबी आ गइल",
-       "upload-process-warning": "कौनो चेतावनी बा",
        "upload-form-label-select-file": "फाइल चुनीं",
        "upload-form-label-infoform-title": "डिटेल जानकारी",
        "upload-form-label-infoform-name": "नाँव",
index beb5966..55f58ad 100644 (file)
        "createacct-error": "অ্যাকাউন্ট তৈরি ত্রুটি",
        "createaccounterror": "অ্যাকাউন্ট তৈরি হয়নি: $1",
        "nocookiesnew": "ব্যবহারকারীর অ্যাকাউন্টটি সৃষ্টি করা হয়েছে, কিন্তু আপনি এখনও অ্যাকাউন্টে প্রবেশ করেননি। {{SITENAME}}-তে কুকি ব্যবহার করে ব্যবহারকারীদের অ্যাকাউন্টে প্রবেশ করানো হয়। আপনার ব্রাউজারে কুকিগুলি নিষ্ক্রিয় করা আছে। অনুগ্রহ করে কুকিগুলি সক্রিয় করুন এবং আপনার নতুন ব্যবহারকারী নাম ও পাসওয়ার্ড ব্যবহার করে অ্যাকাউন্টে প্রবেশ করুন।",
-       "nocookieslogin": "{{SITENAME}} এ কুকি (cookies) এর মাধ্যমে ব্যবহারকারীদের লগ-ইন সম্পন্ন করা হয়। আপনার ব্রাঊজারে কুকি বন্ধ করে দেওয়া আছে। কুকি চালু করে আবার চেষ্টা করুন।",
+       "nocookieslogin": "ব্যবহারকারীদের প্রবেশ সম্পন্ন করতে {{SITENAME}} কুকি ব্যবহার করে। আপনার ব্রাউজারে কুকি নিষ্ক্রিয় করা আছে। কুকি চালু করে আবার চেষ্টা করুন।",
        "nocookiesfornew": "ব্যবহারকারীর অ্যাকাউন্ট তৈরি হয়নি, কারণ এর উৎস সম্পর্কে আমরা নিশ্চিত নই।\nনিশ্চিত করুন আপনার কুকি সক্রিয় রয়েছে, পাতাটি পুনরায় লোড করে আবার চেষ্টা করুন।",
        "noname": "আপনি সঠিক ব্যবহারকারী নাম নির্দিষ্ট করেননি।",
        "loginsuccesstitle": "প্রবেশ সফল",
        "nosuchuser": "\"$1\" নামে কোন ব্যবহারকারী নেই।\nব্যবহারকারী নামের আকার সংবেদনশীল।\nআপনার বানান পরীক্ষা করে দেখুন, অথবা [[Special:UserLogin/signup|নতুন একটি অ্যাকাউন্ট খুলুন]]।",
        "nosuchusershort": "\"$1\" নামের কোন ব্যবহারকারী নেই। নামের বানান পরীক্ষা করুন।",
        "nouserspecified": "আপনাকে অবশ্যই ব্যবহারকারী নাম নির্দিষ্ট করতে হবে।",
-       "login-userblocked": "à¦\8fà¦\87 à¦¬à§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦¬à¦¾à¦§à¦¾ à¦¦à§\87à¦\93য়া à¦¹à¦¯à¦¼à§\87à¦\9bà§\87। à¦²à¦\97-à¦\87ন সম্ভব নয়।",
+       "login-userblocked": "à¦\8fà¦\87 à¦¬à§\8dযবহারà¦\95ারà§\80à¦\95à§\87 à¦¬à¦¾à¦§à¦¾ à¦¦à§\87à¦\93য়া à¦¹à¦¯à¦¼à§\87à¦\9bà§\87। à¦ªà§\8dরবà§\87শ সম্ভব নয়।",
        "wrongpassword": "আপনি ভুল পাসওয়ার্ড ব্যবহার করেছেন। অনুগ্রহ করে আবার চেষ্টা করুন।",
        "wrongpasswordempty": "পাসওয়ার্ড প্রবেশের ঘরটি খালি ছিল। দয়া করে আবার চেষ্টা করুন।",
        "passwordtooshort": "পাসওয়ার্ড কমপক্ষে {{PLURAL:$1|১ অক্ষরের|$1 অক্ষরের}} হতে হবে।",
        "confirmedittext": "কোন সম্পাদনা করার আগে আপনার ই-মেইল ঠিকানাটি অবশ্যই নিশ্চিত করতে হবে। দয়া করে আপনার ই-মেইল ঠিকানাটি [[Special:Preferences|ব্যবহারকারীর পছন্দতালিকায়]] ঠিকমত দিন।",
        "nosuchsectiontitle": "অনুচ্ছেদ পাওয়া যায়নি",
        "nosuchsectiontext": "আপনি এমন একটি অনুচ্ছেদ সম্পাদনার চেষ্টা করেছেন, যার কোন অস্তিত্ব নেই।\nযখন এই পাতাটি দেখার চেষ্টা করছে, তখন হয়তো এটি সরিয়ে অথবা অপসারণ করা হয়েছে।",
-       "loginreqtitle": "লà¦\97-à¦\87ন প্রয়োজন",
+       "loginreqtitle": "পà§\8dরবà§\87শ à¦\95রা প্রয়োজন",
        "loginreqlink": "প্রবেশ",
        "loginreqpagetext": "অন্যান্য পাতা দেখতে হলে আপনাকে অবশ্যই $1 হতে হবে।",
        "accmailtitle": "পাসওয়ার্ড পাঠানো হয়েছে",
        "userpage-userdoesnotexist": "\"<nowiki>$1</nowiki>\" নামের কোন ব্যবহারকারী অ্যাকাউন্ট নিবন্ধিত হয়নি। অনুগ্রহ করে পরীক্ষা করে দেখুন আপনি এই পাতাটি সৃষ্টি/সম্পাদনা করতে চান কি না।",
        "userpage-userdoesnotexist-view": "ব্যবহারকারী অ্যাকাউন্ট \"$1\" অনিবন্ধিত।",
        "blocked-notice-logextract": "এই ব্যবহারকারী বর্তমানে ব্লক রয়েছে।\nরেফারেন্সের জন্য সাম্প্রতিক ব্লক লগ ভুক্তি নিচে দেওয়া হল:",
-       "clearyourcache": "'''লক্ষ্য করুন:''' সংরক্ষণের পর, পরিবর্তনগুলো দেখতে আপনাকে আপনার ব্রাউজারে ক্যাশে পরিস্কার করার প্রয়োজন হতে পারে।\n* '''ফায়ারফক্স / সাফারি:''' ''শিফট'' কি ধরে রাখা অবস্থায় ''রিলোড''-এ ক্লিক করুন, অথবা ''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>Refresh</em>-এ ক্লিক করুন, অথবা <em>Ctrl-F5</em> চাপুন\n* <strong>অপেরা:</strong> <em>সরঞ্জাম → পছন্দসমূহ</em>-এ গিয়ে ক্যাশে পরিস্কার করে নিন",
        "usercssyoucanpreview": "'''পরামর্শ:''' \"{{int:showpreview}}\" বোতাম ব্যবহার করে সংরক্ষণের আগে আপনার নতুন CSS পরীক্ষা করুন।",
        "userjsyoucanpreview": "'''পরামর্শ:''' \"{{int:showpreview}}\" বোতাম ব্যবহার করে সংরক্ষণের আগে আপনার নতুন JavaScript পরীক্ষা করুন।",
        "usercsspreview": "'''মনে রাখবেন আপনি আপনার জন্য বরাদ্ধকৃত সিএসএস প্রাকদর্শন করছেন।\nএটা এখনও সংরক্ষণ করা হয়নি!'''",
        "userrights-groupsmember": "সদস্য:",
        "userrights-groupsmember-auto": "শর্তহীন সদস্য",
        "userrights-groups-help": "আপনি এই ব্যবহারকারীর বর্তমান দল পরিবর্তন করতে পারবেন:\n* টিক চিহ্ন দেওয়া ঘরের অর্থ ব্যবহারকারী এখন ঐ দলের অন্তর্ভুক্ত।\n* টিক চিহ্ন বিহীন ঘরের অর্থ ব্যবহারকারী ঐ দলের অন্তর্ভুক্ত নন।\n* একটি তারকা চিহ্ন (*) দ্বারা বোঝানো হচ্ছে এই দলের অন্তর্ভুক্তির পর আপনি আর তা বাতিল করতে পারবেন না।",
-       "userrights-reason": "কারণ:",
+       "userrights-reason": "কারণ (বাংলায় লিখুন):",
        "userrights-no-interwiki": "আপনার অন্য উইকিতে ব্যবহারকারী অধিকার সম্পাদনা করার অনুমতি নেই।",
        "userrights-nodatabase": "$1 ডাটাবেজটির হয় কোন অস্তিত্ব নেই অথবা এটি স্থানীয় ডাটাবেজ নয়।",
        "userrights-nologin": "ব্যবহারকারী অধিকার প্রযুক্ত করতে হলে আপনাকে কোন প্রশাসকের অ্যাকাউন্টে [[Special:UserLogin|প্রবেশ]] করতে হবে।",
        "right-blockemail": "ই-মেইল পাঠাতে কোনো ব্যবহারকারীকে বাঁধা দাও",
        "right-hideuser": "ব্যবহারকারীকে ব্লক করুন, এবং সর্বসাধারণের দৃষ্টিসীমা থেকে সরিয়ে নিন",
        "right-ipblock-exempt": "আইপি ব্লক, অটো ব্লক এবং রেঞ্জ ব্লক এড়িয়ে যান",
-       "right-proxyunbannable": "সয়ংক্রিয় প্রক্সি ব্লক এড়িয়ে যান",
        "right-unblockself": "একজনকে আনব্লক করুন",
        "right-protect": "পাতাটির সুরক্ষা সীমা পরিবর্তন করুন এবং সুরক্ষিত পাতটি সম্পাদনা করুন",
        "right-editprotected": "সুরক্ষিত পাতা সম্পাদনা (ক্যাসকাডিং সুরক্ষা ছাড়া)",
        "right-managechangetags": "ডাটাবেস থেকে [[Special:Tags|ট্যাগ]] তৈরি ও অপসারণ করুন",
        "right-applychangetags": "সম্পাদনার সাথে [[Special:Tags|ট্যাগ]] যুক্ত করুন",
        "right-changetags": "নির্দিষ্ট সংস্করণ এবং দীর্ঘ সম্পাদনাগুলোতে [[Special:Tags|ট্যাগ]] সংযোজন ও অপসারণ করুন",
+       "grant-group-email": "ইমেইল পাঠান",
+       "grant-createaccount": "অ্যাকাউন্ট তৈরি করুন",
+       "grant-editmyoptions": "আপনার ব্যবহারকারী পছন্দসমূহ সম্পাদনা করুন",
+       "grant-editmywatchlist": "আপনার নজরতালিকা সম্পাদনা করুন",
        "newuserlogpage": "ব্যবহারকারী সৃষ্টির লগ",
        "newuserlogpagetext": "এটি নতুন ব্যবহারকারী সৃষ্টির লগ",
        "rightslog": "ব্যবহারকারীর অধিকার লগ",
        "foreign-structured-upload-form-label-infoform-categories": "বিষয়শ্রেণীসমূহ",
        "foreign-structured-upload-form-label-infoform-date": "তারিখ",
        "foreign-structured-upload-form-label-not-own-work-local-local": "এছাড়াও আপনি [[Special:Upload|ডিফল্ট আপলোডের পাতা]] চেষ্টা করতে পারেন।",
+       "foreign-structured-upload-form-3-label-yes": "হ্যাঁ",
+       "foreign-structured-upload-form-3-label-no": "না",
        "backend-fail-stream": "\"$1\" ফাইলের স্ট্রিম দেখানো যাচ্ছে না।",
        "backend-fail-backup": "\"$1\" ফাইলের ব্যাকআপ তৈরী সম্ভব নয়।",
        "backend-fail-notexists": "\"$1\" নামের কোনো ফাইল নেই।",
        "usereditcount": "$1 {{PLURAL:$1|সম্পাদনা|সম্পাদনা}}",
        "usercreated": "{{GENDER:$3|তৈরি হয়েছে}} $1 তারিখ, সময়: $2",
        "newpages": "নতুন পাতাসমূহ",
+       "newpages-submit": "দেখাও",
        "newpages-username": "ব্যবহারকারী নাম:",
        "ancientpages": "পুরানো নিবন্ধ",
        "move": "সরিয়ে ফেলুন",
        "specialloguserlabel": "সম্পাদক:",
        "speciallogtitlelabel": "লক্ষ্য (শিরোনাম বা {{ns:user}}:ব্যবহারকারীর জন্য ব্যবহারকারী নাম):",
        "log": "লগগুলি",
+       "logeventslist-submit": "দেখাও",
        "all-logs-page": "সব পাবলিক লগ",
        "alllogstext": "{{SITENAME}}-এর সবগুলো লগের সম্মিলিত প্রদর্শন।\nআপনি লগের ধরন, ব্যবহারকারীর নাম, বা পাতার নাম নির্বাচন করে প্রদর্শনটির আকার কমিয়ে আনতে পারেন।",
        "logempty": "মিলে যায় এমন কিছু লগে পাওয়া যায়নি।",
        "cachedspecial-viewing-cached-ts": "আপনার ওপেন করা পাতাটি ক্যাশ থেকে প্রদর্শিত হচ্ছে, এটি সম্পূর্ণ নতুন হতে পারে।",
        "cachedspecial-refresh-now": "সাম্প্রতিকগুলো প্রদর্শন করো।",
        "categories": "বিষয়শ্রেণীসমূহ",
+       "categories-submit": "দেখাও",
        "categoriespagetext": "এই {{PLURAL:$1|বিষয়শ্রেণীতে|বিষয়শ্রেণীসমূহে}} পাতা বা মিডিয়া রয়েছে।\n[[Special:UnusedCategories|অব্যবহৃত বিষয়শ্রেণীসমূহ]] এখানে দেখানো হয়েছে।\nআরও দেখুন [[Special:WantedCategories|আবশ্যক বিষয়শ্রেণীসমূহ]]।",
        "categoriesfrom": "এই অক্ষর দিয়ে শুরু হওয়া বিষয়শ্রেণীগুলো দেখাও:",
        "special-categories-sort-count": "গণনার ভিত্তিতে সাজাও",
        "listgrouprights-removegroup-self-all": "নিজের অ্যাকাউন্ট থেকে সকল দল অপসারণ",
        "listgrouprights-namespaceprotection-header": "নামস্থান নিষেধাজ্ঞাসমূহ",
        "listgrouprights-namespaceprotection-namespace": "নামস্থান",
+       "listgrants-rights": "অধিকার",
        "trackingcategories": "বিষয়শ্রেণীসমূহ অনুসরণ করা হচ্ছে",
        "trackingcategories-msg": "বিষয়শ্রেণী অনুসরণ করা হচ্ছে",
        "trackingcategories-name": "বার্তা নাম",
        "watchlistfor2": "$1 ($2)-এর জন্য",
        "nowatchlist": "আপনার নজরতালিকা খালি।",
        "watchlistanontext": "আপনার নজরতালিকার আইটেমগুলি দেখতে বা সম্পাদনা করতে অনুগ্রহ করে প্রবেশ করুন।",
-       "watchnologin": "à¦\86পনি à¦\8fà¦\96নà¦\93 à¦²à¦\97-à¦\87ন à¦\95রà§\87ননি।",
+       "watchnologin": "à¦\86পনি à¦ªà§\8dরবà§\87শ à¦\95রà§\87ননি",
        "addwatch": "নজরতালিকায় যোগ করো",
        "addedwatchtext": "\"[[:$1]]\" ও এর আলোচনা পাতাটি আপনার [[Special:Watchlist|নজরতালিকাতে]] যোগ করা হয়েছে।",
        "addedwatchtext-short": "\"$1\" পাতাটি আপনার নজরতালিকায় যোগ করা হয়েছে।",
        "wlshowhideanons": "নামহীন ব্যবহারকারী",
        "wlshowhidepatr": "পরীক্ষিত সম্পাদনা",
        "wlshowhidemine": "আমার সম্পাদনা",
+       "wlshowhidecategorization": "পাতা শ্রেণীবদ্ধকরণ",
        "watchlist-options": "নজর তালিকা পছন্দসমূহ",
        "watching": "নজর রাখা হচ্ছে...",
        "unwatching": "নজর তুলে নেওয়া হচ্ছে...",
        "delete-confirm": "\"$1\" অপসারণ",
        "delete-legend": "অপসারণ",
        "historywarning": "<strong>সতর্কীকরণ:</strong> যে পাতাটি আপনি মুছে ফেলতে যাচ্ছেন তার ইতিহাসে প্রায় $1টি {{PLURAL:$1|সংশোধন}} রয়েছে:",
+       "historyaction-submit": "দেখাও",
        "confirmdeletetext": "আপনি একটি পাতা সেটির সমস্ত ইতিহাসসহ মুছে ফেলতে যাচ্ছেন।\nঅনুগ্রহ করে নিশ্চিত করুন আপনি আসলেই এটি করতে চান, আপনি এর ফলাফল সম্পর্কে অবহিত, এবং আপনি [[{{MediaWiki:Policy-url}}|নীতিমালা]] মেনে এটি করছেন।",
        "actioncomplete": "কাজটি নিষ্পন্ন হয়েছে",
        "actionfailed": "অ্যাকশন ব্যর্থ",
        "revertpage-nouser": "একজন গোপন ব্যবহারকারী কর্তৃক সম্পাদিত সম্পাদনাটি বাতিলপূর্বক {{GENDER:$1|[[User:$1|$1]]}}-এর সর্বশেষ সম্পাদনায় ফেরত যাওয়া হয়েছে।",
        "rollback-success": "$1-এর সম্পাদনাগুলি পূর্বাবস্থায় ফিরিয়ে নেওয়া হয়েছে; $2-এর করা শেষ সংস্করণে পাতাটি ফেরত নেওয়া হয়েছে।",
        "sessionfailure-title": "সেশন পরিত্যক্ত",
-       "sessionfailure": "à¦\86পনার à¦²à¦\97 à¦\87ন à¦¸à§\87শনà§\87 à¦\8fà¦\95à¦\9fি à¦¸à¦®à¦¸à§\8dযা à¦¹à¦¯à¦¼à§\87à¦\9bà§\87 à¦¬à¦²à§\87 à¦®à¦¨à§\87 à¦¹à¦\9aà§\8dà¦\9bà§\87;\nসà§\87শন à¦¹à¦¾à¦\87à¦\9cà§\8dযাà¦\95 à¦ªà§\8dরতিরà§\8bধà§\87র à¦\89পায় à¦¹à¦¿à¦¸à§\87বà§\87 à¦\8fà¦\87 à¦\95াà¦\9cà¦\9fি à¦¬à¦¾à¦¤à¦¿à¦² à¦\95রা à¦¹à¦¯à¦¼à§\87à¦\9bà§\87।\nà¦\85নà§\81à¦\97à§\8dরহ à¦¬à§\8dরাà¦\89à¦\9cারà§\87র \"back\" à¦¬à§\8bতাম à¦\9aাপà§\81ন à¦\8fবà¦\82 à¦¯à§\87 à¦ªà¦¾à¦¤à¦¾ à¦¥à§\87à¦\95à§\87 à¦\8fসà§\87à¦\9bিলà§\87ন, à¦¤à¦¾ à¦°à¦¿লোড করুন এবং আবার চেষ্টা করুন।",
+       "sessionfailure": "à¦\86পনার à¦ªà§\8dরবà§\87শ à¦¸à§\87শনà§\87 à¦\8fà¦\95à¦\9fি à¦¸à¦®à¦¸à§\8dযা à¦¹à¦¯à¦¼à§\87à¦\9bà§\87 à¦¬à¦²à§\87 à¦®à¦¨à§\87 à¦¹à¦\9aà§\8dà¦\9bà§\87;\nসà§\87শন à¦¹à¦¾à¦\87à¦\9cà§\8dযাà¦\95 à¦ªà§\8dরতিরà§\8bধà§\87র à¦\89পায় à¦¹à¦¿à¦¸à§\87বà§\87 à¦\8fà¦\87 à¦\95াà¦\9cà¦\9fি à¦¬à¦¾à¦¤à¦¿à¦² à¦\95রা à¦¹à¦¯à¦¼à§\87à¦\9bà§\87।\nà¦\85নà§\81à¦\97à§\8dরহ à¦¬à§\8dরাà¦\89à¦\9cারà§\87র \"পিà¦\9bনà§\87\" à¦¬à§\8bতাম à¦\9aাপà§\81ন à¦\8fবà¦\82 à¦¯à§\87 à¦ªà¦¾à¦¤à¦¾ à¦¥à§\87à¦\95à§\87 à¦\8fসà§\87à¦\9bিলà§\87ন, à¦¤à¦¾ à¦ªà§\81নà¦\83লোড করুন এবং আবার চেষ্টা করুন।",
        "changecontentmodel-title-label": "পাতার শিরোনাম",
        "changecontentmodel-model-label": "পাতার বিষয়বস্তুর প্রতিরূপ",
        "changecontentmodel-reason-label": "কারণ:",
        "protect-cascadeon": "এই পাতাটি বর্তমানে সুরক্ষিত আছে, কারণ পাতাটি নিচের {{PLURAL:$1|পাতায়|পাতাগুলিতে}} অন্তর্ভুক্ত, {{PLURAL:$1|যাতে|যেগুলিতে}} প্রপাতাকার সুরক্ষা চালু আছে। আপনি এই পাতাটির সুরক্ষা স্তর পরিবর্তন করতে পারেন, তবে এটি প্রপাতাকার সুরক্ষাটিতে কোন পরিবর্তন সাধন করবে না।",
        "protect-default": "সমস্ত ব্যবহারকারীর জন্য",
        "protect-fallback": "\"$1\" অধিকার রয়েছে এমন ব্যবহারকারীদের জন্য অনুমতি",
-       "protect-level-autoconfirmed": "à¦\95à§\87বলমাতà§\8dর à¦¸à¦¯à¦¼à¦\82à¦\95à§\8dরিয় পরীক্ষিত ব্যবহারকারীদের জন্য",
+       "protect-level-autoconfirmed": "শà§\81ধà§\81মাতà§\8dর à¦¸à§\8dবয়à¦\82 পরীক্ষিত ব্যবহারকারীদের জন্য",
        "protect-level-sysop": "কেবল প্রশাসকদের জন্য অনুমতি",
        "protect-summary-cascade": "প্রপাতাকার",
        "protect-expiring": "$1 (ইউটিসি) সময়ে মেয়াদোত্তীর্ণ",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''নিষ্ক্রিয় করা''')",
        "mediastatistics": "মিডিয়া পরিসংখ্যান",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 বাইট}} ($2; $3%)",
+       "mediastatistics-bytespertype": "এই অনুচ্ছেদের জন্য মোট ফাইলের আকার: $1 বাইট।",
+       "mediastatistics-allbytes": "সব ফাইলের জন্য মোট ফাইলের আকার: $1 বাইট।",
        "mediastatistics-table-mimetype": "MIME ধরন",
        "mediastatistics-table-extensions": "সম্ভাব্য এক্সটেনশন",
        "mediastatistics-table-count": "ফাইলের সংখ্যা",
index ebdaaa8..2a94971 100644 (file)
        "userlogout": "ཕྱིར་འབུད།",
        "notloggedin": "ནང་འཛུལ་བྱས་མེད།",
        "userlogin-noaccount": "ཐོ་ཞིག་མི་དགོས་སམ།",
+       "nologin": "ཐོ་འགོད་པ།",
        "nologinlink": "ཐོ་ཞིག་འགོད་པ།",
        "createaccount": "ཐོ་འགོད།",
        "gotaccount": "$1 སྔོན་ཚུད་ནས་རྩིས་ཁྲ་ཡོད་དམ།",
        "createaccountmail": "སྐབས་འཕྲལ་རང་མོས་གྱི་གསང་བའི་ཨང་གྲངས་བེད་སྤྱད་པ་དང། ལྷན་དུ་གློག་འཕྲིན་ཁ་བྱང་ངེས་གཏན་ཞིག་ལ་བསྐུར་རོགས།",
        "createaccountreason": "རྒྱུ་མཚན།",
        "createacct-reason-ph": "ཐོ་གཞན་པ་ཞིག་འགོད་པའི་རྒྱུ་མཚན་གང་ལགས།",
+       "createacct-submit": "ཐོ་འགོད་བྱ་བ།",
        "badretype": "ལམ་ཡིག་གང་བཅུག་པ་ཐོ་ཐུག་མ་བྱུང་།",
        "userexists": "སྤྱོད་མིང་འདི་སྔོན་ཚུད་ནས་བེད་སྤྱོད་བྱས་ཟིན་འདུག་པས། མིང་གཞན་ཞིག་གདམ་གནང་རོགས།",
        "loginerror": "ནང་འཛུལ་ནོར་སྐྱོན།",
index f45e43c..e94b478 100644 (file)
        "resetpass_submit": "Cheñch ar ger-tremen ha kevreañ",
        "changepassword-success": "Cheñchet eo bet ho ker-tremen !",
        "changepassword-throttled": "Betek re hoc'h heus klasket kevreañ en aner.\nGortozit $1, mar plij, a-raok esaeañ en-dro.",
+       "botpasswords-label-create": "Krouiñ",
+       "botpasswords-label-cancel": "Nullañ",
+       "botpasswords-label-delete": "Dilemel",
        "resetpass_forbidden": "N'haller ket cheñch ar gerioù-termen",
        "resetpass-no-info": "Ret eo deoc'h bezañ kevreet a-benn mont d'ar bajenn-se war-eeun.",
        "resetpass-submit-loggedin": "Cheñch ger-tremen",
        "passwordreset-emailtext-ip": "Unan bennak (c'hwi moarvat gant ar chomlec'h IP $1) en deus goulennet ma 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-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-emailsent": "Kaset ez eus bet ur postel deoc'h da adderaouekaat ho ker-tremen.",
+       "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",
        "prefs-diffs": "Diforc'hioù",
        "prefs-help-prefershttps": "Efediñ a ray an dibarzh-mañ kentañ gwech ma kevreoc'h.",
        "prefs-tabs-navigation-hint": "Titourig : Gallout a rit implijout an touchennoù bir kleiz ha bir dehoù evit merdeiñ etre an ivinelloù e roll an ivinelloù.",
-       "email-address-validity-valid": "Reizh eo ar chomlec'h postel war a seblant",
-       "email-address-validity-invalid": "Ebarzhit ur chomlec'h postel reizh",
        "userrights": "Merañ statud an implijerien",
        "userrights-lookup-user": "Merañ strolladoù an implijer",
        "userrights-user-editname": "Lakait un anv implijer :",
        "right-blockemail": "Mirout ouzh un implijer a gas posteloù",
        "right-hideuser": "Stankañ un implijer, en ur guzhat anezhañ diouzh ar re all",
        "right-ipblock-exempt": "Tremen dreist an IPoù stanket, ar stankadennoù emgefre hag ar bloc'hadennoù IP stanket",
-       "right-proxyunbannable": "Tremen dreist stankadennoù emgefre ar proksioù",
        "right-unblockself": "En em zistankañ",
        "right-protect": "Kemmañ live gwareziñ ar pajennoù ha kemmañ ar pajennoù gwarezet",
        "right-editprotected": "Aozañ ar pajennoù gwarezet evel \"{{int:protect-level-sysop}}\"",
        "right-override-export-depth": "Ezporzhiañ ar pajennoù en ur lakaat e-barzh ar pajennoù liammet betek un donder a 5 live",
        "right-sendemail": "Kas ur postel d'an implijerien all",
        "right-passwordreset": "Gwelet ar posteloù assevel gerioù-tremen",
+       "grant-group-email": "Kas ur postel",
+       "grant-createaccount": "Krouiñ kontoù",
+       "grant-createeditmovepage": "Krouiñ, aozañ ha dilec'hiañ pajennoù",
+       "grant-editmywatchlist": "Aozañ ho roll evezhiañ",
+       "grant-editpage": "Aoañ pajennoù a zo anezho dija",
+       "grant-editprotected": "Aozañ pajennoù gwarezet",
+       "grant-sendemail": "Kas ur postel d'an implijerien all",
+       "grant-viewdeleted": "Gwelet an titouroù dilamet",
+       "grant-viewmywatchlist": "Gwelet ho roll evezhiañ",
        "newuserlogpage": "Marilh ar c'hontoù krouet",
        "newuserlogpagetext": "Marilh krouiñ ar c'hontoù implijer.",
        "rightslog": "Marilh statud an implijerien",
        "listgrouprights-removegroup-self-all": "Gallout a ra tennañ kuit an holl strolladoù eus kont an-unan.",
        "listgrouprights-namespaceprotection-namespace": "Esaouenn anv",
        "listgrouprights-namespaceprotection-restrictedto": "Gwir(ioù) hag a aotre an implijer da aozañ",
+       "listgrants-rights": "Gwirioù",
        "trackingcategories": "Rummadoù evezhiañ",
        "trackingcategories-msg": "Rummad evezhiañ",
        "trackingcategories-name": "Anv ar gemennadenn",
        "contributions": "Degasadennoù an {{GENDER:$1|implijer|implijerez}}",
        "contributions-title": "Degasadennoù an implijer evit $1",
        "mycontris": "Ma degasadennoù",
+       "anoncontribs": "Ma degasadennoù",
        "contribsub2": "Evit {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "N'eo ket enrollet ar gont implijer \"$1\".",
        "nocontribs": "N'eus bet kavet kemm ebet o klotañ gant an dezverkoù-se.",
        "movenosubpage": "Ispajenn ebet d'ar bajenn-mañ.",
        "movereason": "Abeg :",
        "revertmove": "nullañ",
-       "delete_and_move": "Diverkañ ha sevel adkas",
        "delete_and_move_text": "==Ezhomm diverkañ==\n\nSavet eo ar pennad tal \"[[:$1]]\" c'hoazh.\nDiverkañ anezhañ a fell deoc'h ober evit reiñ lec'h d'an adkas ?",
        "delete_and_move_confirm": "Ya, diverkañ ar bajenn",
        "delete_and_move_reason": "Diverket evit ober lec'h d'an adkas \"[[$1]]\"",
        "special-characters-group-thai": "Thai",
        "special-characters-group-lao": "Laoseg",
        "special-characters-group-khmer": "Khmer",
-       "api-error-blacklisted": "Dibabit un titl deskrivañ all"
+       "api-error-blacklisted": "Dibabit un titl deskrivañ all",
+       "randomrootpage": "Pajenn wrizienn dargouezhek"
 }
index 0f08306..d52c605 100644 (file)
        "toc": "Sadržaj",
        "showtoc": "prikaži",
        "hidetoc": "sakrij",
-       "collapsible-collapse": "sklopi",
-       "collapsible-expand": "Proširi",
+       "collapsible-collapse": "sakrij",
+       "collapsible-expand": "prikaži",
        "confirmable-confirm": "Da li {{GENDER:$1|ste}} sigurni?",
        "confirmable-yes": "Da",
        "confirmable-no": "Ne",
        "myprivateinfoprotected": "Nemate dozvolu da uređujete svoje privatne informacije.",
        "mypreferencesprotected": "Nemate dozvolu da uređujete svoje postavke.",
        "ns-specialprotected": "Specijalne stranice se ne mogu uređivati.",
-       "titleprotected": "Naslov stranice je zaštićen od postavljanja od strane korisnika [[User:$1|$1]].\nIz razloga \"''$2''\".",
+       "titleprotected": "Ovaj naslov stranice je od pravljenja [[User:$1|{{GENDER:$1|zaštitio $1|zaštitila $1}}]].\nRazlog: \"<em>$2</em>\".",
        "filereadonlyerror": "Ne mogu promijeniti datoteku \"$1\" jer je skladište datoteka \"$2\" zaključano samo za čitanje.\n\nAdministrator koji ga je zaključao naveo je ovo objašnjenje: \"$3\".",
        "invalidtitle-knownnamespace": "Neispravan naslov s imenskim prostorom \"$2\" i tekstom \"$3\"",
        "invalidtitle-unknownnamespace": "Neispravan naslov s imenskim prostorom br. $1 i tekstom \"$2\"",
        "viewpagelogs": "Pogledaj zapisnike ove stranice",
        "nohistory": "Ne postoji historija izmjena za ovu stranicu.",
        "currentrev": "Trenutna verzija",
-       "currentrev-asof": "Trenutna verzija na dan $1",
+       "currentrev-asof": "Trenutna verzija na dan $2 u $3",
        "revisionasof": "Verzija od $1",
        "revision-info": "Izmjena od $1 od {{GENDER:$6|$2}}$7",
        "previousrevision": "← Starija izmjena",
        "right-blockemail": "Blokiranje korisnika da šalje e-mail",
        "right-hideuser": "Blokiranje korisničkog imena, i njegovo sakrivanje od javnosti",
        "right-ipblock-exempt": "Zaobilaženje IP blokada, autoblokada i blokada IP grupe",
-       "right-proxyunbannable": "Zaobilaženje automatskih blokada proxy-ja",
        "right-unblockself": "Deblokiraj samog sebe",
        "right-protect": "Promjena nivoa zaštite i uređivanje kaskadno zaštićenih stranica",
        "right-editprotected": "Uređivanje stranice zaštićenih kao \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Napravi i briši [[Special:Tags|oznake]] iz baze podataka",
        "right-applychangetags": "Primijeni [[Special:Tags|oznake]] na nečije izmjene",
        "right-changetags": "Dodavanje ili uklanjanje raznih [[Special:Tags|oznaka]] na pojedinačnim verzijama i unosima zapisnika",
+       "grant-createeditmovepage": "Pravljenje, uređivanje i premještanje stranica",
+       "grant-editmywatchlist": "Uređivanje Vašeg spiska praćenja",
+       "grant-editpage": "Uređivanje postojećih stranica",
+       "grant-editprotected": "Uređivanje zaštićenih stranica",
+       "grant-highvolume": "Uređivanja velikog opsega",
+       "grant-uploadeditmovefile": "Postavljanje, zamjena i premještanje datoteka",
+       "grant-uploadfile": "Postavljanje novih datoteka",
+       "grant-viewmywatchlist": "Pregled Vašeg spiska praćenja",
        "newuserlogpage": "Zapisnik novih korisnika",
        "newuserlogpagetext": "Ovo je zapisnik o registraciji novih korisnika.",
        "rightslog": "Zapisnik korisničkih prava",
        "mostrevisions": "Članci sa najviše izmjena",
        "prefixindex": "Sve stranice sa prefiksom",
        "prefixindex-namespace": "Sve stranice s predmetkom (imenski prostor $1)",
+       "prefixindex-submit": "Prikaži",
        "prefixindex-strip": "Sakrij prefiks u spisku",
        "shortpages": "Kratke stranice",
        "longpages": "Dugačke stranice",
        "usereditcount": "$1 {{PLURAL:$1|izmjena|izmjene}}",
        "usercreated": "{{GENDER:$3|Napravio|Napravila}} dana $1 u $2",
        "newpages": "Nove stranice",
+       "newpages-submit": "Prikaži",
        "newpages-username": "Korisničko ime:",
        "ancientpages": "Najstarije stranice",
        "move": "Premjesti",
        "specialloguserlabel": "Izvršilac:",
        "speciallogtitlelabel": "Cilj (naslov ili {{ns:user}}:korisničko ime):",
        "log": "Zapisnici",
+       "logeventslist-submit": "Prikaži",
        "all-logs-page": "Svi javni zapisnici",
        "alllogstext": "Skupni prikaz svih dostupnih zapisnika sa {{GRAMMAR:genitiv|{{SITENAME}}}}.\nMožete suziti prikaz izabiranjem specifičnog zapisnika, korisničkog imena (razlikovati velika i mala slova) ili izmijenjenog članka (također treba razlikovati velika i mala slova).",
        "logempty": "Nema zatraženih stavki u zapisniku.",
        "cachedspecial-viewing-cached-ts": "Gledate keširanu verziju ove stranice, koja možda nije potpuno aktualna.",
        "cachedspecial-refresh-now": "Pogledaj najnoviju.",
        "categories": "Kategorije",
+       "categories-submit": "Prikaži",
        "categoriespagetext": "{{PLURAL:$1|Slijedeća kategorija sadrži|Slijedeće kategorije sadrže}} stranice ili multimedijalne datoteke.\n[[Special:UnusedCategories|Nekorištene kategorije]] nisu prikazane ovdje.\nVidi također [[Special:WantedCategories|zatražene kategorije]].",
        "categoriesfrom": "Prikaži kategorije počev od:",
        "special-categories-sort-count": "sortiranje po broju",
        "listgrouprights-namespaceprotection-header": "Ograničenja imenskog prostora",
        "listgrouprights-namespaceprotection-namespace": "Imenski prostor",
        "listgrouprights-namespaceprotection-restrictedto": "Prava kojima se dozvoljava korisniku da uređuje",
+       "listgrants-summary": "Ovo je spisak OAuth dozvola s odgovarajućim pravima uz svaku dozvolu s desne strane. Korisnici aplikacijama mogu odobriti da koriste njihov korisnički račun ali uz ograničena prava u zavisnosti od tog koju dozvolu im korisnik omogući. Međutim, aplikacija koja se koristi korisničkim računom ne može koristiti prava koja korisnik ne posjeduje. Moguće je da postoje [[{{MediaWiki:Listgrouprights-helppage}}|dodatne informacije]] o pojedinim pravima.",
        "trackingcategories": "Praćenje kategorija",
        "trackingcategories-summary": "Ova stranica prikazuje prateće kategorije koje MediaWiki softver automatski popunjava. Njihovi nazivi se mogu promijeniti izmjenom odgovarajućih sistemskih poruka u imenskom prostoru {{ns:8}}.",
        "trackingcategories-msg": "Praćenje kategorije",
        "wlshowlast": "Prikaži posljednjih $1 sati $2 dana",
        "watchlistall2": "sve",
        "watchlist-hide": "Sakrij",
+       "watchlist-submit": "Prikaži",
        "wlshowtime": "Prikaži posljednjih:",
        "wlshowhideminor": "manje izmjene",
        "wlshowhidebots": "botove",
        "delete-confirm": "Brisanje \"$1\"",
        "delete-legend": "Obriši",
        "historywarning": "<strong>Upozorenje</strong>: Stranica koju želite da obrišete ima historiju sa otprilike $1 {{PLURAL:$1|revizijom|revizije|revizija}}:",
+       "historyaction-submit": "Prikaži",
        "confirmdeletetext": "Brisanjem ćete obrisati stranicu ili sliku zajedno sa historijom iz baze podataka, ali će se iste moći vratiti kasnije.\nMolim potvrdite svoju namjeru, da razumijete posljedice i da ovo radite u skladu sa [[{{MediaWiki:Policy-url}}|pravilima]].",
        "actioncomplete": "Radnja je izvršena",
        "actionfailed": "Akcija nije uspjela",
        "protect-cascadeon": "Ova stranica je trenutno zaštićena jer je uključena u {{PLURAL:$1|stranicu, koja ima|stranice, koje imaju}} uključenu prenosivu zaštitu.\nPromjene stepena zaštite ove stranice neće utjecati na prenosnu zaštitu.",
        "protect-default": "Dopušteno svim korisnicima",
        "protect-fallback": "Dozvolite samo korisnicima sa \"$1\" ovlastima/privilegijama",
-       "protect-level-autoconfirmed": "Dopušteno samo automatski potvrđenim korisnicima",
-       "protect-level-sysop": "Dopušteno samo administratorima",
+       "protect-level-autoconfirmed": "dopušteno samo automatski potvrđenim korisnicima",
+       "protect-level-sysop": "dopušteno samo administratorima",
        "protect-summary-desc": "[$1=$2] ($3)",
        "protect-summary-cascade": "prenosna zaštita",
        "protect-expiring": "ističe $1 (UTC)",
-       "protect-expiring-local": "ističe $1",
+       "protect-expiring-local": "ističe $2 u $3",
        "protect-expiry-indefinite": "neodređeno",
        "protect-cascade": "Zaštiti sve stranice koje su uključene u ovu (prenosiva zaštita)",
        "protect-cantedit": "Ne možete mijenjati nivo zaštite ove stranice, jer nemate prava da je uređujete.",
        "protect-edit-reasonlist": "Uredi razloge zaštićivanja",
        "protect-expiry-options": "1 sat:1 hour,1 dan:1 day,1 sedmica:1 week,2 sedmice:2 weeks,1 mjesec:1 month,3 mjeseca:3 months,6 mjeseci:6 months,1 godine:1 year,beskonačno:infinite",
        "restriction-type": "Dopuštenje:",
-       "restriction-level": "Stepen ograničenja:",
+       "restriction-level": "Nivo zaštite:",
        "minimum-size": "Najmanja veličina",
        "maximum-size": "Najveća veličina:",
        "pagesize": "(bajta)",
-       "restriction-edit": "Uređivanje",
-       "restriction-move": "Premještanje",
-       "restriction-create": "Napravi",
+       "restriction-edit": "uređivanje",
+       "restriction-move": "premještanje",
+       "restriction-create": "pravljenje",
        "restriction-upload": "Postavljanje",
        "restriction-level-sysop": "potpuno zaštićeno",
        "restriction-level-autoconfirmed": "poluzaštićeno",
        "undeletehistory": "Ako vratite stranicu, sve će verzije biti vraćene u njenu historiju.\nAko je u međuvremenu napravljena nova verzija s istim nazivom, vraćene verzije će se pojaviti njenoj ranijoj historiji.",
        "undeleterevdel": "Vraćanje obrisanog se neće izvršiti ako bi rezultiralo da zaglavlje stranice ili revizija datoteke bude djelimično obrisano.\nU takvim slučajevima, morate ukloniti označene ili otkriti sakrivene najskorije obrisane revizije.",
        "undeletehistorynoadmin": "Ova stranica je obrisana.\nRazlog za brisanje se nalazi ispod, zajedno s detaljima o korisniku koji je mijenjao stranicu prije brisanja.\nTekst obrisanih verzija dostupan je samo administratorima.",
-       "undelete-revision": "Obrisana revizija stranice $1 (dana $4, u $5) od strane $3:",
+       "undelete-revision": "Obrisana izmjena stranice $1 (dana $4, u $5) koju je {{GENDER:$3|napravio|napravila}} $3:",
        "undeleterevision-missing": "Nepoznata ili nedostajuća revizija.\nMožda ste unijeli pogrešan link, ili je revizija vraćena ili uklonjena iz arhive.",
        "undelete-nodiff": "Nije pronađena ranija revizija.",
        "undeletebtn": "Vrati",
        "undeleteviewlink": "pogledaj",
        "undeleteinvert": "Izmijeni odabir",
        "undeletecomment": "Razlog:",
-       "undeletedrevisions": "{{PLURAL:$1|$1 verzija vraćena|$1 verzije vraćene|$1 verzija vraćeno}}",
+       "undeletedrevisions": "{{PLURAL:$1|vraćena $1 verzija|vraćene $1 verzije|vraćeno $1 verzija}}",
        "undeletedrevisions-files": "{{PLURAL:$1|1 verzija|$1 verzije|$1 verzija}} i {{PLURAL:$2|1 datoteka|$2 datoteke|$2 datoteka}} vraćeno",
        "undeletedfiles": "{{PLURAL:$1|1 datoteka vraćena|$1 datoteke vraćene|$1 datoteka vraćeno}}",
        "cannotundelete": "Vraćanje nije uspjelo:\n$1",
        "logentry-move-move": "$1 {{GENDER:$2|premjestio|premjestila}} je stranicu $3 na $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|premjestio|premjestila}} je stranicu $3 na $4 bez ostavljanja preusmjerenja",
        "logentry-move-move_redir": "$1 {{GENDER:$2|premjestio|premjestila}} je stranicu $3 na $4 preko preusmjerenja",
-       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|premjestio|premjestila}} je stranicu $3 na $4 preko preusmjeravanja bez ostavljanja preusmjeravanja",
+       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|premjestio|premjestila}} je stranicu $3 na $4 preko preusmjerenja bez ostavljanja preusmjerenja",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|označio|označila}} je izmjenu $4 stranice $3 pregledanom",
        "logentry-patrol-patrol-auto": "$1 automatski je {{GENDER:$2|označio|označila}} verziju $4 stranice $3 pregledanom",
        "logentry-newusers-newusers": "Korisnički račun $1 je {{GENDER:$2|napravljen}}",
        "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."
+       "api-error-blacklisted": "Molimo izaberite drugačiji, deskriptivniji naziv.",
+       "randomrootpage": "Slučajna root stranica"
 }
index 8e9e456..c0fefac 100644 (file)
        "october": "octubre",
        "november": "novembre",
        "december": "desembre",
-       "january-gen": "gener",
-       "february-gen": "febrer",
-       "march-gen": "març",
+       "january-gen": "de gener",
+       "february-gen": "de febrer",
+       "march-gen": "de març",
        "april-gen": "d'abril",
        "may-gen": "de maig",
        "june-gen": "de juny",
        "september-gen": "de setembre",
        "october-gen": "d'octubre",
        "november-gen": "de novembre",
-       "december-gen": "desembre",
+       "december-gen": "de desembre",
        "jan": "gen",
        "feb": "feb",
        "mar": "març",
        "userloginnocreate": "Inici de sessió",
        "logout": "Finalitza la sessió",
        "userlogout": "Finalitza la sessió",
-       "notloggedin": "No us heu identificat",
+       "notloggedin": "Sense sessió iniciada",
        "userlogin-noaccount": "No teniu cap compte?",
        "userlogin-joinproject": "Uniu-vos a {{SITENAME}}",
        "nologin": "No teniu un compte? '''$1'''.",
        "createacct-error": "Error de creació de compte",
        "createaccounterror": "No s'ha pogut crear el compte: $1",
        "nocookiesnew": "S'ha creat el compte d'usuari, però no s'ha iniciat la sessió.\nEl projecte {{SITENAME}} usa galetes per a iniciar la sessió d'usuari. \nTeniu les galetes desactivades. \nActiveu-les per a poder iniciar la sessió amb el nou nom d'usuari i la nova clau.",
-       "nocookieslogin": "El programari {{SITENAME}} utilitza galetes per enregistrar usuaris. Teniu les galetes desactivades. Activeu-les i torneu a provar.",
+       "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}}",
        "noname": "No heu especificat un nom vàlid d'usuari.",
        "createacct-another-realname-tip": "El nom real és opcional.\nSi decidiu proporcionar-lo, s'utilitzarà per a reconèixer a l'usuari el seu treball.",
        "pt-login": "Inicia la sessió",
        "pt-login-button": "Inicia sessió",
-       "pt-createaccount": "Creeu un compte",
+       "pt-createaccount": "Crea un compte",
        "pt-userlogout": "Finalitza la sessió",
        "php-mail-error-unknown": "Error desconegut en la funció mail() de PHP",
        "user-mail-no-addy": "S'ha intentat enviar un missatge de correu electrònic sense adreça.",
        "content-model-text": "text net",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-model-json": "JSON",
        "content-json-empty-object": "Objecte buit",
        "content-json-empty-array": "Matriu buida",
        "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.",
        "post-expand-template-argument-category": "Pàgines que contenen arguments de plantilla que s'han omès",
        "parser-template-loop-warning": "S'ha detectat un bucle de plantilla: [[$1]]",
        "parser-template-recursion-depth-warning": "S'ha excedit el límit de recursivitat de plantilles ($1)",
-       "language-converter-depth-warning": "El límit de la profunditat del conversor d'idiomes ha excedit ($1)",
+       "language-converter-depth-warning": "S'ha excedit el límit de profunditat del convertidor d'idiomes ($1)",
        "node-count-exceeded-category": "Pàgines on s'ha excedit el recompte de nodes",
        "node-count-exceeded-category-desc": "La pàgina ha excedit el compte màxim de nodes.",
        "node-count-exceeded-warning": "La pàgina ha excedit el recompte de nodes",
        "filename-tooshort": "El nom del fitxer és massa curt.",
        "filetype-banned": "Aquest tipus de fitxer està prohibit.",
        "verification-error": "Aquest fitxer no ha passat la verificació de fitxers.",
-       "hookaborted": "La modificació que vau tractar de fer l'ha canceŀlat un lligam d'extensió.",
+       "hookaborted": "La modificació que heu intentat fer l'ha cancel·lada una extensió.",
        "illegal-filename": "El nom del fitxer no està permès.",
        "overwrite": "No es permet sobreescriure un fitxer existent.",
        "unknown-error": "S'ha produït un error desconegut.",
        "foreign-structured-upload-form-label-infoform-date": "Data",
        "foreign-structured-upload-form-label-not-own-work-local-local": "També podeu provar [[Special:Upload|la pàgina de càrrega per defecte]].",
        "foreign-structured-upload-form-label-own-work-message-default": "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.",
+       "foreign-structured-upload-form-3-label-yes": "Sí",
+       "foreign-structured-upload-form-3-label-no": "No",
        "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.",
        "usereditcount": "$1 {{PLURAL:$1|modificació|modificacions}}",
        "usercreated": "{{GENDER:$3|Creat}}: $1 a les $2",
        "newpages": "Pàgines noves",
+       "newpages-submit": "Mostra",
        "newpages-username": "Nom d'usuari:",
        "ancientpages": "Pàgines més antigues",
        "move": "Reanomena",
        "specialloguserlabel": "Realitzador:",
        "speciallogtitlelabel": "Objectiu (títol o «{{ns:user}}:nom d’usuari» per a un usuari):",
        "log": "Registres",
+       "logeventslist-submit": "Mostra",
        "all-logs-page": "Tots els registres públics",
        "alllogstext": "Presentació combinada de tots els registres disponibles de {{SITENAME}}.\nPodeu reduir l'extensió seleccionant el tipus de registre, el nom d'usuari realitzador (distingeix entre majúscules i minúscules), o la pàgina objectiu (també en distingeix).",
        "logempty": "No hi ha cap coincidència en el registre.",
        "cachedspecial-viewing-cached-ts": "Esteu veient una versió a la memòria cau de la pàgina, que podria no ser completament actual.",
        "cachedspecial-refresh-now": "Mostra la darrera.",
        "categories": "Categories",
+       "categories-submit": "Mostra",
        "categoriespagetext": "{{PLURAL:$1|La següent categoria conté|Les següents categories contenen}} pàgines, o fitxers multimèdia.\n[[Special:UnusedCategories|Les categories no usades]] no s'hi mostren.\nVegeu també [[Special:WantedCategories|les categories sol·licitades]].",
        "categoriesfrom": "Mostra les categories que comencen a:",
        "special-categories-sort-count": "ordena per recompte",
        "delete-confirm": "Elimina «$1»",
        "delete-legend": "Elimina",
        "historywarning": "<strong>Avís:</strong> la pàgina que esteu a punt d'eliminar té un historial amb $1 {{PLURAL:$1|revisió|revisions}}:",
+       "historyaction-submit": "Mostra",
        "confirmdeletetext": "Esteu a punt d'esborrar de forma permanent una pàgina o imatge i tot el seu historial de la base de dades.\nConfirmeu que realment ho voleu fer, que enteneu les\nconseqüències, i que el que esteu fent està d'acord amb la [[{{MediaWiki:Policy-url}}|política]] del projecte.",
        "actioncomplete": "Acció realitzada",
        "actionfailed": "L'acció ha fallat",
        "show-big-image-preview": "Mida d'aquesta previsualització: $1.",
        "show-big-image-other": "{{PLURAL:$2|Altra resolució|Altres resolucions}}: $1.",
        "show-big-image-size": "$1 × $2 píxels",
-       "file-info-gif-looped": "embuclat",
+       "file-info-gif-looped": "en bucle",
        "file-info-gif-frames": "$1 {{PLURAL:$1|fotograma|fotogrames}}",
-       "file-info-png-looped": "embuclat",
+       "file-info-png-looped": "en bucle",
        "file-info-png-repeat": "s'ha reproduït $1 {{PLURAL:$1|vegada|vegades}}",
        "file-info-png-frames": "$1 {{PLURAL:$1|fotograma|fotogrames}}",
        "file-no-thumb-animation": "<strong>Nota: per limitacions tècniques no s'animaran les miniatures per aquest fitxer.</strong>",
        "exif-gpsdirection-m": "Direcció magnètica",
        "exif-ycbcrpositioning-1": "Centrat",
        "exif-ycbcrpositioning-2": "co-localitzats",
-       "exif-dc-contributor": "Coŀlaboradors",
+       "exif-dc-contributor": "Colaboradors",
        "exif-dc-coverage": "Abast espacial o temporal del contingut",
        "exif-dc-date": "Data(es)",
        "exif-dc-publisher": "Editorial",
        "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-logentry-legend": "Afegeix o suprimeix etiquetes {{PLURAL:$1|d'aquesta entrada del registres|de totes les entrades del registre}}",
+       "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": "''Cap''",
        "tags-edit-new-tags": "Etiquetes noves:",
index 2b08bee..46b81bb 100644 (file)
@@ -16,6 +16,7 @@
        "tog-hideminor": "Къайладаха кигийра нисдарш оц могӀама керла хийцамехь",
        "tog-hidepatrolled": "Къайладаха гӀаролладина нисдарш оц могӀама керла нисдаршкахь",
        "tog-newpageshidepatrolled": "Къайлаяха гӀароллайина агӀонаш оьцу могӀама керла агӀонашкахь",
+       "tog-hidecategorization": "Къайлаяха агӀонийн категореш",
        "tog-extendwatchlist": "Шорбина тӀехьажарна могӀам, ша беригге а, хийцамаш чубогӀуш, тӀехьаббина боцурш а",
        "tog-usenewrc": "Лелабе дика могӀам керла чу хийцамашна (оьшу JavaScript)",
        "tog-numberheadings": "Ша шех хlитто терахь корташна",
@@ -37,7 +38,7 @@
        "tog-shownumberswatching": "Гайта декъашхойн терахь, агӀо латийна болу шай тергаме могӀанан юкъа",
        "tog-oldsig": "Карара куьгтаӀорна:",
        "tog-fancysig": "Шен вики-къастаман куьгтаӀдар (ша шех хьажорг йоцуш)",
-       "tog-uselivepreview": "Ð\9bелайа Ñ\87еÑ\85ка Ñ\85Ñ\8cалÑ\85а Ñ\85Ñ\8cажа (JavaScript, Ð¼Ñ\83Ñ\85а Ñ\8e Ñ\85Ñ\8cажаÑ\80на)",
+       "tog-uselivepreview": "Ð\9bелае Ñ\87еÑ\85ка Ñ\85Ñ\8cалÑ\85а Ñ\85Ñ\8cажаÑ\80",
        "tog-forceeditsummary": "Дага даийта, нагахь нисйарх лаьцна чохь язйина яцахь",
        "tog-watchlisthideown": "Къайлаяха ас нисйинарш тергаме могӀам чура",
        "tog-watchlisthidebots": "Къайладаха тергаме могӀам чура ботан нисдинарш",
        "no-null-revision": "«$1» агӀона нисдар дан цаделира",
        "badtitle": "Цамегаш йолу цӀе",
        "badtitletext": "Дехарца йолу агӀонан цӀе нийса яц, йаьсса ю, хила мега нийса ца хӀоттийна меттаюкъар йа юкъарвики цӀе. Хила мега, цӀарца цамагош йолу символаш.",
-       "perfcached": "Лахара хаам схьаэцна кэша чура цундела тӀаьххьарлера хийцамаш гойтуш бац. Кэша чохь латтайо оцул $1  кӀезиг {{PLURAL:$1|дӀаяздар}}.",
-       "perfcachedts": "Лахара хаам схьаэцна кэша чура иза тӀаьххьара карлаяьлла $1. Кэша чохь латта до оцул $4 кӀезиг {{PLURAL:$4|дӀаяздар}}.",
+       "perfcached": "Лахара хаам схьаэцна кэша чура цундела тӀаьххьарлера хийцамаш гойтуш бац. Кэша чохь латтайо $1 кӀезиг {{PLURAL:$1|дӀаяздар}}.",
+       "perfcachedts": "Лахара хаам схьаэцна кэша чура иза тӀаьххьара карлаяьлла $1. Кэша чохь латта до $4 кӀезиг {{PLURAL:$4|дӀаяздар}}.",
        "querypage-no-updates": "ХӀинца хӀара агӀо карлаякхар дӀадайина ду.\nКхузахь гайтина болу хаамаш карла боккхур бац.",
        "viewsource": "Хьажар",
        "viewsource-title": "Агӏона $1 дуьххьарлера йозане хьажар",
        "createacct-benefit-body3": "{{PLURAL:$1|декъашхо|декъашхой}} тӀаьхьарчу хенахь",
        "badretype": "Ахьа язъен паролаш цхьатерра яц",
        "userexists": "Ахьа язъен декъашхочун цӀе йолуш ю, дехар до кхин цӀе харжар.",
-       "loginerror": "Ð\93Ó\80алаÑ\82 Ð´Ñ\83 Ð´ÐµÐºÑ\8aаÑ\88Ñ\85о Ð²Ð¾Ð²Ð·Ð°Ñ\80еÑ\85Ñ\8c/йовзаÑ\80еÑ\85Ñ\8c",
+       "loginerror": "ЦÓ\80е Ñ\8f Ð¿Ð°Ñ\80олÑ\8c Ð½Ð¸Ð¹Ñ\81а Ñ\8fÑ\86",
        "createacct-error": "ДӀаяздар кхуллуш гӀалат ду",
        "createaccounterror": "Декъашхочун дӀаяздар кхолла йиш яц: $1",
        "nocookiesnew": "Декъашхочун дӀаяздар ду амма системин чохь вац/яц. Декъашхой чу гӀош {{SITENAME}} «cookies» лелош ю. Хьа «cookies» лелаян магийна дац дехар до и магийтина керлачу цӀарца а паролаца а системин чугӀо.",
        "nocookieslogin": "{{SITENAME}} лелош ю «cookies» декъашхой системин  чуболучу хенахь. Ахьа иш дӀайаьйина.",
        "nocookiesfornew": "Хьост хьажа йиш цахиларна декъашхочун дӀаяздар цакхоьллина. Хьажа «cookies» латина юьй такха агӀо карлаяьккхина юху гӀорта.",
        "nocookiesforlogin": "{{int:nocookieslogin}}",
-       "noname": "Ð\90Ñ\85Ñ\8cа Ð¼Ð°Ð³Ð¸Ð¹Ñ\82ина Ð¹Ð¾Ð»Ñ\83 Ð´ÐµÐºÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н Ñ\86Ó\80е Ð±Ð¸Ð»Ð³Ð°Ð» йина яц.",
+       "noname": "Ð\90Ñ\85Ñ\8cа Ð´ÐµÐºÑ\8aаÑ\88Ñ\85оÑ\87Ñ\83н Ñ\86Ó\80е Ð»Ð°Ñ\80Ñ\82Ó\80аÑ\85Ñ\8c Ñ\8fзйина яц.",
        "loginsuccesstitle": "Хьан пароль тӀеэца, марша догӀила Википеди чу!",
        "loginsuccess": "Хlинца ахьа болх бó оцу цlарца $1.",
        "nosuchuser": "Иштта $1 цӀе йолуш декъашхочун дӀаяздар дац.\nДекъашхой цӀерш хаалуш ю дӀаяздарца элпаш.\nНийса юьй хьажа цӀе я [[Special:UserLogin/signup|дӀаяздар кхолла керла]].",
        "nosuchusershort": "Ишта «$1» цӀе йолу декъашхо вац/яц. Хьажа цӀе нийса язйина юй.",
-       "nouserspecified": "Ахьа декъашхочун цӀе билгал ян езаш ю.",
+       "nouserspecified": "Ахьа декъашхочун цӀе язъян езаш ю.",
        "login-userblocked": "ХӀара декъашхо блоктоьхна ву/ю. Системин чувала/яла магийна дац.",
        "wrongpassword": "Ахьа язйина йолу пароль нийса яц. Хьажа юху цхьаъз.",
        "wrongpasswordempty": "Дехар до, язъе еса йоцу пароль.",
        "right-blockemail": "Цамагдо декъашхошка хааман кехаташ кхехьийта",
        "right-hideuser": "декъашхочун цӀе а, и лечкъо а цамагор",
        "right-ipblock-exempt": "IP блоктохаршна чекхбовлар, диапазонийн шаблоктохаршна а, блоктохаршна а",
-       "right-proxyunbannable": "проксен автоматически блоктохаран чекхбовлар",
        "right-unblockself": "ша шин блокдӀаяккхар",
        "right-protect": "АгӀо ларъяран хийцар а, ларйина агӀо нисяр а",
        "right-editprotected": "«{{int:protect-level-sysop}}» бахьанца ларйина агӀонаш нисъяр",
        "right-sendemail": "кхечу декъашхошка электронан хаамаш кхехьийта",
        "right-passwordreset": "пароль хийцарца электроннан хаамашка хьажар",
        "right-managechangetags": "Хаамийн базан чохь [[Special:Tags|билгалонаш]] кхолла а, дӀаяха а",
+       "grant-generic": "Бакъонийн гулам «$1»",
+       "grant-group-page-interaction": "АгӀонашца зӀе",
+       "grant-group-file-interaction": "Медиафайлашца зӀе",
+       "grant-group-email": "ДӀадахьийта кехат",
+       "grant-group-high-volume": "Дешдерг сиха дар",
+       "grant-blockusers": "Декъашхойн блоктохар а, блокдӀаяккхар а",
+       "grant-createaccount": "Декъашхочун дӀаяздарш кхоллар",
+       "grant-createeditmovepage": "АгӀонашна хийцам бар",
+       "grant-delete": "АгӀонаш а, нисдарш а, декъашхойн дӀаяздар а дӀадахар",
+       "grant-editinterface": "MediaWiki цӀерийн ана а, долара CSS/JavaScript нисъяр",
+       "grant-editmycssjs": "Долара CSS/JavaScript нисъяр",
+       "grant-editmyoptions": "Табе хьай гӀирс",
+       "grant-editmywatchlist": "Хьан тергаме могӀам табар",
+       "grant-editpage": "Йолуш йолу агӀонаш таяр",
+       "grant-editprotected": "Ларйина агӀонаш таяр",
+       "grant-highvolume": "Сиха тадарш дар",
+       "grant-patrol": "АгӀонийн хийцамаш патруль яр",
+       "grant-protect": "АгӀонаш ларъяр а, ларъяр дӀадаккхар а",
+       "grant-rollback": "АгӀонийн хийцамаш юхабахар",
+       "grant-sendemail": "Кхечу декъашхошка электронан хаамаш кхехьийта",
+       "grant-uploadeditmovefile": "Чуяхар а, хийцар а файлийн цӀерш хийцар а",
+       "grant-uploadfile": "Чуяха керла файлаш",
+       "grant-viewdeleted": "ДӀабаьхьна хаамашка хьажар",
+       "grant-viewmywatchlist": "Шен тергаме могӀаме хьажар",
        "newuserlogpage": "Декъашхой дӀабазбина тептар",
        "newuserlogpagetext": "Дукху хан йоцуш дӀабазбелла декъашхойн могӀам",
        "rightslog": "Декъашхочун бакъона тéптар",
        "recentchanges-label-plusminus": "байташкахь барам хийцар",
        "recentchanges-legend-heading": "'''Легенда:&nbsp;'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (хьажа кхин [[Special:NewPages|керла агӀонийн могӀа]])",
+       "recentchanges-submit": "Гайта",
        "rcnotefrom": "Лахахь гайтина тӀера <strong>$2</strong> (хийцамаш <strong>$1</strong> кӀезиг).",
        "rclistfrom": "Гайта хийцам {{CURRENTYEAR}} шаран {{CURRENTDAY}} {{CURRENTMONTHNAMEGEN}} {{CURRENTTIME}} бина болу",
        "rcshowhideminor": "$1 кегийра нисдарш",
        "rcshowhidemine": "$1 айхьа нисдинарш",
        "rcshowhidemine-show": "Гайта",
        "rcshowhidemine-hide": "Къайладаха",
+       "rcshowhidecategorization": "$1 агӀонийн категореш",
+       "rcshowhidecategorization-show": "Гайта",
+       "rcshowhidecategorization-hide": "Къайлаяккха",
        "rclinks": "Гайта тӀаьххьарлерачу $2 дийнахь бина болу $1 хийцамаш\n<br />$3",
        "diff": "башхалла",
        "hist": "истори",
        "boteditletter": "б",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|тӀехьожу декъашхо|тӀехьожу декъашхой}}]",
        "rc_categories": "Категори чура бен (къасторг «|»)",
-       "rc_categories_any": "Муьлхаа",
+       "rc_categories_any": "Муьлха а хаьржиначух",
        "rc-change-size-new": "Хийцам бин чул тӀехьа болу барам: $1 {{PLURAL:$1|байт}}",
        "newsectionsummary": "/* $1 */ Керла хьедар",
        "rc-enhanced-expand": "Гайта мадарра",
        "recentchangeslinked-summary": "ХӀара хийцам биначу агӀонийн могӀам бу, тӀетовжар долуш хьагучу агӀон (я хьагойтуш йолучу категорена).\nАгӀонаш юькъа йогӀуш йолу хьан [[Special:Watchlist|тергаме могӀам чохь]] '''къастийна ю'''.",
        "recentchangeslinked-page": "АгӀон цӀе:",
        "recentchangeslinked-to": "Кхечу агӀор, гайта хийцамаш агӀонашца, хӀоттийначу агӀонтӀе хьажорг йолуш",
+       "recentchanges-page-added-to-category": "[[:$1]] категори чу тоьхна",
        "upload": "Файл чуяккхар",
        "uploadbtn": "Файл чуяккхар",
        "reuploaddesc": "Юху гӀо файл чуйоккху агӀоне",
        "upload-tryagain": "ДӀадахьийта хийцина файлах лаьцнарг",
        "uploadnologin": "ЦӀарца доцуш",
-       "uploadnologintext": "Серверан чу файлаш яха хьо $1.",
+       "uploadnologintext": "Серверан чу файлаш яха ахьа дан дезарг ду $1.",
        "uploaderror": "Файл чуяккхаран гӀалат",
        "upload-recreate-warning": "'''Тегам бе: иштта цӀе йолу файл дӀаяьккхина я цӀе хийцина.'''\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* '''<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-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-select-file": "Харжа файл",
+       "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": "файлан цӀе",
+       "foreign-structured-upload-form-label-own-work": "ХӀара сан долара болх бу",
        "foreign-structured-upload-form-label-infoform-categories": "Категореш",
        "foreign-structured-upload-form-label-infoform-date": "Терахь",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "ХӀара файл {{SITENAME}} сайтан бакъонашца чуйоккхила делахь, хьайн таро ю [[Special:Upload|хӀара агӀо]] лелаян.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Аса со хӀокху файлан авторан бакъонаш долахь ерг хилар бакъдо, цундела хӀара файл [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0] лицензица Викигуламан чуяккха бакъо ю, цул совнах [https://wikimediafoundation.org/wiki/ хӀокху хьолаца лело] а мега.",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "ХӀокху файлан авторан бакъонаш хьай яцахь, я хьайна кхечу лицензица яржо лууш делахь хьажа [https://commons.wikimedia.org/wiki/Special:UploadWizard Викигуламера чуяхаран говзанча] лелон тароне.",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "ХӀара файл {{SITENAME}} сайтан бакъонашца чуйоккхила делахь, хьайн таро ю [[Special:Upload|хӀара агӀо]] лелаян.",
+       "foreign-structured-upload-form-3-label-yes": "ХӀаъ",
+       "foreign-structured-upload-form-3-label-no": "ХӀахӀа",
        "backend-fail-stream": "ДӀаяккха цатарло файл «$1».",
        "backend-fail-backup": "Таро яц файлан $1 тӀаьхьалонан копиян.",
        "backend-fail-notexists": "Файл $1 яц.",
        "mostrevisions": "Сих сиха нисйина йолу агӀонаш",
        "prefixindex": "Хьалха агӀонийн цӀерш хӀотто еза",
        "prefixindex-namespace": "Хьалха агӀонийн цӀерш хӀотто еза («{{ns:$1}}»)",
+       "prefixindex-submit": "Гайта",
        "prefixindex-strip": "Хиламийн могӀам чура префикс къайлаяккха",
        "shortpages": "Боца яззамаш",
        "longpages": "Беха яззамаш",
        "usereditcount": "$1 {{PLURAL:$1|нисдар|нисдарш}}",
        "usercreated": "{{GENDER:$3|дӀавазвелла|дӀаязелла}} $1 $2",
        "newpages": "Керла агӀонаш",
+       "newpages-submit": "Гайта",
        "newpages-username": "Декъашхо:",
        "ancientpages": "Шира агӀонаш",
        "move": "ЦӀе хийца",
        "specialloguserlabel": "Декъашхо:",
        "speciallogtitlelabel": "Ӏалашо (цӀе я декъашхо):",
        "log": "Тéптарш",
+       "logeventslist-submit": "Гайта",
        "all-logs-page": "Дерриге тӀекхочучехь долу тептарш",
        "alllogstext": "Массо юкъара журлийн могӀам. {{SITENAME}}.\nШуьга харжалур бу хилам оцу тептаре хьаьжжина, декъашхочун цӀе (дӀаяздар диц а цадеш) я цо хьейина агӀонаш (ишта дӀаяздар а диц цадеш).",
        "logempty": "Тептарш чохь хӀокху агӀона дӀаяздарш дац.",
        "cachedspecial-viewing-cached-ttl": "Хьо хьоьжу агӀона верси кэш чура ю, иза карлаяьккхина хила мега $1 хьалха.",
        "cachedspecial-refresh-now": "Хьажа тӀехьарчу версега.",
        "categories": "Категореш",
+       "categories-submit": "Гайта",
        "categoriespagetext": "{{PLURAL:$1|1=Лахара категореш чохь ю|Лахара категореш чохь ю}} агӀонаш я медиа-файлаш.\nКхузахь гойтуш яц [[Special:UnusedCategories|лелош йоцу категореш]].\nКхин дӀа [[Special:WantedCategories| хийла еза категореш]].",
        "categoriesfrom": "Гучé яха категореш, тӀера:",
        "special-categories-sort-count": "нисъе дукхаллица",
        "listgrouprights-namespaceprotection-header": "ЦӀеран анан бехкам",
        "listgrouprights-namespaceprotection-namespace": "ЦӀерийн ана",
        "listgrouprights-namespaceprotection-restrictedto": "Декъашхочун хийцамаш бан таро хуьлуьйту бакъонаш",
+       "listgrants-grant": "Бакъо",
+       "listgrants-rights": "Бакъонаш",
        "trackingcategories": "Хьожуш йолу категореш",
        "trackingcategories-summary": "ХӀокху агӀонгахь ю хьожуш йолу категореш, MediaWikiс тӀеюзаш ю уьш. {{ns:8}} цӀерийн меттигера системин хаам хийцина цера цӀерш хийца йиш ю.",
        "trackingcategories-msg": "Категореш зер",
        "wlnote": "Гойту <strong>$2</strong> {{plural:$2|сахьтчохь}} бина {{PLURAL:$1|тӀеххьара '''$1''' хийцам}}, хан $3 $4",
        "wlshowlast": "Гайта тӀаьххьара $1 сахьт $2 де",
        "watchlistall2": "массо",
+       "watchlist-hide": "Къайлаяккха",
+       "watchlist-submit": "Гайта",
+       "wlshowtime": "Гаран хенан мур:",
+       "wlshowhideminor": "жима нисдарш",
+       "wlshowhidebots": "Боташ",
+       "wlshowhideliu": "ДӀабазбелла декъашхой",
+       "wlshowhideanons": "ЦӀе хьулйина декъашхой",
+       "wlshowhidepatr": "хьажжина нисдарш",
+       "wlshowhidemine": "Сан нисдарш",
        "watchlist-options": "Тергаме могlаман гlирс нисбар",
        "watching": "Тергаме мlогаман юкъаяккха…",
        "unwatching": "Тергаме могӀанан чура дӀаяккхар…",
        "delete-confirm": "$1 — дӀаяккхар",
        "delete-legend": "ДӀаяккхар",
        "historywarning": "<strong>Тергам бе:</strong> Хьо дӀаяккха гӀертачу агӀона, нисдарийн истори ю, $1 {{PLURAL:$1|верси}} йолуш:",
+       "historyaction-submit": "Гайта",
        "confirmdeletetext": "Хьо гӀерта агӀо я файл дӀаяккха '''дехар до''', дӀаяккхале хьалха хьажа [[{{MediaWiki:Policy-url}}|кхуза]].",
        "actioncomplete": "Дешдерг кхочушдина",
        "actionfailed": "Кхочушъ дина дац",
        "sp-contributions-newbies": "Гайта бекъ къинхьегам, керла дlабазбиначара бина болу",
        "sp-contributions-newbies-sub": "Керла декъашхойн дӀаяздаршкара",
        "sp-contributions-newbies-title": "Дукху хан йоцуш кхоьллинчу декъашхойн дӀаяздарийн къинхьегам",
-       "sp-contributions-blocklog": "блоктоьхарш",
+       "sp-contributions-blocklog": "блокÑ\82оÑ\8cÑ\85наÑ\80Ñ\88",
        "sp-contributions-suppresslog": "Декъашхочун дӀабаьккхина къинхьегам",
        "sp-contributions-deleted": "дӀадяхна нийсдарш",
        "sp-contributions-uploads": "Файлаш",
        "whatlinkshere-hidelinks": "$1 хьажорг",
        "whatlinkshere-hideimages": "$1 файлийн хьажоргаш",
        "whatlinkshere-filters": "Литтарш",
+       "whatlinkshere-submit": "Кхочушдé",
        "autoblockid": "Ша блоккхетар #$1",
        "block": "Декъашхочун блоктохар",
        "unblock": "ДекъашхонтӀера блокдӀаякхар",
        "change-blocklink": "хийцам бе блоктохарна",
        "contribslink": "къинхьегам",
        "emaillink": "дӀадахьийта кехат",
-       "blocklogpage": "Блоктоьхарш болу тептар",
+       "blocklogpage": "Ð\91локÑ\82оÑ\8cÑ\85наÑ\80Ñ\88 Ð±Ð¾Ð»Ñ\83 Ñ\82епÑ\82аÑ\80",
        "blocklog-showlog": "{{GENDER:$1|ХӀокху декъашхочун хьалхо блоктоьхна хила}}.\nЛахахь гойту блоктохарш долу тептар:",
        "blocklogentry": "блоктоьхна [[$1]] цхьана ханна $2 $3",
        "reblock-logentry": "Хийцина  блоктоьхна хан [[$1]] $2 $3",
-       "blocklogtext": "Ð\91локÑ\82оÑ\85аÑ\80Ñ\88на Ð° Ð±Ð»Ð¾ÐºÐ´Ó\80аÑ\8fкÑ\85аÑ\80Ñ\88на Ð° Ñ\82епÑ\82аÑ\80. Ð¨Ð° Ð±Ð»Ð¾ÐºÐºÑ\85еÑ\82аÑ\88 Ð´Ð¾Ð»Ñ\83 IP-адÑ\80еÑ\81аÑ\88 ÐºÑ\85Ñ\83заÑ\85Ñ\8c Ð³Ð¾Ð¹Ñ\82Ñ\83Ñ\88 Ð´Ð°Ñ\86. Ð\9aÑ\85ин. [[Special:BlockList|Ñ\85Ó\80ийнÑ\86а Ð±Ð»Ð¾ÐºÑ\82оÑ\8cÑ\85а Ð±Ðµрш]].",
+       "blocklogtext": "Ð\91локÑ\82оÑ\85аÑ\80Ñ\88на Ð° Ð±Ð»Ð¾ÐºÐ´Ó\80аÑ\8fкÑ\85аÑ\80Ñ\88на Ð° Ñ\82епÑ\82аÑ\80. Ð¨Ð° Ð±Ð»Ð¾ÐºÐºÑ\85еÑ\82аÑ\88 Ð´Ð¾Ð»Ñ\83 IP-адÑ\80еÑ\81аÑ\88 ÐºÑ\85Ñ\83заÑ\85Ñ\8c Ð³Ð¾Ð¹Ñ\82Ñ\83Ñ\88 Ð´Ð°Ñ\86. Ð\9aÑ\85ин. [[Special:BlockList|Ñ\85Ó\80инÑ\86а Ð±Ð»Ð¾ÐºÑ\82оÑ\8cÑ\85нарш]].",
        "unblocklogentry": "дӀаяькхинаблок $1",
        "block-log-flags-anononly": "Къайлаха берш",
        "block-log-flags-nocreate": "цамагдо керла дӏаяздарш кхоллар",
        "allmessagescurrent": "Карарчу хенан йоза",
        "allmessagestext": "ХӀара «MediaWiki» цӀерийн меттигера системан хаамийн могӀа бу.\nХьайна MediaWiki тая лууш делахь, дехар до, проект [//translatewiki.net translatewiki.net] [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation юьйцучу хьажа].",
        "allmessagesnotsupportedDB": "'''$wgUseDatabaseMessages''' дӀаяйина хилар бахьнехь хӀара агӀо дӀачӀаьгӀна ю.",
-       "allmessages-filter-legend": "Литтар",
-       "allmessages-filter": "Литтар оцу хьола хийцамца:",
+       "allmessages-filter-legend": "Луьттург",
+       "allmessages-filter": "Луьттург оцу хьола хийцамца:",
        "allmessages-filter-unmodified": "Хийцан йоцурш",
        "allmessages-filter-all": "Массо",
        "allmessages-filter-modified": "Хийцнарш",
-       "allmessages-prefix": "Литтар оцу дешахьалхе:",
+       "allmessages-prefix": "Луьттург оцу дешахьалхе:",
        "allmessages-language": "Мотт:",
        "allmessages-filter-submit": "Дехьа гӀо",
        "allmessages-filter-translate": "Гочйе",
        "tooltip-pt-watchlist": "Ахьа тергам бо агӀонийн хийцаман могӀам",
        "tooltip-pt-mycontris": "Хьан нисдаран могӀам",
        "tooltip-pt-anoncontribs": "ХӀокху IP-адресца бина хийцамийн могӀам",
-       "tooltip-pt-login": "Кхузахь дӀаяздар кхолла мега, амма иза тӀедожийна дац.",
+       "tooltip-pt-login": "Кхузахь системин чудала мега, амма иза тӀедожийна дац.",
        "tooltip-pt-logout": "Дlадерзадо болх бар",
        "tooltip-pt-createaccount": "Шу йиш ю дӀаяздар кхоьллина системин чудаха, амма иза тӀедожийна дац.",
        "tooltip-ca-talk": "Дийцаре агlон чулацам",
        "anonusers": "{{PLURAL:$2|1=цӀе хьулйина декъашхо|цӀе хьулйина декъашхой}} {{grammar:genitive|{{SITENAME}}}} $1",
        "creditspage": "Баркаллаш",
        "nocredits": "Бац декъашхойн могӀам хӀокху яззамца",
-       "spamprotectiontitle": "СовбилаÑ\80на Ð»Ð¸Ñ\82Ñ\82аÑ\80",
+       "spamprotectiontitle": "Спам-лÑ\83Ñ\8cÑ\82Ñ\82Ñ\83Ñ\80г",
        "spamprotectiontext": "Хьо дӀаязъян гӀерта агӀо спам-литтаро дӀакъоьвлина.\nЦуна бахьана хила там бу агӀона чохь зулам литтаран чутоьхна йолу хьажорг хилар.",
        "spambot_username": "Спам дӀацӀаняр",
        "pageinfo-title": "Хаамаш цу «$1»",
        "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",
        "file-nohires": "Кхи йоккха гlоле башхо яц.",
        "svg-long-desc": "SVG-файл, лартӀахь ю $1 × $2 пиксель, файлан барам: $3",
        "svg-long-desc-animated": "Анимироват йина SVG-файл, номиналан $1 × $2 пиксель, файлан барам: $3",
+       "svg-long-error": "нийса йоцу SVG-файл: $1",
        "show-big-image": "Оригиналан файл",
        "show-big-image-preview": "Барам хьажале: $1.",
        "show-big-image-other": "{{PLURAL:$2|1=Кхин шоралла|Кхин шоралла}}: $1.",
        "file-info-png-frames": "$1 {{PLURAL:$1|кадр|кадраш}}",
        "newimages": "Керлачу файлийн галерей",
        "newimages-summary": "ХӀокху белхан агӀона чохь гойтуш ю дукха хан йоццуш чуяьхна файлаш.",
-       "newimages-legend": "Литтар",
+       "newimages-legend": "Луьттург",
        "newimages-showbots": "Гайта боташ чуяьхна файлаш",
+       "newimages-hidepatrolled": "Къайлаяха патруль йина файлаш",
        "noimages": "Суьрташ дац.",
        "ilsubmit": "Лахар",
        "bydate": "терахьашца",
        "hours-abbrev": "$1 сахь.",
        "seconds": "{{PLURAL:$1|$1 секунд|$1 секунд}}",
        "minutes": "{{PLURAL:$1|$1 минот|$1 минот}}",
-       "hours": "{{PLURAL:$1|$1 сахьт|$1 сахьт}}",
-       "days": "{{PLURAL:$1|$1 де|$1 де}}",
+       "hours": "{{PLURAL:$1|$1 сахьт}}",
+       "days": "{{PLURAL:$1|$1 де}}",
        "weeks": "{{PLURAL:$1|$1 кӀира}}",
        "months": "{{PLURAL:$1|$1 бутт|$1 бутт}}",
        "years": "{{PLURAL:$1|$1 шо|$1 шо}}",
        "version-entrypoints-header-url": "URL",
        "version-entrypoints-articlepath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgArticlePath АгӀона тӀе некъ]",
        "version-entrypoints-scriptpath": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:$wgScriptPath Скриптан тӀе некъ]",
+       "version-libraries": "ДӀахӀоттийна библиотекаш",
+       "version-libraries-library": "Библиотека",
+       "version-libraries-version": "Верси",
+       "version-libraries-license": "Лицензи",
        "version-libraries-description": "Цуьнах лаьцна",
        "version-libraries-authors": "Автораш",
        "redirect": "Декъашхочун файлан тӀера дӀасхьажор",
        "specialpages-group-spam": "Спаман дуьхьала гӀирсаш",
        "blankpage": "Еса агӀо",
        "tags": "Болш болу хийцаман къастам",
-       "tag-filter": "Къастам [[Special:Tags|хьажар]]:",
+       "tag-filter": "[[Special:Tags|Билгалонаш]] луьттург:",
        "tag-filter-submit": "Литта",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|1=Билгало|Билгалонаш}}]]: $2)",
        "tags-title": "Билгалонаш",
        "mediastatistics-header-text": "Йозанан",
        "mediastatistics-header-executable": "Кхочушдийриш",
        "mediastatistics-header-archive": "Архиваш",
+       "mediastatistics-header-total": "Массо файлаш",
        "json-error-unknown": "JSON бала бу. ГӀалат: $1",
        "json-error-syntax": "Синтаксин гӀалат",
        "headline-anchor-title": "ХӀокху дакъан тӀе хьажорг",
        "special-characters-title-endash": "юкъар сиз",
        "special-characters-title-emdash": "деха сиз",
        "special-characters-title-minus": "хьаьрк минус",
-       "mw-widgets-titleinput-description-redirect": "ДӀасхьажорг $1 тӀе"
+       "mw-widgets-titleinput-description-redirect": "ДӀасхьажорг $1 тӀе",
+       "randomrootpage": "Цахууш нисъелла ораман агӀо"
 }
index f1a72d3..1a083ca 100644 (file)
        "cannotdelete-title": "ناکرێ پەڕەی «$1» بسڕدرێتەوە",
        "delete-hook-aborted": "سڕینەوە لە لایەن قولاپەوە ھەڵوەشێنرایەوە.\nھۆکارەکەی لەبەر دەست نییە.",
        "no-null-revision": "نەتوانرا پێداچوونەوەی خاڵیی بۆ پەڕەی \"$1\" درووست بکرێت",
-       "badtitle": "سەردێڕی خراپ",
+       "badtitle": "سەرناوی خراپ",
        "badtitletext": "سەرناوی پەڕەی داواکراو بەتاڵە، واڵایە یان سەرناوێکی نێوان-زمانی یان نێوانی-ویکییە کە بە شێوەیەکی ھەڵە بەستەری بۆ دراوە.\nڕەنگە یەک یان چەند کاراکتەری تێدا بێت کە ناکرێت لە سەرناوەکاندا بەکار بھێنرێت.",
        "perfcached": "داتای خوارەوە پاشەکەوتکراوەیە و لەوانەیە بەڕۆژنەکرابێتەوە. لانی زۆر {{PLURAL:$1|یەک ئەنجام|$1 ئەنجام}} لە cacheدا لەبەردەستدایە.",
        "perfcachedts": "داتای خوارەوە cacheکراوە و دوایین جار لە $1 نوێ کراوەتەوە. لە cacheدا لانی زۆر {{PLURAL:$4|یەک ئەنجام|$4 ئەنجام}} لەبەردەستە.",
        "passwordremindertext": "کەسێک (لەوانەیە خۆت، لە ئای‌پی ئەدرەسی $1) داوای تێپەڕوشەیەکی نوێی کردووە بۆ {{SITENAME}} ($4). تێپەڕوشەیەکی کاتی بۆ بەکارهێنەر «$2» دروستکراو و وەک «$3» دانراوه. ئەگەر ئەمە داخوازی تۆ بووە، پێویستت بەوەیە ئێستا بچیتە ژوورەوە و تێپەڕوشەیەکی نوێ هەڵبژێریت. ماوەی‌ تێپەڕوشە کاتییەکەت لە {{PLURAL:$5|یەک ڕۆژدا|$5 ڕۆژدا}} بەسەردەچێت.\n\nئەگەر کەسێکی تر ئەم داوایەی کردووە یان تێپەڕوشەکەت هاتووەتەوە بیرت و ئیتر پێویستت بە گۆڕانی نییە، دەتوانی گوێ بەم پەیامە نەدەیت و لە تێپەڕوشە کۆنەکەت کەڵک وەربگری.",
        "noemail": "ھیچ ئەدرەسێکی ئیمەیل تۆمار نەکراوە بۆ بەکارھێنەر « $1 ».",
        "noemailcreate": "دەبێ ناونیشانێکی دروستی ئیمەیل بنووسی",
-       "passwordsent": "تێپەڕوشەیەکی نوێ ناردرا Ø¨Û\86 Ø¦Û\95درÛ\95سÛ\8c Ø¦Û\8cÙ\85Û\95Û\8cÙ\84Û\8c ØªÛ\86Ù\85ارکراÙ\88Û\8c Â«$1».\nتکاÛ\8cÛ\95 Ø¯Ù\88اÛ\8c Ù\88Û\95رگرتÙ\86Û\8c Ø¯Û\8cساÙ\86 Ø¨Ú\86Û\86 Ú\98Ù\88Ù\88رÛ\95Ù\88Û\95.",
+       "passwordsent": "تێپەڕوشەیەکی نوێ نÛ\8eررا Ø¨Û\86 Ø¦Û\95درÛ\95سÛ\8c Ø¦Û\8cÙ\85Û\8eÙ\84Û\8c ØªÛ\86Ù\85ارکراÙ\88Û\8c Â«$1».\nتکاÛ\8cÛ\95 Ø¯Ù\88اÛ\8c Ù\88Û\95رگرتÙ\86Û\8cØ\8c Ø¯Û\8cساÙ\86 Ø¨Ú\86Û\86 Ú\98Ù\88Ù\88رÛ\95Ù\88Û\95.",
        "blocked-mailpassword": "ئادرەسی ئای‌پی تۆ بۆ دەستکاری کردن بەستراوه بۆیە بۆ بەرگری لە بەکارهێنانی نابەجێ ئەنجامی گەڕانەوەی تێپەڕوشە ڕیگە نەدراوە.",
-       "eauthentsent": "ئÛ\8cÙ\85Û\95Û\8cÙ\84Û\8eÚ©Û\8c Ù¾Ø´ØªÚ\95استکردÙ\86Û\95Ù\88Û\95 Ø¨Û\86 Ù\86اÙ\88Ù\86Û\8cشاÙ\86Û\8c Ø¦Û\8cÙ\85Û\95Û\8cÙ\84Û\8c Ø¯Û\8cارÛ\8cکراÙ\88 Ù\86اردرا.\nÙ¾Û\8eØ´ Ø¦Û\95Ù\88Û\95Û\8c Ø¦Û\8cÙ\85Û\95Û\8cÙ\84Û\8c ØªØ± Ø¨Û\86 Ø¦Û\95Ù\85 Ù\87Û\95Ú\98Ù\85ارÛ\95 Ø¨Ù\86Û\8eردرÛ\8eØ\8c Ø¨Û\86 Ù¾Ø´ØªÚ\95استکردÙ\86Û\95Ù\88Û\95Û\8c Ø¦Û\95Ù\85Û\95Û\8c Ø¦Û\95Ù\85 Ù\87Û\95Ú\98Ù\85ارÛ\95 Ø¨Û\95Ú\95استÛ\8c Ú¾Û\8c ØªÛ\86Û\8cÛ\95Ø\8c Ø¯Û\95بÛ\8e Ù¾Û\95Û\8cÚ\95Û\95Ù\88Û\8c Ú\95Û\8eکارÛ\95کاÙ\86Û\8c Ù\86اÙ\88 Ø¦Û\8cÙ\85Û\95Û\8cÙ\84Û\95Ú©Û\95 Ø¨Ú©Û\95Û\8cت.",
+       "eauthentsent": "ئÛ\8cÙ\85Û\8eÙ\84Û\8eÚ©Û\8c Ù¾Ø´ØªÚ\95استکردÙ\86Û\95Ù\88Û\95 Ø¨Û\86 Ù\86اÙ\88Ù\86Û\8cشاÙ\86Û\8c Ø¦Û\8cÙ\85Û\8eÙ\84Û\8c Ø¯Û\8cارÛ\8cکراÙ\88 Ù\86Û\8eررا.\nÙ¾Û\8eØ´ Ø¦Û\95Ù\88Û\95Û\8c Ø¦Û\8cÙ\85Û\95Û\8cÙ\84Û\8c ØªØ± Ø¨Û\86 Ø¦Û\95Ù\85 Ú¾Û\95Ú\98Ù\85ارÛ\95 Ø¨Ù\86Û\8eررÛ\8eتØ\8c Ø¯Û\95بÛ\8eت Ù¾Û\95Û\8cÚ\95Û\95Ù\88Û\8c Ú\95Û\8eکارÛ\95کاÙ\86Û\8c Ù\86اÙ\88 Ø¦Û\8cÙ\85Û\8eÙ\84Û\95Ú©Û\95 Ø¨Ú©Û\95Û\8cت Ø¨Û\86 Ù¾Ø´ØªÚ\95استکردÙ\86Û\95Ù\88Û\95Û\8c Ø¦Û\95Ù\88Û\95Û\8c Ú©Û\95 Ø¦Û\95Ù\85 Ù\87Û\95Ú\98Ù\85ارÛ\95 Ø¨Û\95 Ú\95استÛ\8c Ú¾Û\8c ØªÛ\86Û\8cÛ\95.",
        "throttled-mailpassword": "ئیمەیلێکی ڕیکخستنەوەی تێپەڕوشە لە ماوەی {{PLURAL:$1|ساعەت}}ی ڕابردوودا نێردراوە.\nبۆ ڕێگری لە بەکارھێنانی خراپ، ھەر {{PLURAL:$1|ساعەت}} تاکە یەک ئیمەیلی ڕیکخستنەوەی تێپەڕوشە دەنێردرێت.",
        "mailerror": "هەڵە ڕوویدا لە ناردنی ئیمەیل: $1",
        "acct_creation_throttle_hit": "بینەرانی ویکی بەکەڵک وەرگرتن لەم ئای‌پی ئەدرەسەی تۆ لە ڕۆژانی ڕابردوودا، دەستیان کردە بە درووست‌کردنی {{PLURAL:$1|هەژمارە}}، کە زۆرینە ڕیگەپێدان لە یەک ماوە‌دایە.\nوەک ئەنجامی ئەو ڕووداوە، ئەو بینەرانی لەم ئای‌پی ئەدرەسە کەڵک وەر دەگرن لەم کاتەدا ناتوانن هەژماری دیکە درووست‌بکەن.",
        "subject-preview": "پێشبینینی بابەت/سەردێڕ:",
        "blockedtitle": "به‌کار هینه‌ر له‌کار خراوه",
        "blockedtext": "'''ناوی بەکارهێنەری یان ئای‌پی ئەدرەسی تۆ بەربەست‌ کراوە.'''\n\nبەربەست لە لایەن $1 کراوە.\nهۆکاری بەربەست کردن ''$2''ە.\n\n* دەستپێکی بەربەست‌کران: $8\n* کۆتایی هاتنی بەربەست‌کران: $6\n* بابەتی بەربەست: $7\n\nبۆ وتووێژ سەبارەت بە بەربەست‌کرانەکە دەبێ پەیوەندی بکەی بە $1 یان یەکێ دی لە [[{{MediaWiki:Grouppage-sysop}}|بەڕێوبەران]].\nلە بیرت بێ تاکوو ئیمەیل ئەدرەسێکی بڕوا پێ‌کراو لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]] ڕاچاو نەکەی، نابێت لە هەلی «ئیمەیل ناردن بۆ ئەم بەکارهێنەرە» کەڵک وەر بگری؛ کەڵک وەرگرتن لەوە بەربەست نەکراوە بۆت.\n\nئای‌پی ئەدرەسی ئێستای تۆ $3 و پێناسەی بەربەست‌کراو #$5.\nتکایە لە هەر پرس و داواکاریەکت‌دا هەموو وردەکاریەکانی سەرەوە بگونجێنە.",
-       "autoblockedtext": "ئای‌پی ئەدرەسی تۆ بە شێوەی خۆکار بەربەست کراوە چونکە لە لایەن بەکارهێنەرێکی دی کەڵکی لێ وەرگیراوە کە لە لایەن $1 بەربەست کراوە.<br />\nئەمە هۆکارەکەیەتی:<br /><br />\n:''$2''<br /><br />\n* دەستپێکی بەربەست‌کران: $8<br />\n* کۆتایی هاتنی بەربەست‌کران: $6<br />\n* بابەتی بەربەست: $7<br /><br />\n\nدەبێ پەیوەندی بکەی بە $1 یان یەکێ دی لە [[{{MediaWiki:Grouppage-sysop}}|بەڕێوبەران]] بۆ وتووێژ سەبارەت بە بەربەست‌کرانەکە.<br /><br />\n\nلە بیرت بێ تاکوو ئەمەیل ئەدرەسێکی بڕوا پێ‌کراو لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]] ڕاچاو نەکەی، نابێت لە هەلی \"ئی‌مەیل ناردن بۆ ئەم بەکارهێنەرە\" کەڵک وەر بگری؛ کەڵک وەرگرتن لەوە بەربەست نەکراوە بۆت.<br /><br />\n\nئای‌پی ئەدرەسی ئێستای تۆ $3 و پەێناسەی بەربەست‌کراو #$5.<br />\nتکایە لە هەر پرس و داواکاریەکت‌دا هەموو وردەکاریەکانی سەرەوە بگونجێنە.",
+       "autoblockedtext": "ناونیشانی IPی تۆ بە شێوەی خۆکارانە بەرگیری لێ کراوە چوونکە بەکارھێنەرێکی دیکە بە خراپی بە کاری ھێناوە و بە دەستی $1 بەرگیری لێ کراوە.\nبەم ھۆکارەوە:\n\n:<em>$2</em>\n\n* دەست پێ کردنی بەرگیری: $8\n* بە سەر چوونی بەرگیری: $6\n* Intended blockee: $7\n\nدەتوانیت پەیوەندی بکەیت بە $1 یان یەکێکی دیکە لە [[{{MediaWiki:Grouppage-sysop}}|بەڕێوەبەران]] بۆ وتووێژ لە سەر بەرگیرییەکە.\n\nتێ بگە کە ناتوانیت ئامرازی «ئیمێل بنێرە بۆ ئەم بەکارھێنەرە» بە کار بھێنیت مەگەر ئەوەی کە پێشتر لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]]تدا ناونیشانێکی گونجاوی ئیمێلت تۆمار کردبێت و بەرگیریت لێ نەکرابێت لە بەکارھێنانی ئەو ئامرازەش.\n\nناونیشانی IPی ئێستای تۆ $3ـە و پێناسەی یەرگیرییەکە #$5ـە.\nتکایە ھەموو وردەکارییەکانی سەرەوە ھەبێت لە ھەر پرس و داوایک کە دەیکەیت.",
        "blockednoreason": "هیچ هۆکارێک نەدراوە",
        "whitelistedittext": "بۆ دەستکاریی پەڕەکان دەبێ $1.",
        "confirmedittext": "پێویستە پێش هەرجۆرە دەستکاریەکی لاپەڕەکان ئەدرەسی ئیمەیلت ڕاچاو کردبێت .<br />\nتکایە لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]] ئی‌مەیلەکەت دانێ و بڕواپێکراوی بکە.",
        "loginreqtitle": "پێویستە بچییە ژوورەوە",
        "loginreqlink": "بچییە ژوورەوە",
        "loginreqpagetext": "بۆ دیتنی لاپەڕەکانی دیکە دەبێ $1 .",
-       "accmailtitle": "وشه‌ی نهێنی ناردرا.",
+       "accmailtitle": "تێپەڕوشە نێررا",
        "accmailtext": "تێپەڕوشەیەک کە بە هەڕەمەکی بۆ [[User talk:$1|$1]] دروست کرا، نێررا بۆ $2. دەتوانیت لە پەڕەی <em>[[Special:ChangePassword|گۆڕینی تێپەڕوشەدا]]</em> لە کاتی چوونەژوورەوەدا بیگۆڕی.",
        "newarticle": "(نوێ)",
        "newarticletext": "بە دوای بەستەری پەڕەیەک کەوتووی کە ھێشتا دروست نەکراوە.\nبۆ دروست کردنی پەڕەکە، لە چوارچێوەکەی خوارەوە دەست بکە بە تایپ کردن. (بۆ زانیاری زورتر\n[$1 یارمەتی] ببینە).\nئەگەر بە ھەڵەوە ھاتویتە ئێرە، لە سەر دوگمەی '''back'''ی وێبگەڕەکەت کلیک بکە.",
        "diff-empty": "(بەبێ جیاوازی)",
        "searchresults": "ئاکامەکانی گەڕان",
        "searchresults-title": "ئاکامەکانی گەڕان بۆ «$1»",
-       "titlematches": "سÛ\95ردÛ\8eÚ\95Û\8c Ù¾Û\95Ú\95Û\95 Ù¾Û\8eÛ\8c Ø¦Û\95Ø®Ù\88ا",
-       "textmatches": "هاوتاکانی دەقی لاپەڕە",
+       "titlematches": "ئÛ\95Ù\88 Ø³Û\95رÙ\86اÙ\88اÙ\86Û\95Û\8c Ø¯Û\95Ú¯Ù\88Ù\86جÙ\86",
+       "textmatches": "ئەو دەقانەی دەگونجن",
        "notextmatches": "لە دەقی نووسراوەکان دا نەبینرا",
        "prevn": "{{PLURAL:$1|$1}}ی پێشوو",
        "nextn": "{{PLURAL:$1|$1}}ی دواتر",
        "right-blockemail": "بەربەستنی بەکارھێنەرێک لە ناردنی ئیمەیل",
        "right-hideuser": "بەربەستنی ناوێکی بەکارهێنەری، شاردنەوەی لەبەر چاوی ھەمووان",
        "right-ipblock-exempt": "لادان لە بەربەستنەکانی ئایپی، بەربەستنە خۆگەڕەکان و بەربەستنەکانی زنجیرە",
-       "right-proxyunbannable": "لادان لە بەربەستنە خۆگەڕەکانی پرۆکسییەکان",
        "right-unblockself": "کردنەوەی خۆیان",
        "right-protect": "گۆڕینی ئاستەکانی پاراستن و دەستکاریی پەڕە پارێزراوە تاڤگەیییەکان",
        "right-editprotected": "دەستکاریی پەڕە پارێزراوەکانی وەک «{{int:protect-level-sysop}}»",
        "recentchanges-legend-heading": "'''کورتکراوەکان:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ھەروەھا بڕوانە [[Special:NewPages|پێرستی پەڕە نوێکان]])",
        "recentchanges-legend-plusminus": "(''±۱٢٣'')",
+       "recentchanges-submit": "نیشانی بدە",
        "rcnotefrom": "ژێرەوە {{PLURAL:$5|گۆڕانکارییەکەیە|گۆڕانکارییەکانە}} لە strong>$3، $4</strong>ەوە (ھەتا <strong>$1</strong> نیشان دراوە).",
        "rclistfrom": "گۆڕانکارییە نوێکان نیشان بدە بە دەستپێکردن لە $3 $2",
        "rcshowhideminor": "دەستکارییە بچووکەکان $1",
        "rcshowhidemine": "دەستکارییەکانم $1",
        "rcshowhidemine-show": "نیشان بدە",
        "rcshowhidemine-hide": "بشارەوە",
+       "rcshowhidecategorization-show": "نیشانی بدە",
        "rclinks": "دوایین $1 گۆڕانکاریی $2 ڕۆژی ڕابردوو نیشان بدە<br />$3",
        "diff": "جیاوازی",
        "hist": "مێژوو",
        "uploaderror": "ھەڵە لە بارکردن دا",
        "upload-recreate-warning": "'''ھۆشیار بە: پەرگەیەک بەو ناوەوە سڕاوەتەوە یان گوێزاوەتەوە.'''\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* '''<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": "جۆرە پەڕگە ڕێگەپێدراوەکان {{PLURAL:$2|type|types}}: $1.",
+       "upload-permitted": "جۆرە پەڕگە {{PLURAL:$2|ڕێگەپێدراوەکە|ڕێگەپێدراوەکان}}: $1.",
        "upload-preferred": "جۆرە پەڕگانەی بە باشتر دەزانرێن: $1.",
        "upload-prohibited": "جۆرە پەڕگانەی قەدەغە کراون: $1.",
        "uploadlogpage": "لۆگی بارکردن",
        "linksearch-text": "Wildcardی وەک \"*.wikipedia.org\" بەکاردێت.\nلانی کەم پێویستی بە پاوانێکی ئاست-بان ھەیە، بۆ نموونە «*.org» .<br />\nپرۆتۆکۆلە پشتیوانی لێکراوەکان: $1 (ھیچ کام لەمانە بە گەڕانەکەت زێدە مەکە).",
        "linksearch-line": "$1 بەستەرپێ‌دراو لە $2",
        "listusersfrom": "نیشاندانی بەکارھێنەران بە دەستپێکردن لە:",
-       "listusers-submit": "نیشانیبدە",
+       "listusers-submit": "نیشان بدە",
        "listusers-noresult": "ھیچ بەکارھێنەرێک نەدۆزرایەوە.",
        "listusers-blocked": "(بەربەست کراوە)",
        "activeusers": "پێرستی بەکارھێنەرە چالاکەکان",
        "defemailsubject": "ئیمەیلی {{SITENAME}} لە بەکارھێنەر «$1»ەوە",
        "usermaildisabled": "ئیمەیڵی بەکارهێنەر لەکاردانیە",
        "noemailtitle": "هیچ ناونیشانێکی ئی‌مەیل نییە",
-       "noemailtext": "ئەم بەکارهێنەرە ناونێشانێکی بڕوا پێکراوی ئی‌مەیلی دانەناوە.",
+       "noemailtext": "ئەم بەکارھێنەرە ناونێشانێکی گونجاوی ئیمێلی دەستنیشان نەکردووە.",
        "nowikiemailtext": "ئەم بەکارهێنەرە تایبەتمەندیی وەرنەگرتنی ئی‌مەیل لە بەکارهێنەرانی دیکەی هەلبژاردووە.",
        "emailtarget": "ناوی بەکارھێنەریی وەرگر بنووسە",
        "emailusername": "ناوی به‌كارھێنه‌ر:",
        "emailsend": "بینێرە",
        "emailccme": "کۆپییەک لە پەیامەکە بنێرە بۆ ئیمەیلەکەم.",
        "emailccsubject": "کۆپیی نامەکەت بۆ $1: $2",
-       "emailsent": "نامەکەت ناردرا",
-       "emailsenttext": "Ù\86اÙ\85Û\95Ú©Û\95ت Ù\86اردرا",
+       "emailsent": "ئیمێل نێررا",
+       "emailsenttext": "Ù¾Û\95Û\8cاÙ\85Û\95 Ø¦Û\8cÙ\85Û\8eÙ\84Û\8cÛ\8cÛ\95Ú©Û\95ت Ù\86Û\8eررا",
        "emailuserfooter": "ئەم ئیمەیلە لە $1ەوە ناردرا بۆ $2 بە \"Email user\" لە {{SITENAME}}ەوە.",
        "usermessage-summary": "بەجێھێشتنی پەیامی سیستەم",
        "usermessage-editor": "پەیامنێری سیستەم",
        "revertpage-nouser": "دەستکارییەکانی بەکارھێنەرێکی شاڕدراوە بۆ دوایین پێداچوونەوەی {{GENDER:$1|[[User:$1|$1]]}} گەڕێنرایەوە.",
        "rollback-success": "دەستکارییەکانی $1 وەرگێرایەوە؛<br />\nگۆڕدرا بۆ دوایین پێداچوونەوەی $2.",
        "sessionfailure": "لەوەدەچی کێشەیەک لە دانیشتنی چوونەژوورەوەت (login session)دا ببێت.\nئەم کردەوە هەڵوەشێندرایەوە بۆ بەرگری لە دزینی دراوەکانی دانیشتن.\nتکایە بگەڕێوە بۆ پەڕەی پێشوو و نوێی بکەوە، ئینجا دیسان تاقیی بکەوە.",
-       "changecontentmodel-title-label": "سەردێڕی پەڕە",
+       "changecontentmodel-title-label": "سەرناوی پەڕە",
        "changecontentmodel-reason-label": "هۆکار:",
        "protectlogpage": "لۆگی پاراستن",
        "protectlogtext": "لە ژێرەوە پێرستێک لە گۆڕانکارییەکانی پەڕە پارێزراوەکان دەبینی.\nبۆ پێرستی ئەو پەڕانەی ئێستا پاراستنیان لە ئارادایە بڕوانە [[Special:ProtectedPages|پێرستی پەڕە پارێزراوەکان]].",
        "undeleteextrahelp": "بۆ ھێنانەوەی گشت مێژووی پەڕەکە، ھەموو چوارچێوەکانی نیشانکردن ھەڵنەبژێردراو بھێڵەوە و لە سەر '''''{{int:undeletebtn}}''''' کرتە بکە.\nبۆ ھێنانەوەی ھەڵبژێردراو، چوارچێوەی بەرامبەر بەو پێداچوونەویەی دەتەوێ بیھێنیتەوە، نیشان بکە و لە سەر '''''{{int:undeletebtn}}''''' کرتە بکە.",
        "undeleterevisions": "$1 {{PLURAL:$1|پێداچوونەوە|پێداچوونەوە}} ئەرشیڤ‌کرا",
        "undeletehistory": "ئەگەر پەڕەیەک بھێنیتەوە، ھەموو پێداچوونەوەکان دەگەڕێنەوە بۆ مێژووی پەڕە.\nئەگەر لە کاتی سڕانەوەی پەڕەکەوە، پەڕەیەک هەر بەو ناوەوە دروست کرابێت، پێداچوونەوە گەرێنراوەکان لە مێژووی پێشووەکەدا دەدرەکەوێت.",
-       "undeletehistorynoadmin": "ئÛ\95Ù\85 Ù\84اپÛ\95Ú\95Û\95 Ø³Ú\95اÙ\88Û\95تÛ\95Ù\88Û\95.\nÙ\84Û\95Ù\88 Ù¾Û\86ختÛ\95Û\8c Ù\84Û\95 Ø®Ù\88ارÛ\95Ù\88Û\95 Ø¯Û\95Û\8cبÛ\8cÙ\86Û\8cØ\8c Ù\87Û\86کارÛ\8c Ø³Ú\95Û\8cÙ\86Û\95Ù\88Û\95 Ù\88 Ù\87Û\95رÙ\88ا Ù\88ردÛ\95کارÛ\8cÛ\95کاÙ\86 Ø³Û\95بارÛ\95 Ø¨Û\95Ù\88 Ú©Û\95سÛ\95Û\8c Ù¾Û\8eØ´ Ø³Ú\95Û\8cÙ\86Û\95Ù\88Û\95 Ø¯Û\95ستکارÛ\8c Ù\84اپÛ\95Ú\95Û\95Ú©Û\95Û\8c Ú©Ø±Ø¯Ù\88Ù\88Û\95Ø\8c Ø¯Û\95ستâ\80\8cدÛ\95Ú©Û\95Ù\88Û\8e.\nدÛ\95Ù\82Û\8c Ú\95استÛ\8c Ø¦Û\95Ù\85 Ù¾Û\8eداÚ\86Ù\88Ù\88Ù\86Û\95Ù\88Û\95 Ø³Ú\95اÙ\88اÙ\86Û\95 ØªÛ\95Ù\86Ù\87ا Ø¨Û\86 Ø¨Û\95Ú\95Û\8eÙ\88بÛ\95راÙ\86 Ø¯Û\95ستâ\80\8cÙ¾Û\8eâ\80\8cÚ¯Û\95Û\8cشتÙ\86Û\8c Ù\87Û\95یە.",
+       "undeletehistorynoadmin": "ئÛ\95Ù\85 Ù¾Û\95Ú\95Û\95Û\8cÛ\95 Ø³Ú\95اÙ\88Û\95تÛ\95Ù\88Û\95.\nÚ¾Û\86کارÛ\8c Ø³Ú\95Û\8cÙ\86Û\95Ù\88Û\95 Ù\88 Ù\88ردÛ\95کارÛ\8cÛ\8cÛ\95کاÙ\86Û\8c Ø¦Û\95Ù\88 Ø¨Û\95کارھÛ\8eÙ\86Û\95راÙ\86Û\95Û\8c Ú©Û\95 Ù¾Û\8eØ´ Ø³Ú\95Û\8cÙ\86Û\95Ù\88Û\95Û\8c Ø¯Û\95ستکارÛ\8cÛ\8cاÙ\86 Ú©Ø±Ø¯Ù\88Ù\88Û\95 Ù\84Û\95 Ù¾Û\86ختÛ\95Û\8c Ø®Ù\88ارÛ\95Ù\88Û\95دا Ù\86Û\8cشاÙ\86 Ø¯Ø±Ø§Ù\88Û\95.\nدÛ\95Ù\82Û\8c Ú\95استÛ\95Ù\82Û\8cÙ\86Û\95Û\8c Ø¦Û\95Ù\85 Ù¾Û\8eداÚ\86Ù\88Ù\88Ù\86Û\95Ù\88Û\95 Ø³Ú\95راÙ\88اÙ\86Û\95 ØªÛ\95Ù\86ھا Ù\84Û\95بÛ\95ر Ø¯Û\95ستÛ\8c Ø¨Û\95Ú\95Û\8eÙ\88Û\95بÛ\95راÙ\86دایە.",
        "undelete-revision": "پێداچوونەوەی سڕاوەی $1 (لە $4،  $5) لەلایەن $3:",
        "undeleterevision-missing": "پێداچوونەوەی نادیار یا نەناسراو.\nلەوانەیە خەریکی لە بەستەرێکی خراپ کەڵک وەر ئەگری ئا لەوانەیە پێداچوونەوەکە لە ئەرشیڤ لابرابێت.",
        "undelete-nodiff": "هیچ پێداچوونەوەیەکی پێشو نەدۆزرایەوە.",
        "spamprotectionmatch": "ئەم دەقە ئەوەیە کە پاڵێوی سپامەکە دەبزوێنێ: $1",
        "spambot_username": "خاوێنکردنەوەی سپامی میدیاویکی",
        "spam_reverting": "گەڕانەوە بۆ دوایین پێداچوونەوە کە بەستەری لەخۆگرتووە بۆ $1",
-       "simpleantispam-label": "پشکنینی دژەھەرزە.\nئەمە پڕ <strong>مەکەرەوە</strong>پڕکردنەوە ئەوا لە!",
+       "simpleantispam-label": "پشکنینی دژەھەرزە.\nئەمە پڕ <strong>مەکەرەوە</strong>!",
        "pageinfo-title": "زانیاری بۆ «$1»",
        "pageinfo-header-basic": "زانیاریی سەرەتایی",
        "pageinfo-header-edits": "مێژووی دەستکاری",
        "namespacesall": "ھەموو",
        "monthsall": "ھەموو",
        "confirmemail": "بڕواپێکردنی ناونیشانی ئیمەیل",
-       "confirmemail_noemail": "لە [[Special:Preferences|هەڵبژاردەکانی بەکارهێنەر]] ناونیشانی ئی‌مەیلی گونجاوت دیاری نەکردووە.",
-       "confirmemail_text": "{{SITENAME}} بە پێویستی دەزانێ پێش کەڵک وەرگرتن لە تایبەتمەندیەکانی ئی‌مەیل، ناونیشانی ئی‌مەیلی خۆت ڕاچاو بکەیت.\nئەو دوکمەی خوارەوە چالاک بکە تاکوو ئی‌مەیلێکی بڕوا پێ‌کردن بنێردرێت بۆ ناونیشانی ئی‌مەیلەکەت.\nئەو ئی‌مەیلە بەستەرەکی تێدایە؛ لە وێبگەڕەکەت ئەو بەستەرە ببینە تاکوو ناونیشانی ئی‌مەیلەکەت بڕوادار بێت.",
+       "confirmemail_noemail": "لە [[Special:Preferences|ھەڵبژاردەکانی بەکارھێنەر]]دا ناونیشانی گونجاوی ئیمێلت دیاری نەکردووە.",
+       "confirmemail_text": "{{SITENAME}} بە پێویستی دەزانێت پێش بەکارھێنانی ئامرازە ئیمێلییەکان ناونیشانی ئیمێلی خۆت بسەلمێنیت.\nئەم دوگمەی خوارەوە چالاک بکە تاکوو ئیمێلێکی پشتڕاستکردنەوە بنێررێت بۆ ناونیشانەکەت.\nلە ئیمێلەکەدا لینکێک دەبێت کە کۆدێکی تێدایە؛\nلە وێبگەڕەکەتدا لینکەکە بکەرەوە تاکوو ناونیشانی ئیمێلەکەت پشتڕاست بکرێتەوە.",
        "confirmemail_pending": "کۆدی بڕواپێ‌کردن لە پێش‌دا ئی‌مەیل کراوە بۆت.\nئەگەر بە تازەیی هەژمارەت درووست‌کردووە، لەوانەیە باشتربێت چەن خۆلکێک بوەستی بۆ گەیشتنی ئەو ئەمەیلی، پێش دیسان داواکردنەوەی کۆدی نوێ.",
        "confirmemail_send": "ئی‌مەیل‌کردنی کۆدی بڕواپێ‌کردن",
-       "confirmemail_sent": "ئی‌مەیلی بڕواپێ‌کردن ناردرا.",
-       "confirmemail_oncreate": "Ú©Û\86دÛ\8c Ø¨Ú\95Ù\88اپÛ\8eâ\80\8cکردÙ\86Û\8c Ù\86اردرا Ø¨Û\86 Ù\86اÙ\88Ù\86Û\8cشاÙ\86Û\8c Ø¦Û\8câ\80\8cÙ\85Û\95Û\8cÙ\84ت.\nÙ¾Û\8eÙ\88Û\8cست Ù\86Û\8cÛ\95 Ø¨Ú\95Û\86Û\8cتÛ\95 Ú\98Ù\88Ù\88رÛ\95Ù\88Û\95Ø\8c ØªÛ\95Ù\86Ù\87ا Ù¾Û\8eÙ\88Û\8cستÛ\95 Ù¾Û\8eØ´ Ú©Û\95ÚµÚ© Ù\88Û\95رگرتÙ\86 Ù\84Û\95 ØªØ§Û\8cبÛ\95تÙ\85Û\95Ù\86دÛ\8cÛ\95کاÙ\86 Ø¦Û\8câ\80\8cÙ\85Û\95Û\8cÙ\84Û\8cÛ\8c Ù\88Û\8cÚ©Û\8c Ø¦Û\95Ù\88Û\95 Ø¬Û\8eبÛ\95جÛ\8e Ø¨Ú©Û\95Û\8cت.",
-       "confirmemail_sendfailed": "{{SITENAME}} ناتوانێ ئی‌مەیلی برواکردن بنێرێت بۆ تۆ.\nتکایە ئەرخەیان بە هەموو پیتەکانی ناونیشانەکەت گونجاوە.\n\nمەیلکەر ئەوەی گەڕاندەوە: $1",
+       "confirmemail_sent": "ئیمێلی پشتڕاستکردنەوە نێررا.",
+       "confirmemail_oncreate": "Ú©Û\86دÛ\8eÚ©Û\8c Ù¾Ø´ØªÚ\95استکردÙ\86Û\95Ù\88Û\95 Ù\86Û\8eررا Ø¨Û\86 Ø¦Û\95درÛ\95سÛ\8c Ø¦Û\8cÙ\85Û\8eÙ\84Û\8c ØªÛ\86.\nئÛ\95Ù\85 Ú©Û\86دÛ\95 Ù¾Û\8eÙ\88Û\8cست Ù\86Û\8cÛ\8cÛ\95 Ø¨Û\86 Ú\86Ù\88Ù\88Ù\86Û\95 Ú\98Ù\88Ù\88رÛ\95Ù\88Û\95Ø\8c Ø¨Û\95ڵاÙ\85 Ù¾Û\8eÙ\88Û\8cستÛ\95 Ú¾Û\95تبÛ\8eت Ù¾Û\8eØ´ Ø¦Û\95Ù\88Û\95Û\8c Ú¾Û\95ر Ø¦Ø§Ù\85رازÛ\8eÚ©Û\8c Ø¦Û\8cÙ\85Û\8eÙ\84Û\8cÛ\8c Ù\88Û\8cÚ©Û\8c Ú\86اÙ\84اک Ø¨Ú©Û\95Û\8cتÛ\95Ù\88Û\95.",
+       "confirmemail_sendfailed": "{{SITENAME}} نەیتوانی نامەی پشتڕاستکردنەوەکەت بنێرێت.\nتکایە ناونیشانی ئیمێلەکەت بپشکنێ بۆ پیت نەگونجاوەکان.\n\nنێرەری ئیمێل ئەوەی گەڕاندەوە: $1",
        "confirmemail_invalid": "کۆدی بڕواپێ‌کردنی نەگونجاو.\nلەوانەیە ئەو کۆدە ماوەی بەسەر چووبێت.",
        "confirmemail_needlogin": "بۆ بڕواپێکردنی ناونیشانی ئیمەیلەکەت دەبێ $1.",
        "confirmemail_success": "ناونیشانی ئی‌میلەکەت بڕوای‌پێ‌کرا.\nئێستە دەتوانی [[Special:UserLogin|بڕۆیتە ژوورەوە]] و لە ویکی کەڵک بگری.",
        "confirmemail_loggedin": "ئێستا بڕواکراوە بە ئیمەیلەکەت.",
-       "confirmemail_subject": "بڕوا پێ‌کردنی ناونیشانی ئی‌مەیلی {{SITENAME}}",
-       "confirmemail_body": "کەسێک، لەوانەیە خۆت، لە ناونیشانی ئای‌پی $1،\nلە {{SITENAME}} بەم ناونیشانی ئی‌مەیلە، هەژمارەیەکی تۆمارکردووە \"$2\" .\n\nبۆ ئەوەی بڕا بکرێت کە ئەم هەژمارە لە ڕاستیدا بۆتۆیە و بۆ چالاک‌کردنی تایبەتمەندیەکانی ئی‌مەیل لە {{SITENAME}}دا، ئەو بەستەرەی خوارەوە لە وێبگەڕەکەت‌دا بکەوە:\n\n$3\n\nئەگەر تۆ ئەو هەژمارەت تۆمار *نەکردووە*، بۆ هەڵوەشاندنەوەی بڕوا‌پێ‌کردنی ناونیشانی ئی‌مەیل بڕۆ بۆ ئەم بەستەرە:\n\n$5\n\nئەم کۆدی بڕواپێ‌کردنە لە $4 ماوەی بەسەردێت.",
+       "confirmemail_subject": "پشتڕاستکردنەوەی ناونیشانی ئیمێلی {{SITENAME}}",
+       "confirmemail_body": "کەسێک، لەوانەیە خۆت، لە ناونیشانی IPی $1 ـەوە،\nلە {{SITENAME}}دا، بەم ناونیشانی ئیمێلەوە، هەژماری «$2»ی تۆمار کردووە.\n\nبۆ ئەوەی کە پشتڕاست بکرێتەوە کە ئەم هەژمارە لە ڕاستیدا ھی تۆیە و بۆ چالاک کردنی ئامرازە ئیمێلییەکانی {{SITENAME}}، ئەم بەستەرەی خوارەوە لە وێبگەڕەکەتدا بکەرەوە:\n\n$3\n\nئەگەر تۆ ئەم ھەژمارەت تۆمار *نەکردووە*، بە دوای ئەم لینکەدا بکەوە بۆ ھەڵوەشاندنەوەی پشتڕاست کردنەوەی ناونیشانی ئیمێل:\n\n$5\n\nئەم کۆدی پشتڕاستکردنەوە لە $4دا ماوەی بە سەر دەچێت.",
        "confirmemail_body_changed": "کەسێک، لەوانەیە خۆت، لە ئای‌پی ئەدرەسی $1،\nئەدرەسی ئەیمەیلی ھەژماری «$2» لە {{SITENAME}}دا گۆڕاوە بۆ ئەم ئەدرەسە.\n\nبۆ ئەوەی بڕوا بکرێت کە ئەم ھەژمارە لە ڕاستیدا بۆتۆیە و بۆ چالاککردنەوەی تایبەتمەندیەکانی ئیمەیل لە {{SITENAME}}دا، ئەم بەستەرەی خوارەوە لە وێبگەڕەکەتدا بکەوە:\n\n$3\n\nئەگەر ھەژمارە ھی تۆ *نییە*، بۆ هەڵوەشاندنەوەی بڕوا‌پێکردنی ئەدرەسی ئیمەیل بەدوای ئەم بەستەرە بکەوە:\n\n$5\n\nئەم کۆدی بڕواپێکردنە لە $4 ماوەی بەسەردێت.",
-       "confirmemail_body_set": "کەسێک، لەوانەیە خۆت، لە ناونیشانی ئایپیی $1،\nناونیشانیی ئەیمەیلی ھەژماری «$2» لە {{SITENAME}}دا کردووە بەم ناونیشانە.\n\nبۆ پشتڕاستکردنەوەی ئەمەی ئەم ھەژمارە بەڕاستی ھی تۆیە و بۆ چالاککردنی تایبەتمەندیەکانی ئیمەیل لە {{SITENAME}}دا، ئەم بەستەرە لە وێبگەڕەکەتدا بکەوە:\n\n$3\n\nئەگەر ھەژمارەکە ھی تۆ *نییە*، بۆ هەڵوەشاندنەوەی پشتڕاستکردنەوەی ناونیشانی ئیمەیل، شوێنی ئەم بەستەرە بکەوە:\n\n$5\n\nئەم کۆدەی پشتڕاستکردنەوەیە لە $4 ماوەی بەسەر دەچێت.",
+       "confirmemail_body_set": "کەسێک، لەوانەیە خۆت، لە ناونیشانی IPی $1 ـەوە،\nلە {{SITENAME}}دا، ناونیشانیی ئیمێلی ھەژماری «$2»ی کردووە بەم ناونیشانە.\n\nبۆ پشتڕاستکردنەوەی ئەوەی کە ئەم ھەژمارە بە ڕاستی ھی تۆیە و بۆ چالاککردنەوەی ئامرازە ئیمێلییەکانی {{SITENAME}}، ئەم لینکە لە وێبگەڕەکەتدا بکەرەوە:\n\n$3\n\nئەگەر ھەژمارەکە ھی تۆ *نییە*، بۆ هەڵوەشاندنەوەی پشتڕاستکردنەوەی ناونیشانی ئیمێل، شوێنی ئەم لینکە بکەوە:\n\n$5\n\nماوەی ئەم کۆدی پشتڕاستکردنەوەیە لە $4دا بە سەر دەچێت.",
        "confirmemail_invalidated": "بڕواپی‌کردنی ناونیشانی ئی‌مەیل هەڵوەشێندراوە",
        "invalidateemail": "هەڵوەشاندنەوەی بڕواپێ‌کردنی ئی‌مەیل",
        "scarytranscludetoolong": "[URL زۆر درێژە]",
        "version-libraries-license": "مۆڵەت",
        "version-libraries-description": "وەسف",
        "version-libraries-authors": "نووسەر",
-       "redirect": "ڕەوانەکەر بە پێی پەڕگە، بەکارھێنەر، پەڕە یان پێناسەی پێداچوونەوە",
+       "redirect": "ڕەوانەکەر بە پێی پەڕگە، بەکارھێنەر، پەڕە، پێداچوونەوە یان پێناسەی لۆگ",
        "redirect-legend": "ڕەوانەکەر بۆ پەڕگە یان پەڕەیەک",
        "redirect-summary": "ئەم پەڕە تایبەتە ڕەوانە دەکرێ بۆ پەڕگەیەک (ناوی پەڕگەکە)، پەڕەیەک (پێناسەی پێداچوونەوەیەک یان پێناسەی پەڕە) یان پەڕەیەکی بەکارھێنەر (پێناسەیەکی  ژمارەیی بەکارھێنەر). بەکارھێنان: [[{{#Special:Redirect}}/file/Example.jpg]]، [[{{#Special:Redirect}}/page/64308]]، [[{{#Special:Redirect}}/revision/328429]] یان [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "بڕۆ",
        "htmlform-selectorother-other": "دیکە",
        "htmlform-no": "نا",
        "htmlform-yes": "بەڵێ",
-       "htmlform-title-not-creatable": "پەڕە بە سەردێڕی \"$1\" دروست ناکرێت",
+       "htmlform-title-not-creatable": "«$1» سەرناوێک نییە کە بکرێت پەڕەی بۆ دروست بکرێت",
        "htmlform-title-not-exists": "$1 بوونی نیە.",
        "logentry-delete-delete": "$1 پەڕەی $3ی {{GENDER:$2|سڕییەوە}}",
        "logentry-delete-restore": "$1 پەڕەی $3ی {{GENDER:$2|ھێنایەوە}}",
index 1dc61ea..d098b02 100644 (file)
@@ -10,7 +10,7 @@
                ]
        },
        "tog-underline": "Багълантыларнынъ тюбюни сызув:",
-       "tog-hideminor": "\"Сонъки денъиштирмелер\" саифесинде кичик денъиштирмелерни гизле",
+       "tog-hideminor": "«Сонъки денъиштирмелер» саифесинде кичик денъиштирмелерни гизле",
        "tog-hidepatrolled": "Сонъки денъиштирмелер косьтергенде тешкерильген денъиштирмелерни гизле",
        "tog-newpageshidepatrolled": "Янъы саифелер косьтергенде тешкерильген саифелерни гизле",
        "tog-extendwatchlist": "Козетюв джедвелини, тек сонъки дегиль, бутюн денъиштирмелерни корьмек ичюн кенишлет",
        "token_suffix_mismatch": "'''Сизинъ программанъызнынъ озь тюрлендирюв пенджересинде пунктуация ишаретлерини догъру ишлемегени ичюн япкъан денъиштирмелеринъиз къабул олунмады. Денъиштирмелер саифе метнининъ корюниши бозулмасын деп лягъу этильди.\nБунынъ киби проблемалар хаталы аноним web-проксилер къулланувдан чыкъып ола.'''",
        "editing": "«$1» саифесини денъиштиреятасыз",
        "creating": "«$1» саифесини яратув",
-       "editingsection": "\"$1\" саифесинде болюк денъиштиреятасыз",
+       "editingsection": "«$1» саифесинде болюк денъиштиреятасыз",
        "editingcomment": "$1 саифесини денъиштиреятасыз (янъы болюк)",
        "editconflict": "Денъиштирмелер чатышмасы: $1",
        "explainconflict": "Сиз саифени денъиштиргенде башкъа бири де денъиштирме япты.\nЮкъарыдаки язы саифенинъ шимдики алыны косьтере.\nСизинъ денъиштирмелеринъиз астында косьтерильди.\nШимди япкъан денъиштирмелеринъизни ашагъы пенджереден юкъары пенджереге авуштырмакъ керексинъиз.\n\"{{int:savearticle}}\"гъа баскъанда '''тек''' юкъарыдаки язы сакъланаджакъ.",
        "wlheader-showupdated": "Сонъки зияретинъизден сонъ денъиштирильген саифелер '''къалын арифлернен''' косьтерильди.",
        "wlnote": "Ашагъыда саат $3, $4 ичюн сонъки {{PLURAL:$2|1=саат|'''$2''' саат}} ичинде япылгъан сонъки {{PLURAL:$1|1=денъиштирме|'''$1''' денъиштирме}} косьтериле.",
        "wlshowlast": "Сонъки $1 саат ичюн, $2 кунь ичюн я да  косьтер",
+       "watchlistall2": "эписини",
        "watchlist-options": "Козетюв джедвели сазламалары",
        "watching": "Козетюв джедвелине кирсетильмекте...",
        "unwatching": "Козетюв джедвелинден ёкъ этильмекте...",
        "movenosubpage": "Бу саифенинъ алт саифеси ёкъ.",
        "movereason": "Себеп",
        "revertmove": "Кериге ал",
-       "delete_and_move": "Ёкъ эт ве адыны денъиштир",
        "delete_and_move_text": "==Ёкъ этмек лязимдир==\n\n«[[:$1]]» саифеси энди бар. Адыны денъиштирип олмакъ ичюн оны ёкъ этмеге истейсинъизми?",
        "delete_and_move_confirm": "Эбет, бу саифени ёкъ эт",
        "delete_and_move_reason": "Исим денъиштирип олмакъ ичюн ёкъ этильди",
index 2cccdc1..b35f032 100644 (file)
@@ -8,7 +8,7 @@
                ]
        },
        "tog-underline": "Bağlantılarnıñ tübüni sızuv:",
-       "tog-hideminor": "\"Soñki deñiştirmeler\" saifesinde kiçik deñiştirmelerni gizle",
+       "tog-hideminor": "“Soñki deñiştirmeler” saifesinde kiçik deñiştirmelerni gizle",
        "tog-hidepatrolled": "Soñki deñiştirmeler köstergende teşkerilgen deñiştirmelerni gizle",
        "tog-newpageshidepatrolled": "Yañı saifeler köstergende teşkerilgen saifelerni gizle",
        "tog-extendwatchlist": "Közetüv cedvelini, tek soñki degil, bütün deñiştirmelerni körmek içün kenişlet",
        "createaccountreason": "Sebep:",
        "createacct-reason": "Sebep",
        "createacct-reason-ph": "Başqa bir esap yazısı neden sebep yaratasıñız",
-       "createacct-captcha": "Telükesizlik kontroli",
-       "createacct-imgcaptcha-ph": "Yuqarıda körgen metniñizni yazıñız",
        "createacct-submit": "Esap yazıñıznı yaratıñız",
        "createacct-another-submit": "Başqa bir esap yazısı yaratıñız",
        "createacct-benefit-heading": "{{SITENAME}} siziñ kibi adamlar tarafından yazıla.",
        "token_suffix_mismatch": "'''Siziñ programmañıznıñ öz türlendirüv penceresinde punktuatsiya işaretlerini doğru işlemegeni içün yapqan deñiştirmeleriñiz qabul olunmadı. Deñiştirmeler saife metniniñ körünişi bozulmasın dep lâğu etildi.\nBunıñ kibi problemalar hatalı anonim web-proksiler qullanuvdan çıqıp ola.'''",
        "editing": "“$1” saifesini deñiştireyatasız",
        "creating": "“$1” saifesini yaratuv",
-       "editingsection": "\"$1\" saifesinde bölük deñiştireyatasız",
+       "editingsection": "“$1” saifesinde bölük deñiştireyatasız",
        "editingcomment": "$1 saifesini deñiştireyatasız (yañı bölük)",
        "editconflict": "Deñiştirmeler çatışması: $1",
        "explainconflict": "Siz saifeni deñiştirgende başqa biri de deñiştirme yaptı.\nYuqarıdaki yazı saifeniñ şimdiki alını köstere.\nSiziñ deñiştirmeleriñiz astında kösterildi. Şimdi yapqan deñiştirmeleriñizni aşağı pencereden yuqarı pencerege avuştırmaq kereksiñiz.\n\"{{int:savearticle}}\"ğa basqanda '''tek''' yuqarıdaki yazı saqlanacaq.",
        "wlheader-showupdated": "Soñki ziyaretiñizden soñ deñiştirilgen saifeler '''qalın ariflernen''' kösterildi.",
        "wlnote": "Aşağıda saat $3, $4 içün soñki {{PLURAL:$2|saat|'''$2''' saat}} içinde yapılğan soñki {{PLURAL:$1|deñiştirme|'''$1''' deñiştirme}} kösterile.",
        "wlshowlast": "Soñki $1 saat içün, $2 kün içün ya da  köster",
+       "watchlistall2": "episini",
        "watchlist-options": "Közetüv cedveli sazlamaları",
        "watching": "Közetüv cedveline kirsetilmekte...",
        "unwatching": "Közetüv cedvelinden yoq etilmekte...",
        "move-page-legend": "Saifeniñ adını deñiştirüv",
        "movepagetext": "Aşağıdaki forma qullanılıp saifeniñ adı deñiştirilir. Bunıñnen beraber deñiştirmeler jurnalı da yañı adğa avuştırılır.\nEski adı yañı adına yönetme olur. Eski serlevağa yönetip turğan saifelerni avtomatik olaraq yañartıp olasıñız. Bu areketni avtomatik yapmağa istemeseñiz, bütün [[Special:DoubleRedirects|çift]] ve [[Special:BrokenRedirects|yırtıq]] yönetme saifelerini özüñiz teşkermege mecbur olursıñız. Bağlantılar endiden berli doğru çalışmasından emin olmalısıñız.\n\nYañı adda bir saife endi bar olsa, ad deñiştirilüvi <strong>yapılmaycaq</strong>, ancaq bar olğan saife yönetme ya da boş olsa ad deñiştirilüvi mümkün olacaq. Bu demek ki, saifeniñ adını yañlıştan deñiştirgen olsañız deminki adını keri qaytarıp olasıñız, amma bar olğan saifeni tesadüfen yoq etamaysıñız.\n\n<strong>TENBİ!</strong>\nAd deñiştirilüvi populâr saifeler içün büyük ve beklenmegen deñişmelerge sebep ola bilir. Lütfen, deñiştirme yapmazdan evel ola bileceklerni köz ögüne alıñız.",
        "movepagetalktext": "Qoşulğan muzakere saifesiniñ de (bar olsa) adı avtomatik tarzda deñiştirilecek. '''Müstesnalar:'''\n\n*Aynı bu isimde boş olmağan bir muzakere saifesi endi bar;\n*Aşağıdaki boşluqqa işaret qoymadıñız.\n\nBöyle allarda, kerek olsa, saifelerni qolnen taşımağa ya da birleştirmege mecbur olursıñız.",
-       "movearticle": "Eski ad",
        "movecategorypage-warning": "<strong>İhtar:</strong> Bir kategoriya saifesiniñ adını deñiştirmek üzresiñiz. Lütfen, yalıñız kategoriya saifesiniñ köçürilecegini ve eski kategoriyada yer alğan saifelerniñ yañı kategoriyağa avotmatik olaraq <em>avuştırılmaycağını</em> unutmañız.",
        "movenologintext": "Saifeniñ adını deñiştirip olmaq içün [[Special:UserLogin|oturım açıñız]].",
        "movenotallowed": "Saifeler adlarını deñiştirmege iziniñiz yoq.",
        "movenosubpage": "Bu saifeniñ alt saifesi yoq.",
        "movereason": "Sebep",
        "revertmove": "Kerige al",
-       "delete_and_move": "Yoq et ve adını deñiştir",
        "delete_and_move_text": "== Yoq etmek lâzimdir ==\n\n\"[[:$1]]\" saifesi endi bar. Adını deñiştirip olmaq içün onı yoq etmege isteysiñizmi?",
        "delete_and_move_confirm": "Ebet, bu saifeni yoq et",
        "delete_and_move_reason": "İsim deñiştirip olmaq içün yoq etildi",
index 44ee916..e7cdaf4 100644 (file)
@@ -32,8 +32,8 @@
        },
        "tog-underline": "Podtrhávat odkazy:",
        "tog-hideminor": "Skrýt malé editace v posledních změnách",
-       "tog-hidepatrolled": "Skrýt patrolované editace v posledních změnách",
-       "tog-newpageshidepatrolled": "Skrýt patrolované stránky v seznamu nových stránek",
+       "tog-hidepatrolled": "Skrýt prověřené editace v posledních změnách",
+       "tog-newpageshidepatrolled": "Skrýt prověřené stránky v seznamu nových stránek",
        "tog-hidecategorization": "Skrýt kategorizaci stránek",
        "tog-extendwatchlist": "Na seznamu sledovaných stránek zobrazovat všechny změny, ne jen tu poslední",
        "tog-usenewrc": "V posledních změnách a sledovaných stránkách seskupovat změny podle stránek",
@@ -64,7 +64,7 @@
        "tog-watchlisthideliu": "Na seznamu sledovaných stránek skrýt editace přihlášených uživatelů",
        "tog-watchlistreloadautomatically": "Při změně nastavení automaticky aktualizovat seznam sledovaných stránek (vyžaduje JavaScript)",
        "tog-watchlisthideanons": "Na seznamu sledovaných stránek skrýt editace nepřihlášených uživatelů",
-       "tog-watchlisthidepatrolled": "Skrýt patrolované editace ve sledovaných stránkách",
+       "tog-watchlisthidepatrolled": "Skrýt prověřené editace ve sledovaných stránkách",
        "tog-watchlisthidecategorization": "Skrýt kategorizaci stránek",
        "tog-ccmeonemails": "Zasílat mi kopie e-mailů, které pošlu jiným uživatelům",
        "tog-diffonly": "Nezobrazovat obsah stránky pod rozdílem verzí",
        "october-date": "$1. října",
        "november-date": "$1. listopadu",
        "december-date": "$1. prosince",
+       "period-am": "dop.",
+       "period-pm": "odp.",
        "pagecategories": "{{PLURAL:$1|Kategorie}}",
        "category_header": "Stránky v kategorii „$1“",
        "subcategories": "Podkategorie",
        "virus-scanfailed": "prověřování selhalo (kód $1)",
        "virus-unknownscanner": "neznámý antivirus:",
        "logouttext": "<strong>Nyní jste odhlášeni.</strong>\n\nNěkteré stránky se mohou i nadále zobrazovat, jako byste byli dosud přihlášeni, dokud nevymažete cache prohlížeče.",
+       "cannotlogoutnow-title": "Momentálně se nelze odhlásit",
+       "cannotlogoutnow-text": "Odhlášení není možné, když se používají $1.",
        "welcomeuser": "Vítejte, uživateli $1!",
        "welcomecreation-msg": "Váš účet byl vytvořen.\nNezapomeňte si upravit své [[Special:Preferences|nastavení {{grammar:2sg|{{SITENAME}}}}]].",
        "yourname": "Uživatelské jméno:",
        "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",
+       "cannotloginnow-text": "Přihlášení není možné, když se používají $1.",
        "yourdomainname": "Vaše doména",
        "password-change-forbidden": "Na této wiki nemůžete měnit hesla.",
        "externaldberror": "Buď nastala chyba externí autentizační databáze, nebo nemáte dovoleno měnit svůj externí účet.",
        "resetpass_submit": "Nastavit heslo a přihlásit se",
        "changepassword-success": "Vaše heslo bylo úspěšně změněno!",
        "changepassword-throttled": "Provedli jste příliš mnoho pokusů o přihlášení.\nČekejte prosím $1 a zkuste to znovu.",
+       "botpasswords": "Hesla pro boty",
+       "botpasswords-summary": "<em>Hesla pro boty</em> umožňují přistupovat k uživatelskému účtu prostřednictví API bez použití hlavních přihlašovacích údajů účtu. Uživatelská oprávnění dostupná po přihlášení pomocí hesla pro boty mohou být omezena.\n\nPokud nevíte, k čemu byste to {{GENDER:|chtěl|chtěla|chtěli}} použít, pravděpodobně byste to používat {{GENDER:|neměl|neměla|neměli}}. Nikdo by vás nikdy neměl žádat, abyste si zde vygenerovali heslo a dali mu ho.",
+       "botpasswords-disabled": "Hesla pro boty jsou zakázána.",
+       "botpasswords-no-central-id": "Abyste {{GENDER:|mohl|mohla|mohl(a)}} použít hesla pro boty, musíte být {{GENDER:|přihlášen|přihlášena|přihlášen(a)}} k centrálnímu účtu.",
+       "botpasswords-existing": "Stávající hesla pro boty",
+       "botpasswords-createnew": "Vytvořit nové heslo pro boty",
+       "botpasswords-editexisting": "Editovat existující heslo pro boty",
+       "botpasswords-label-appid": "Název bota:",
+       "botpasswords-label-create": "Vytvořit",
+       "botpasswords-label-update": "Aktualizovat",
+       "botpasswords-label-cancel": "Storno",
+       "botpasswords-label-delete": "Smazat",
+       "botpasswords-label-resetpassword": "Resetovat heslo",
+       "botpasswords-label-restrictions": "Omezení užití:",
+       "botpasswords-label-grants-column": "Přiděleno",
+       "botpasswords-bad-appid": "Název bota „$1“ není platný.",
+       "botpasswords-insert-failed": "Nepodařilo se přidat název bota „$1“. Nebyl už přidán?",
+       "botpasswords-update-failed": "Nepodařilo se aktualizovat název bota „$1“. Nebyl smazán?",
+       "botpasswords-created-title": "Heslo pro bota vytvořeno",
+       "botpasswords-created-body": "Heslo pro bota „$1“ bylo úspěšně vytvořeno.",
+       "botpasswords-updated-title": "Heslo pro bota aktualizováno",
+       "botpasswords-updated-body": "Heslo pro bota „$1“ bylo úspěšně aktualizováno.",
+       "botpasswords-deleted-title": "Heslo pro bota smazáno",
+       "botpasswords-deleted-body": "Heslo pro bota „$1“ bylo smazáno.",
+       "botpasswords-newpassword": "Nové přihlašovací heslo pro bota <strong>$1</strong> je <strong>$2</strong>. <em>Zaznamenejte si je pro budoucí použití.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider není dostupný.",
+       "botpasswords-restriction-failed": "Toto přihlášení bylo zamítnuto omezením hesel pro boty.",
+       "botpasswords-invalid-name": "Uvedené uživatelské jméno neobsahuje oddělovač hesel pro boty („$1“).",
+       "botpasswords-not-exist": "Uživatel „$1“ nemá heslo pro bota nazvaného „$2“.",
        "resetpass_forbidden": "Hesla nelze změnit.",
        "resetpass-no-info": "K této stránce mají přímý přístup jen přihlášení uživatelé.",
        "resetpass-submit-loggedin": "Změnit heslo",
        "passwordreset-emailtext-ip": "Někdo (patrně vy, z IP adresy $1) zažádal na {{grammar:6sg|{{SITENAME}}}} ($4) o nastavení nového hesla k vašemu účtu. K této adrese {{PLURAL:$3|je přiřazen následující účet|jsou přiřazeny následující účty}}:\n\n$2\n\n{{PLURAL:$3|Toto dočasné heslo|Tato dočasná hesla}} vyprší za {{PLURAL:$5|jeden den|$5 dny|$5 dnů}}.\nNyní byste se měli přihlásit a zvolit si nové heslo. Pokud tento požadavek poslal někdo jiný nebo jste si na své staré heslo vzpomněli, a nechcete ho tedy změnit, můžete tuto zprávu ignorovat a nadále používat původní heslo.",
        "passwordreset-emailtext-user": "{{gender:$1|Uživatel|Uživatelka}} $1 na {{grammar:6sg|{{SITENAME}}}} {{gender:$1|zažádal|zažádala}} na {{grammar:6sg|{{SITENAME}}}} ($4) o nastavení nového hesla k vašemu\núčtu. K této adrese {{PLURAL:$3|je přiřazen následující účet|jsou přiřazeny následující účty}}:\n\n$2\n\n{{PLURAL:$3|Toto dočasné heslo|Tato dočasná hesla}} vyprší {{PLURAL:$5|za jeden den|za $5 dny|za $5 dnů}}.\nNyní byste se měl(a) přihlásit a zvolit si nové heslo. Pokud tento požadavek\nposlal někdo jiný nebo jste si na své staré heslo vzpomněl(a), a nechcete ho\ntedy změnit, můžete tuto zprávu ignorovat a nadále používat původní heslo.",
        "passwordreset-emailelement": "Uživatelské jméno: \n$1\n\nDočasné heslo: \n$2",
-       "passwordreset-emailsentemail": "Pokud je to registrovaná e-mailová adresa k vašemu účtu, bude vám zaslán e-mail pro získání nového hesla.",
-       "passwordreset-emailsentusername": "Pokud existuje odpovídající registrovaná e-mailová adresa, bude vám zaslán e-mail pro získání nového hesla.",
+       "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",
        "changeemail": "Změna nebo odstranění e-mailové adresy",
        "usercssyoucanpreview": "<strong>Tip:</strong> Použijte tlačítko „{{int:showpreview}}“ k testování vašeho nového CSS před uložením.",
        "userjsyoucanpreview": "<strong>Tip:</strong> Použijte tlačítko „{{int:showpreview}}“ k testování vašeho nového JavaScriptu před uložením.",
        "usercsspreview": "<strong>Pamatujte, že si prohlížíte jen náhled vašeho uživatelského CSS, jelikož dosud nebyl uložen!</strong>",
-       "userjspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled vašeho uživatelského JavaScriptu, jelikož dosud nebyl uložen!</strong>'",
+       "userjspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled svého uživatelského JavaScriptu, jelikož dosud nebyl uložen!</strong>",
        "sitecsspreview": "<strong>Pamatujte, že si prohlížíte jen náhled tohoto CSS, jelikož dosud nebylo uloženo!</strong>",
        "sitejspreview": "<strong>Pamatujte, že testujete a prohlížíte pouze náhled tohoto JavaScriptu, jelikož dosud nebyl uložen!</strong>",
        "userinvalidcssjstitle": "<strong>Varování:</strong> Vzhled „$1“ neexistuje. Nezapomeňte, že uživatelské .css a .js soubory používají malá písmena, např. {{ns:user}}:{{BASEPAGENAME}}/vector.css, nikoli {{ns:user}}:{{BASEPAGENAME}}/Vector.css.",
        "userrights": "Správa uživatelských skupin",
        "userrights-lookup-user": "Spravovat uživatelské skupiny",
        "userrights-user-editname": "Zadejte uživatelské jméno:",
-       "editusergroup": "Upravit uživatelské skupiny",
+       "editusergroup": "Upravit {{GENDER:$1|uživatelské}} skupiny",
        "editinguser": "Úprava práv {{GENDER:$1|uživatele|uživatelky}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Upravit uživatelské skupiny",
-       "saveusergroups": "Uložit uživatelské skupiny",
+       "saveusergroups": "Uložit {{GENDER:$1|uživatelské}} skupiny",
        "userrights-groupsmember": "{{GENDER:$2|Člen|Členka}} {{PLURAL:$1|skupiny|skupin}}:",
        "userrights-groupsmember-auto": "Automaticky {{GENDER:$2|člen|členka}} {{PLURAL:$1|skupiny|skupin}}:",
        "userrights-groups-help": "Můžete měnit skupiny, do nichž je {{GENDER:$1|uživatel zařazen|uživatelka zařazena}}.\n* Zaškrtnuté políčko znamená, že {{GENDER:$1|uživatel|uživatelka}} je v dané skupině.\n* Nezaškrtnuté políčko značí, že {{GENDER:$1|uživatel|uživatelka}} v dané skupině není.\n* Hvězdička (*) znamená, že nemůžete danou skupinu odstranit, jakmile ji přidáte, nebo naopak.",
        "right-createpage": "Zakládání stránek (které nejsou diskusní)",
        "right-createtalk": "Zakládání diskusních stránek",
        "right-createaccount": "Vytváření nových uživatelských účtů",
+       "right-autocreateaccount": "Automatické přihlášení externím uživatelským účtem",
        "right-minoredit": "Označování editací jako malé",
        "right-move": "Přesouvání stránek",
        "right-move-subpages": "Přesouvání stránek i s jejich podstránkami",
        "right-blockemail": "Blokování možnosti poslat e-mail",
        "right-hideuser": "Blokování uživatelského jména a jeho skrytí",
        "right-ipblock-exempt": "Obcházení blokování IP adres, jejich rozsahů a autobloků",
-       "right-proxyunbannable": "Obcházení automatického blokování proxy serverů",
        "right-unblockself": "Odblokování sebe sama",
        "right-protect": "Měnění úrovně zámku a editace kaskádově zamčených stránek",
        "right-editprotected": "Editace stránek zamčených na „{{int:protect-level-sysop}}“",
        "right-noratelimit": "Imunita vůči rychlostním limitům",
        "right-import": "Import stránek z jiných wiki",
        "right-importupload": "Import stránek nahráním souboru",
-       "right-patrol": "Označování úprav jako prověřené",
-       "right-autopatrol": "Automatické označování editací jako prověřených",
-       "right-patrolmarks": "Zobrazování patrolovacích značek v Posledních změnách",
+       "right-patrol": "Označování cizích editací jako prověřených",
+       "right-autopatrol": "Automatické označování vlastních editací jako prověřených",
+       "right-patrolmarks": "Zobrazování záznamů o prověření v Posledních změnách",
        "right-unwatchedpages": "Zobrazování seznamu nesledovaných stránek",
        "right-mergehistory": "Slučování historií stránek",
        "right-userrights": "Nastavování práv ostatním uživatelům",
        "right-managechangetags": "Vytváření [[Special:Tags|značek]] a jejich mazání z databáze",
        "right-applychangetags": "Přidávání [[Special:Tags|značek]] k vlastním změnám",
        "right-changetags": "Přidávání libovolných [[Special:Tags|značek]] na jednotlivé revize a protokolovací záznamy a jejich odebírání",
+       "grant-generic": "Balíček oprávnění „$1“",
+       "grant-group-page-interaction": "Interakce se stránkami",
+       "grant-group-file-interaction": "Interakce se soubory",
+       "grant-group-watchlist-interaction": "Interakce s vaším seznamem sledovaných stránek",
+       "grant-group-email": "Rozesílání e-mailů",
+       "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-other": "Různé činnosti",
+       "grant-blockusers": "Blokovat a odblokovávat uživatele",
+       "grant-createaccount": "Zakládat účty",
+       "grant-createeditmovepage": "Vytvářet, editovat a přesouvat stránky",
+       "grant-delete": "Mazat stránky, revize a protokolovací záznamy",
+       "grant-editinterface": "Editovat jmenný prostor MediaWiki a uživatelské CSS/JavaScript",
+       "grant-editmycssjs": "Editovat váš uživatelský CSS/JavaScript",
+       "grant-editmyoptions": "Změna vašich uživatelských nastavení",
+       "grant-editmywatchlist": "Upravovat váš seznam sledovaných stránek",
+       "grant-editpage": "Editovat existující stránky",
+       "grant-editprotected": "Editovat zamčené stránky",
+       "grant-highvolume": "Hromadné editace",
+       "grant-oversight": "Skrývat uživatele a utajovat revize",
+       "grant-patrol": "Patrolovat změny stránek",
+       "grant-protect": "Zamykat a odemykat stránky",
+       "grant-rollback": "Vracet editace zpět",
+       "grant-sendemail": "Posílat e-maily ostatním uživatelům",
+       "grant-uploadeditmovefile": "Načítat, nahrazovat a přesouvat soubory",
+       "grant-uploadfile": "Načítat nové soubory",
+       "grant-basic": "Základní oprávnění",
+       "grant-viewdeleted": "Prohlížet si smazané soubory a stránky",
+       "grant-viewmywatchlist": "Prohlížet si váš seznam sledovaných stránek",
        "newuserlogpage": "Kniha nových uživatelů",
        "newuserlogpagetext": "Toto je záznam nově zaregistrovaných uživatelů.",
        "rightslog": "Kniha práv uživatelů",
        "action-createpage": "vytvářet stránky",
        "action-createtalk": "vytvářet diskusní stránky",
        "action-createaccount": "vytvořit tento uživatelský účet",
+       "action-autocreateaccount": "automaticky založit tento externí uživatelský účet",
        "action-history": "prohlížet si historii této stránky",
        "action-minoredit": "označit tuto editaci jako malou",
        "action-move": "přesunout tuto stránku",
        "action-rollback": "rychle revertovat úpravy posledního uživatele editujícího danou stránku",
        "action-import": "importovat stránky z jiné wiki",
        "action-importupload": "importovat stránky z načteného souboru",
-       "action-patrol": "označit úpravy ostatních jako zhlédnuté",
+       "action-patrol": "označovat cizí editace jako prověřené",
        "action-autopatrol": "označit vlastní úpravy jako zhlédnuté",
        "action-unwatchedpages": "zobrazit seznam nesledovaných stránek",
        "action-mergehistory": "sloučit historii této stránky",
        "log-title-wildcard": "Hledat názvy začínající na tento text",
        "showhideselectedlogentries": "Ukázat/skrýt vybrané záznamy",
        "log-edit-tags": "Editovat značky vybraných protokolovacích záznamů",
+       "checkbox-select": "Vybrat: $1",
+       "checkbox-all": "Vše",
+       "checkbox-none": "Nic",
+       "checkbox-invert": "Obrátit",
        "allpages": "Všechny stránky",
        "nextpage": "Další stránka ($1)",
        "prevpage": "Předchozí stránka ($1)",
        "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-summary": "Následující seznam obsahuje oprávnění s odpovídajícím přístupem 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ě oprávnění, která uživatel aplikaci přidělil. 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-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-msg": "Sledovací kategorie",
        "wlshowhideanons": "anonymní uživatele",
        "wlshowhidepatr": "prověřené editace",
        "wlshowhidemine": "moje editace",
+       "wlshowhidecategorization": "kategorizaci stránek",
        "watchlist-options": "Možnosti sledovaných stránek",
        "watching": "Přidávám na seznam sledovaných stránek…",
        "unwatching": "Odebírám ze seznamu sledovaných stránek…",
        "revertpage": "Editace uživatele „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|diskuse]]) vráceny do předchozího stavu, jehož autorem je „[[User:$1|$1]]“",
        "revertpage-nouser": "Editace skrytého uživatele vráceny do předchozího stavu, jehož {{GENDER:$1|autorem|autorkou}} je „[[User:$1|$1]]“",
        "rollback-success": "Editace uživatele $1 byly vráceny na poslední verzi od uživatele $2.",
-       "sessionfailure-title": "Chyba sezení",
+       "sessionfailure-title": "Chyba relace",
        "sessionfailure": "Zřejmě je nějaký problém s vaším přihlášením;\nvámi požadovaná činnost byla stornována jako prevence před neoprávněným přístupem.\nStiskněte tlačítko „zpět“, obnovte stránku, ze které jste přišli, a zkuste činnost znovu.",
        "changecontentmodel": "Změna modelu obsahu stránky",
        "changecontentmodel-legend": "Změnit model obsahu",
        "changecontentmodel-nodirectediting": "Model obsahu $1 nepodporuje přímou editaci",
        "log-name-contentmodel": "Kniha změny modelů obsahu",
        "log-description-contentmodel": "Události týkající se modelů obsahu stránek",
-       "logentry-contentmodel-change": "$1 {{GENDER:$2|změnil|změnila}} obsah modelu stránky $3 z „$4“ na „$5“",
+       "logentry-contentmodel-change": "$1 {{GENDER:$2|změnil|změnila}} model obsahu stránky $3 z „$4“ na „$5“",
        "logentry-contentmodel-change-revertlink": "vrátit",
        "logentry-contentmodel-change-revert": "vrácení zpět",
        "protectlogpage": "Kniha zamčení",
        "unblock": "Odblokovat uživatele",
        "blockip": "Zablokovat {{GENDER:$1|uživatele|uživatelku}}",
        "blockip-legend": "Zablokovat uživatele",
-       "blockiptext": "Tento formulář slouží k zablokování editací z konkrétní IP adresy nebo uživatelského jména.\nToto by mělo být používáno jen v souladu s [[{{MediaWiki:Policy-url}}|pravidly]].\nUdejte přesný důvod níže (například ocitujte, které stránky byly poškozeny).",
+       "blockiptext": "Tento formulář slouží k zablokování editací z konkrétní IP adresy nebo uživatelského jména.\nToto by mělo být používáno jen v souladu s [[{{MediaWiki:Policy-url}}|pravidly]].\nUdejte přesný důvod níže (například ocitujte, které stránky byly poškozeny).\nIP rozsahy můžete blokovat pomocí syntaxe [https://cs.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; největší dovolený rozsah je /$1 pro IPv4 a /$2 pro IPv6.",
        "ipaddressorusername": "IP adresa nebo uživatelské jméno:",
        "ipbexpiry": "Čas vypršení:",
        "ipbreason": "Důvod:",
        "block-log-flags-hiddenname": "uživatelské jméno skryto",
        "range_block_disabled": "Blokování rozsahů IP adres je zakázáno.",
        "ipb_expiry_invalid": "Neplatný čas vypršení.",
+       "ipb_expiry_old": "Čas vypršení je v minulosti.",
        "ipb_expiry_temp": "Blokování skrytých uživatelských jmen by měla být trvalá.",
        "ipb_hide_invalid": "Tento účet nelze utajit; má více než $1 {{PLURAL:$1|editaci|editace|editací}}.",
        "ipb_already_blocked": "„$1“ již je zablokován.",
        "lockedbyandtime": "({{gender:$1|zamkl|zamkla|zamkl}} $1 $2 v $3)",
        "move-page": "Přesunout „$1“",
        "move-page-legend": "Přesunout stránku",
-       "movepagetext": "Použitím tohoto formuláře změníte název stránky a přesunete i celou její historii na nový název.\nPůvodní název se stane přesměrováním na nový název.\nPřesměrování na původní název můžete nechat aktualizovat automaticky.\nPokud nenecháte, nezapomeňte poté zkontrolovat [[Special:DoubleRedirects|dvojitá]] nebo [[Special:BrokenRedirects|přerušená]] přesměrování.\nJe vaší zodpovědností zajistit, aby odkazy stále vedly tam, kam mají.\n\nStránku '''není možno''' přejmenovat, pokud pod cílovým názvem již nějaká stránka existuje, s výjimkou situace, kdy je cílová stránka přesměrováním na tuto stránku a nemá žádnou historii editací.\nTo znamená, že stránku můžete přesunout zpět na původní název, pokud uděláte chybu, a že nemůžete přepsat existující stránku.\n\n'''Upozornění!'''\nPřejmenování oblíbené stránky může být drastická a nečekaná změna;\npředtím, než změnu provedete, se ujistěte, že chápete důsledky svého kroku.",
-       "movepagetext-noredirectfixer": "Použitím tohoto formuláře změníte název stránky a přesunete i celou její historii na nový název.\nPůvodní název se stane přesměrováním na nový název.\nNezapomeňte poté zkontrolovat [[Special:DoubleRedirects|dvojitá]] nebo [[Special:BrokenRedirects|přerušená]] přesměrování.\nJe vaší zodpovědností zajistit, aby odkazy stále vedly tam, kam mají.\n\nStránku '''není možno''' přejmenovat, pokud pod cílovým názvem již nějaká stránka existuje, s výjimkou situace, kdy je cílová stránka prázdná nebo je přesměrováním na tuto stránku a nemá žádnou historii editací.\nTo znamená, že stránku můžete přesunout zpět na původní název, pokud uděláte chybu, a že nemůžete přepsat existující stránku.\n\n'''Upozornění!'''\nPřejmenování oblíbené stránky může být drastická a nečekaná změna; předtím, než změnu provedete, se prosím ujistěte, že chápete důsledky svého kroku.",
+       "movepagetext": "Použitím tohoto formuláře změníte název stránky a přesunete i celou její historii na nový název.\nPůvodní název se stane přesměrováním na nový název.\nPřesměrování na původní název můžete nechat aktualizovat automaticky.\nPokud nenecháte, nezapomeňte poté zkontrolovat [[Special:DoubleRedirects|dvojitá]] nebo [[Special:BrokenRedirects|přerušená]] přesměrování.\nJe vaší zodpovědností zajistit, aby odkazy stále vedly tam, kam mají.\n\nStránku <strong>není možné</strong> přejmenovat, pokud pod cílovým názvem již nějaká stránka existuje, s výjimkou situace, kdy je cílová stránka přesměrováním na tuto stránku a nemá žádnou historii editací.\nTo znamená, že stránku můžete přesunout zpět na původní název, pokud uděláte chybu, a že nemůžete přepsat existující stránku.\n\n<strong>Poznámka:</strong>\nPřejmenování oblíbené stránky může být drastická a nečekaná změna;\npředtím, než změnu provedete, se ujistěte, že chápete důsledky svého kroku.",
+       "movepagetext-noredirectfixer": "Použitím tohoto formuláře změníte název stránky a přesunete i celou její historii na nový název.\nPůvodní název se stane přesměrováním na nový název.\nNezapomeňte poté zkontrolovat [[Special:DoubleRedirects|dvojitá]] nebo [[Special:BrokenRedirects|přerušená]] přesměrování.\nJe vaší zodpovědností zajistit, aby odkazy stále vedly tam, kam mají.\n\nStránku <strong>není možno</strong> přejmenovat, pokud pod cílovým názvem již nějaká stránka existuje, s výjimkou situace, kdy je cílová stránka prázdná nebo je přesměrováním na tuto stránku a nemá žádnou historii editací.\nTo znamená, že stránku můžete přesunout zpět na původní název, pokud uděláte chybu, a že nemůžete přepsat existující stránku.\n\n<strong>Poznámka:</strong>\nPřejmenování oblíbené stránky může být drastická a nečekaná změna; předtím, než změnu provedete, se prosím ujistěte, že chápete důsledky svého kroku.",
        "movepagetalktext": "Pokud zaškrtnete toto pole, přidružená diskusní stránka bude automaticky přesunuta na nový název, leda by tam již neprázdná diskusní stránka existovala.\n\nV takovém případě musíte stránky přesunout nebo sloučit ručně, přejete-li si to.",
        "moveuserpage-warning": "'''Upozornění:''' Chystáte se přesunout uživatelskou stránku. Uvědomte si prosím, že bude přesunuta pouze tato stránka, ale uživatel ''nebude'' přejmenován.",
        "movecategorypage-warning": "<strong>Upozornění:</strong> Chystáte se přesunout stránku kategorie. Uvědomte si, že bude přesunuta pouze tato stránka a že žádné stránky v původní kategorii <em>nebudou</em> do nové překategorizovány.",
        "movenosubpage": "Tato stránka nemá žádné podstránky.",
        "movereason": "Důvod:",
        "revertmove": "vrátit",
-       "delete_and_move_text": "==Je potřeba smazání==\n\nCílová stránka „[[:$1]]“ již existuje. Přejete si ji smazat pro uvolnění místa pro přesun?",
+       "delete_and_move_text": "Cílová stránka „[[:$1]]“ již existuje.\nPřejete si ji smazat pro uvolnění místa pro přesun?",
        "delete_and_move_confirm": "Ano, smazat cílovou stránku",
        "delete_and_move_reason": "Smazáno pro umožnění přesunu z „[[$1]]“",
        "selfmove": "Původní a nový název jsou stejné; nelze stránku přesunout na sebe samu.",
        "move-leave-redirect": "Ponechat přesměrování",
        "protectedpagemovewarning": "'''Varování:''' Tato stránka byla zamčena, takže ji mohou přesouvat pouze správci.\nNíže je pro přehled zobrazen nejnovější protokolovací záznam:",
        "semiprotectedpagemovewarning": "'''Poznámka:''' Tato stránka byla zamčena, takže ji mohou přesouvat pouze registrovaní uživatelé.\nNíže je pro přehled zobrazen nejnovější protokolovací záznam:",
-       "move-over-sharedrepo": "== Soubor existuje ==\n[[:$1]] existuje ve sdíleném úložišti. Přesun souboru na tento název způsobí potlačení sdíleného souboru.",
+       "move-over-sharedrepo": "[[:$1]] existuje ve sdíleném úložišti. Přesun souboru na tento název způsobí potlačení sdíleného souboru.",
        "file-exists-sharedrepo": "Vybrané jméno souboru je již používáno ve sdíleném úložišti.\nProsíme, vyberte jiné jméno.",
        "export": "Exportovat stránky",
        "exporttext": "Můžete exportovat text a historii editací některé stránky nebo sady stránek zabalené v XML. Výsledný soubor lze naimportovat do jiné wiki, která běží na software MediaWiki, pomocí [[Special:Import|importovací stránky]].\n\nDo níže uvedeného editačního pole zadejte názvy stránek, které chcete exportovat; každý řádek jeden název. Zvolte také, zda se mají exportovat i starší verze stránky včetně informací v historii editací, nebo jen aktuální verze s informací o poslední editaci.\n\nV druhém případě můžete také používat přímý odkaz, např. pomocí [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] se vyexportuje „[[{{MediaWiki:Mainpage}}]]“.",
        "export-download": "Nabídnout uložení jako soubor",
        "export-templates": "Zahrnout šablony",
        "export-pagelinks": "Zahrnout odkazované stránky až do hloubky:",
+       "export-manual": "Přidat stránky ručně:",
        "allmessages": "Všechna systémová hlášení",
        "allmessagesname": "Označení hlášení",
        "allmessagesdefault": "Původní text",
        "import-error-special": "Stránka „$1“ se nenaimportovala, protože patří do speciálního jmenného prostoru, ve kterém stránky být nemohou.",
        "import-error-invalid": "Stránka „$1“ se nenaimportovala, protože název, pod kterým by se naimportovala, je na této wiki nepoužitelný.",
        "import-error-unserialize": "Nepodařilo se deserializovat revizi $2 stránky „$1“. Revize měla používat model obsahu $3 serializovaný jako $4.",
-       "import-error-bad-location": "Revizi $2 používající obsahový model $3 nelze uložit na \"$1\" na této wiki, neboť tento model není na této stránce podporován.",
+       "import-error-bad-location": "Revizi $2 používající model obsahu $3 nelze uložit jako „$1“ na této wiki, neboť tento model není na této stránce podporován.",
        "import-options-wrong": "{{PLURAL:$2|Chybná volba|Chybné volby}}: <nowiki>$1</nowiki>",
        "import-rootpage-invalid": "Zadaný název kořenové stránky je neplatný.",
        "import-rootpage-nosubpage": "Ve jmenném prostoru „$1“ kořenové stránky nejsou dovoleny podstránky.",
        "javascripttest-pagetext-frameworks": "Zvolte jednu z následujících testovacích knihoven: $1",
        "javascripttest-pagetext-skins": "Zvolte vzhled, pod kterým se mají testy spustit:",
        "javascripttest-qunit-intro": "Vizte [$1 dokumentaci testování] na mediawiki.org",
-       "tooltip-pt-userpage": "Vaše uživatelská stránka",
+       "tooltip-pt-userpage": "{{GENDER:|Vaše uživatelská}} stránka",
        "tooltip-pt-anonuserpage": "Uživatelská stránka pro IP adresu, ze které editujete",
-       "tooltip-pt-mytalk": "Vaše diskusní stránka",
+       "tooltip-pt-mytalk": "{{GENDER:|Vaše}} diskusní stránka",
        "tooltip-pt-anontalk": "Diskuse o editacích provedených z této IP adresy",
-       "tooltip-pt-preferences": "Vaše nastavení",
+       "tooltip-pt-preferences": "{{GENDER:|Vaše}} nastavení",
        "tooltip-pt-watchlist": "Seznam stránek, jejichž změny sledujete",
-       "tooltip-pt-mycontris": "Seznam vašich příspěvků",
+       "tooltip-pt-mycontris": "Seznam {{GENDER:|vašich}} příspěvků",
        "tooltip-pt-anoncontribs": "Seznam editací provedených z této IP adresy",
        "tooltip-pt-login": "Doporučujeme vám přihlásit se, ovšem není to povinné.",
        "tooltip-pt-logout": "Odhlásit se",
        "tooltip-t-recentchangeslinked": "Nedávné změny stránek, na které je odkazováno",
        "tooltip-feed-rss": "RSS kanál pro tuto stránku",
        "tooltip-feed-atom": "Atom kanál pro tuto stránku",
-       "tooltip-t-contributions": "Prohlédnout si seznam příspěvků tohoto uživatele",
-       "tooltip-t-emailuser": "Poslat e-mail tomuto uživateli",
+       "tooltip-t-contributions": "Seznam příspěvků {{GENDER:$1|tohoto uživatele|této uživatelky}}",
+       "tooltip-t-emailuser": "Poslat e-mail {{GENDER:$1|tomuto uživateli|této uživatelce}}",
        "tooltip-t-info": "Více informací o této stránce",
        "tooltip-t-upload": "Nahrát obrázky či jiná multimédia",
        "tooltip-t-specialpages": "Seznam všech speciálních stránek",
        "pageinfo-category-files": "Počet souborů",
        "markaspatrolleddiff": "Označit jako prověřené",
        "markaspatrolledtext": "Označit tuto stránku jako prověřenou",
+       "markaspatrolledtext-file": "Označit tuto verzi souboru jako prověřenou",
        "markedaspatrolled": "Označeno jako prověřené",
        "markedaspatrolledtext": "Vybraná verze stránky [[:$1]] byla označena jako prověřená.",
        "rcpatroldisabled": "Hlídka posledních změn vypnuta",
-       "rcpatroldisabledtext": "Hlídka posledních změn je momentálně vypnuta.",
+       "rcpatroldisabledtext": "Patrola posledních změn je momentálně vypnuta.",
        "markedaspatrollederror": "Nelze označit za prověřené",
        "markedaspatrollederrortext": "Musíte zvolit revizi, která má být označena jako prověřená.",
        "markedaspatrollederror-noautopatrol": "Nemáte dovoleno označovat vlastní editace jako prověřené.",
        "newimages-legend": "Filtr",
        "newimages-label": "Název souboru (nebo jeho část):",
        "newimages-showbots": "Zobrazit soubory načtené boty",
+       "newimages-hidepatrolled": "Skrýt prověřená načtení souborů",
        "noimages": "Není co zobrazit.",
        "ilsubmit": "Hledat",
        "bydate": "podle data",
        "scarytranscludefailed-httpstatus": "[Nepodařilo se načíst šablonu pro $1: HTTP $2]",
        "scarytranscludetoolong": "[Příliš dlouhé URL]",
        "deletedwhileediting": "'''Upozornění''': V průběhu vaší editace byla tato stránka smazána!",
-       "confirmrecreate": "Uživatel [[User:$1|$1]] ([[User talk:$1|diskuse]]) tuto stránku smazal poté, co jste začali editovat, s odůvodněním:\n: „$2“\nOpravdu si přejete znovu tuto stránku založit?",
-       "confirmrecreate-noreason": "{{gender:$1|Uživatel|Uživatelka|Uživatel}} [[User:$1|$1]] ([[User talk:$1|diskuse]]) {{gender:$1|smazal|smazala|smazal}} tuto stránku poté, co jste {{gender:|začal|začala|začali}} s editací. Potvrďte, zda chcete stránku skutečně znovu založit.",
+       "confirmrecreate": "{{GENDER:$1|Uživatel|Uživatelka}} [[User:$1|$1]] ([[User talk:$1|diskuse]]) tuto stránku {{GENDER:$1|smazal|smazala}} poté, co jste {{GENDER:|začal|začala|začali}} editovat, s odůvodněním:\n: „$2“\nOpravdu si přejete znovu tuto stránku založit?",
+       "confirmrecreate-noreason": "{{GENDER:$1|Uživatel|Uživatelka}} [[User:$1|$1]] ([[User talk:$1|diskuse]]) {{GENDER:$1|smazal|smazala}} tuto stránku poté, co jste {{GENDER:|začal|začala|začali}} s editací. Potvrďte, zda chcete stránku skutečně znovu založit.",
        "recreate": "Založit znovu",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Aktualizovat cachovanou verzi této stránky?",
        "autoredircomment": "Přesměrování na [[$1]]",
        "autosumm-new": "Založena nová stránka s textem „$1“",
        "autosumm-newblank": "Založena prázdná stránka",
+       "size-bytes": "$1 {{PLURAL:$1|bajt|bajty|bajtů}}",
        "size-kilobytes": "$1 KB",
        "lag-warn-normal": "Změny za {{PLURAL:$1|poslední sekundu|poslední $1 sekundy|posledních $1 sekund}} nemusí být v tomto seznamu zobrazeny.",
        "lag-warn-high": "Protože je databázový server právě mimořádně vytížen, nemusí být změny za {{PLURAL:$1|poslední sekundu|poslední $1 sekundy|posledních $1 sekund}} v tomto seznamu zobrazeny.",
        "hebrew-calendar-m11-gen": "avu",
        "hebrew-calendar-m12-gen": "elulu",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskuse]])",
+       "timezone-local": "místní čas",
        "duplicate-defaultsort": "Upozornění: Implicitní klíč řazení (DEFAULTSORTKEY) „$2“ přepisuje dříve nastavenou hodnotu „$1“.",
        "duplicate-displaytitle": "<strong>Upozornění:</strong> Předchozí zobrazovaný název „$1“ je nahrazen zobrazovaným názvem „$2“.",
        "invalid-indicator-name": "<strong>Chyba:</strong> Atribut <code>name</code> indikátoru stavu stránky nesmí být prázdný.",
        "version-libraries-license": "Licence",
        "version-libraries-description": "Popis",
        "version-libraries-authors": "Autoři",
-       "redirect": "Přesměrování podle souboru, uživatele, stránky nebo ID revize",
+       "redirect": "Přesměrování podle ID souboru, uživatele, stránky, revize nebo protokolovacího záznamu",
        "redirect-legend": "Přesměrování na soubor či stránku",
-       "redirect-summary": "Tato speciální stránka přesměrovává na soubor (podle názvu), stránku (podle ID stránky nebo revize) nebo uživatele (podle číselného uživatelského ID). Použití: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] nebo [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Tato speciální stránka přesměrovává na soubor (podle názvu), stránku (podle ID stránky nebo revize), uživatele (podle číselného uživatelského ID) nebo protokolovací záznam (podle ID záznamu). Použití: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]],[[{{#Special:Redirect}}/user/101]] nebo [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Přejít",
        "redirect-lookup": "Najít:",
        "redirect-value": "Hodnota:",
        "redirect-page": "ID stránky",
        "redirect-revision": "Revizi stránky",
        "redirect-file": "Jméno souboru",
+       "redirect-logid": "ID protokolovacího záznamu",
        "redirect-not-exists": "Hodnota nenalezena",
        "fileduplicatesearch": "Hledání duplicitních souborů",
        "fileduplicatesearch-summary": "Hledání duplicitních souborů podle jejich hašů.",
        "tags-deactivate": "deaktivovat",
        "tags-hitcount": "$1 {{PLURAL:$1|změna|změny|změn}}",
        "tags-manage-no-permission": "Nemáte oprávnění spravovat značky pro změny.",
-       "tags-manage-blocked": "Nemůžete spravovat značky, když jste {{GENDER:zablokován|zablokována|zablokováni}}.",
+       "tags-manage-blocked": "Nemůžete spravovat značky, když jste {{GENDER:|zablokován|zablokována|zablokováni}}.",
        "tags-create-heading": "Vytvořit novou značku",
        "tags-create-explanation": "Nově vytvořené značky jsou implicitně k dispozici uživatelům a botům.",
        "tags-create-tag-name": "Název značky:",
        "tags-deactivate-not-allowed": "Značku „$1“ nelze deaktivovat.",
        "tags-deactivate-submit": "Deaktivovat",
        "tags-apply-no-permission": "Nemáte oprávnění přidávat značky k vlastním změnám",
-       "tags-apply-blocked": "Nemůžete ke svým změnám přidávat značky, když jste {{GENDER:zablokován|zablokována|zablokováni}}.",
+       "tags-apply-blocked": "Nemůžete ke svým změnám přidávat značky, když jste {{GENDER:|zablokován|zablokována|zablokováni}}.",
        "tags-apply-not-allowed-one": "Značku „$1“ není dovoleno ručně přidávat.",
        "tags-apply-not-allowed-multi": "Následující {{PLURAL:$2|značku|značky}} není dovoleno ručně přidávat: $1",
        "tags-update-no-permission": "Nemáte oprávnění přidávat libovolné značky na jednotlivé revize a protokolovací záznamy a odebírat je",
-       "tags-update-blocked": "Nemůžete přidávat nebo ubírat značky, když jste {{GENDER:zablokován|zablokována|zablokováni}}.",
+       "tags-update-blocked": "Nemůžete přidávat nebo odebírat značky, když jste {{GENDER:|zablokován|zablokována|zablokováni}}.",
        "tags-update-add-not-allowed-one": "Značku „$1“ není dovoleno ručně přidávat.",
        "tags-update-add-not-allowed-multi": "Následující {{PLURAL:$2|značku|značky}} není dovoleno ručně přidávat: $1",
        "tags-update-remove-not-allowed-one": "Značku „$1“ není dovoleno odebírat.",
        "expand_templates_generate_xml": "Zobrazit syntaktický strom v XML",
        "expand_templates_generate_rawhtml": "Zobrazit surové HTML",
        "expand_templates_preview": "Náhled",
-       "expand_templates_preview_fail_html": "<em>Protože {{SITENAME}} má povolené syrové HTML a došlo ke ztrátě dat sezení, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, zkuste to znovu.</strong>\nPokud to stále nebude fungovat, zkuste se [[Special:UserLogout|odhlásit]] a znovu přihlásit.",
+       "expand_templates_preview_fail_html": "<em>Protože {{SITENAME}} má povolené syrové HTML a došlo ke ztrátě dat relace, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, zkuste to znovu.</strong>\nPokud to stále nebude fungovat, zkuste se [[Special:UserLogout|odhlásit]] a znovu přihlásit.",
        "expand_templates_preview_fail_html_anon": "<em>Protože {{SITENAME}} má povolené syrové HTML a vy nejste přihlášeni, je náhled skryt kvůli ochraně před JavaScriptovými útoky.</em>\n\n<strong>Pokud to byl legitimní pokus o náhled, [[Special:UserLogin|přihlaste se]] a zkuste to znovu.</strong>",
+       "expand_templates_input_missing": "Musíte zadat alespoň nějaký vstupní text.",
        "pagelanguage": "Volba jazyka stránky",
        "pagelang-name": "Stránka",
        "pagelang-language": "Jazyk",
        "pagelang-use-default": "Použít implicitní jazyk",
        "pagelang-select-lang": "Vybrat jazyk",
+       "pagelang-submit": "Odeslat",
        "right-pagelang": "Změnit jazyk stránky",
        "action-pagelang": "měnit jazyk stránky",
        "log-name-pagelang": "Kniha změn jazyků",
        "mediastatistics-summary": "Statistika o typech načtených souborů. Zahrnuje vždy jen nejnovější verzi souboru. Staré nebo smazané verze se nezapočítávají.",
        "mediastatistics-nfiles": "$1 ($2 %)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2; $3 %)",
+       "mediastatistics-bytespertype": "Celková velikost souborů v této sekci: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2; $3 %).",
+       "mediastatistics-allbytes": "Celková velikost všech souborů: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtů}} ($2).",
        "mediastatistics-table-mimetype": "MIME typ",
        "mediastatistics-table-extensions": "Možné přípony",
        "mediastatistics-table-count": "Počet souborů",
        "mediastatistics-header-text": "Text",
        "mediastatistics-header-executable": "Spustitelné soubory",
        "mediastatistics-header-archive": "Komprimované formáty",
+       "mediastatistics-header-total": "Všechny soubory",
        "json-warn-trailing-comma": "Z JSONu {{PLURAL:$1|byla odstraněna 1 koncová čárka|byly odstraněny $1 koncové čárky|bylo odstraněno $1 koncových čárek}}",
        "json-error-unknown": "Došlo k potížím s JSONem. Chyba: $1",
        "json-error-depth": "Byla překročena maximální hloubka zásobníku",
        "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."
+       "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",
+       "sessionprovider-nocookies": "Možná jsou zakázány cookies. Ujistěte se, že máte cookies povoleny a zkuste to znovu.",
+       "randomrootpage": "Náhodná kořenová stránka"
 }
index 7b44872..3e1bdb3 100644 (file)
@@ -87,6 +87,7 @@
        "about": "опьсаниѥ",
        "article": "члѣнъ",
        "newwindow": "(иномь окънѣ)",
+       "cancel": "отъмѣтаниѥ",
        "moredotdotdot": "вѧщє ···",
        "mypage": "страница",
        "mytalk": "бєсѣда",
        "namespaces": "имєнъ просторꙑ",
        "navigation-heading": "плаваниѥ",
        "errorpagetitle": "блаꙁна",
+       "returnto": "къ страници ⁖ $1 ⁖ въꙁвращєниѥ ⁙",
        "tagline": "{{grammar:genitive|{{SITENAME}}}} страница",
        "help": "помощь",
        "search": "исканиѥ",
        "printableversion": "пєчатьнъ обраꙁъ",
        "permalink": "въиньна съвѧꙁь",
        "print": "пєчатаниѥ",
+       "view": "поꙁьрѣниѥ",
        "edit": "исправи",
        "create": "сътворѥниѥ",
        "editthispage": "си страницѧ исправлѥниѥ",
        "otherlanguages": "дроугꙑ ѩꙁꙑкꙑ",
        "redirectedfrom": "(прѣнаправлѥниѥ отъ ⁖ $1 ⁖)",
        "redirectpagesub": "прѣнаправлѥниѥ",
+       "redirectto": "прѣнаправлѥниѥ къ :",
        "lastmodifiedat": "страницѧ послѣдьнꙗ мѣна сътворѥна $2 · $1 бѣ ⁙",
        "jumpto": "прѣиди къ :",
        "jumptonavigation": "плаваниѥ",
        "copyrightpage": "{{ns:project}}:Творьцъ права",
        "currentevents": "сѫщѧѩ вѣщи",
        "currentevents-url": "Project:Сѫщѧѩ вѣщи",
+       "disclaimers": "отърицаниꙗ",
+       "disclaimerpage": "Project:Главьно отърицаниѥ",
        "edithelp": "помощь по исправлѥниѭ",
        "helppage-top-gethelp": "помощь",
        "mainpage": "главьна страница",
        "createaccount": "съꙁижди си мѣсто",
        "gotaccount": "мѣсто ти ѥстъ ли? $1",
        "gotaccountlink": "въниди",
+       "userlogin-resetpassword-link": "таино слово ꙁабꙑлъ ли ;",
        "createaccountreason": "какъ съмꙑслъ :",
        "createacct-reason": "какъ съмꙑслъ",
        "createacct-submit": "съꙁижди си мѣсто",
        "oldpassword": "старо таино слово :",
        "newpassword": "ново таино слово :",
        "retypenew": "опакꙑ ново таиноѥ слово напиши :",
+       "botpasswords-label-cancel": "отъмѣтаниѥ",
        "resetpass-submit-loggedin": "таина словєсє иꙁмѣнѥниѥ",
+       "resetpass-submit-cancel": "отъмѣтаниѥ",
        "passwordreset-username": "польꙃєватєлꙗ имѧ :",
        "changeemail-none": "(нѣстъ)",
        "link_sample": "съвѧꙁи имѧ",
        "media_tip": "дѣла съвѧꙁь",
        "sig_tip": "твои аѵтографъ и нꙑнѣшьна врѣмѧ и дьнь",
        "summary": "опьсаниѥ :",
+       "subject": "ѳєма :",
        "minoredit": "малаꙗ мѣна",
        "watchthis": "си страницѧ блюдєниѥ",
        "savearticle": "съхранѥниѥ",
        "revdelete-otherreason": "инъ или допльнитєл҄ьнъ съмꙑслъ :",
        "revdelete-reasonotherlist": "инъ съмꙑслъ",
        "mergehistory-reason": "какъ съмꙑслъ :",
+       "editundo": "отъмѣтаниѥ",
        "searchresults": "исканиꙗ слѣдьствиѥ",
        "searchresults-title": "исканиꙗ ⁖ $1 ⁖ слѣдьствиѥ",
        "viewprevnext": "виждь ($1 {{int:pipe-separator}} $2) ($3)",
        "recentchanges-label-newpage": "по сѥи мѣнꙑ нова страница сътворѥна ѥстъ",
        "recentchanges-label-minor": "малаꙗ мѣна",
        "recentchanges-label-bot": "сѭ мѣноу аѵтоматъ сътворилъ",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (таждє ꙁьри [[Special:NewPages|новъ страницѧ каталогъ]])",
+       "rclistfrom": "новъ мѣнъ каꙁаниѥ отъ $2 · $3",
        "rcshowhideminor": "$1 малꙑ мѣнꙑ",
        "rcshowhideminor-show": "каꙁаниѥ",
        "rcshowhideminor-hide": "съкрꙑтиѥ",
        "recentchangeslinked": "съвѧꙁанꙑ страницѧ",
        "recentchangeslinked-feed": "съвѧꙁанꙑ страницѧ",
        "recentchangeslinked-toolbox": "съвѧꙁанꙑ страницѧ",
+       "recentchangeslinked-title": "съвѧꙁанꙑ съ ⁖ $1 ⁖ мѣнꙑ",
        "recentchangeslinked-page": "страницѧ имѧ :",
        "upload": "положєниѥ дѣла",
        "uploadbtn": "положєниѥ дѣла",
        "sourcefilename": "источьна дѣла имꙗ :",
        "watchthisupload": "си дѣла блюдєниѥ",
        "upload-success-subj": "дѣло положєно ѥстъ",
+       "upload-dialog-button-cancel": "отъмѣтаниѥ",
        "license": "прощєниѥ :",
        "license-header": "прощєниѥ",
        "imgfile": "дѣло",
        "dellogpage": "поничьжєниꙗ їсторїꙗ",
        "deletionlog": "поничьжєниꙗ їсторїꙗ",
        "deletecomment": "какъ съмꙑслъ :",
+       "rollbacklink": "въꙁвращєниѥ",
+       "rollbacklinkcount": "въꙁвращєниѥ $1 {{PLURAL:$1|мѣнꙑ|мѣноу|мѣнъ}}",
        "protectlogpage": "ꙁабранѥниꙗ їсторїꙗ",
        "protectedarticle": "⁖ [[$1]] ⁖ ꙁабранѥна ѥстъ",
        "prot_1movedto2": "⁖ [[$1]] ⁖ нарєчєнъ ⁖ [[$2]] ⁖ ѥстъ",
        "invert": "обрати иꙁборъ",
        "namespace_association": "съвѧꙁанꙑ имєнъ просторꙑ",
        "blanknamespace": "(главьно)",
-       "contributions": "{{GENDER:$1|польꙃєватєлꙗ}} добродѣꙗниꙗ",
+       "contributions": "{{GENDER:$1|польꙃєватєлꙗ|польꙃєватєлицѧ}} добродѣꙗниꙗ",
        "contributions-title": "польꙃєватєлꙗ ⁖ $1 ⁖ добродѣꙗниꙗ",
        "mycontris": "добродѣꙗниꙗ",
        "anoncontribs": "добродѣꙗниꙗ",
        "contribsub2": "польꙃєватєлꙗ имѧ ⁖ {{GENDER:$3|$1}} ⁖ ѥстъ ($2)",
        "uctop": "(нꙑнѣщьн҄ь обраꙁъ)",
+       "month": "отъ мѣсѧца и давѣѥ :",
+       "year": "отъ лѣта и давѣѥ :",
        "sp-contributions-blocklog": "ꙁаграждєниꙗ їсторїꙗ",
        "sp-contributions-deleted": "поничьжєнꙑ добродѣꙗниꙗ",
        "sp-contributions-uploads": "положєнꙑ дѣла",
        "allmessages-filter-submit": "прѣиди",
        "import-upload-filename": "дѣла имѧ :",
        "javascripttest": "искоушєниѥ ⁖ JavaScript ⁖",
-       "tooltip-pt-userpage": "твоꙗ польꙃєватєл҄ьска страница",
-       "tooltip-pt-mytalk": "твоꙗ бєсѣдꙑ страница",
-       "tooltip-pt-preferences": "твои строи",
-       "tooltip-pt-mycontris": "твоѩ добродѣꙗнии каталогъ",
+       "tooltip-pt-userpage": "{{GENDER:|твоꙗ польꙃєватєл҄ьска}} страница",
+       "tooltip-pt-mytalk": "{{GENDER:|твоꙗ}} бєсѣдꙑ страница",
+       "tooltip-pt-preferences": "{{GENDER:|твои}} строи",
+       "tooltip-pt-watchlist": "страницѧ ижє ихъжє иꙁмѣнѥниꙗ подъ твоимь блюдєниѥмь сѫтъ",
+       "tooltip-pt-mycontris": "{{GENDER:|твоѩ}} добродѣꙗнии каталогъ",
        "tooltip-pt-logout": "ис̾ходъ",
        "tooltip-ca-talk": "си страницѧ бєсѣда",
+       "tooltip-ca-edit": "си страницѧ исправлѥниѥ",
        "tooltip-ca-viewsource": "си страница ꙁабранєна ѥстъ ⁙\nѥѩ источьнъ обраꙁъ видєти можєши",
        "tooltip-ca-protect": "си страницѧ ꙁабранєниѥ",
        "tooltip-ca-delete": "си страницѧ поничьжєниѥ",
        "tooltip-ca-move": "си страницѧ прѣимєнованиѥ",
        "tooltip-ca-watch": "си страницѧ блюдєниѥ",
        "tooltip-search": "ищи {{{grammar:genitive|{{SITENAME}}}}} страницѧ",
+       "tooltip-search-fulltext": "исканиѥ страницѧ ижє сѥ напьсаниѥ дрьжатъ",
        "tooltip-p-logo": "главьна страница",
        "tooltip-n-mainpage": "виждь главьноу страницѫ",
        "tooltip-n-mainpage-description": "виждь главьноу страницѫ",
        "tooltip-n-recentchanges": "послѣдьн҄ь мѣнъ каталогъ",
-       "tooltip-t-contributions": "виждь польꙃєватєлꙗ добродѣꙗнии каталогъ",
+       "tooltip-t-contributions": "{{GENDER:$1|польꙃєватєлꙗ|польꙃєватєлицѧ}} добродѣꙗнии каталогъ",
        "tooltip-t-upload": "положєниѥ дѣлъ",
        "tooltip-t-specialpages": "вьсѣѩ нарочьнъ страницѧ каталогъ",
        "tooltip-t-print": "сѥѩ страницѧ пєчатьнъ обраꙁъ",
-       "tooltip-ca-nstab-special": "си нарочьна страница ѥстъ · ѥѩжє иꙁмѣнꙗти нє можєши",
+       "tooltip-ca-nstab-user": "виждь польꙃєватєлꙗ страницѫ",
+       "tooltip-ca-nstab-special": "сѥ нарочьна страница ѥстъ · ѥѩжє иꙁмѣнꙗти нє можєши",
        "tooltip-ca-nstab-image": "виждь дѣла страницѫ",
        "tooltip-ca-nstab-category": "виждь катигорїѩ страницѫ",
        "tooltip-minoredit": "оꙁначи ꙗко малоу мѣноу",
        "tooltip-save": "твоѩ мѣнъ съхранѥниѥ",
        "tooltip-watch": "си страницѧ блюдєниѥ",
+       "tooltip-summary": "кратъко опьсаниѥ напьши",
        "pageinfo-header-edits": "мѣнъ їсторїꙗ",
        "pageinfo-header-restrictions": "страницѧ ꙁабранѥниѥ",
        "pageinfo-firstuser": "страницѧ творьць",
        "exif-artist": "творьць",
        "exif-languagecode": "ѩꙁꙑкъ",
        "exif-iimcategory": "катигорїꙗ",
+       "exif-orientation-1": "обꙑчьно",
        "exif-gpsspeed-k": "хїлїомєтрꙑ ꙁа часъ",
        "exif-gpsdestdistance-k": "хїлїомєтрꙑ",
        "exif-iimcategory-edu": "навꙑканиѥ",
        "logentry-newusers-create": "польꙃєватєльско мѣсто ⁖ $1 ⁖ {{GENDER:$2|сътворѥно}} ѥстъ",
        "logentry-upload-upload": "$1 {{GENDER:$2|положишє}} $3",
        "revdelete-summary": "мѣнꙑ опьсаниѥ",
+       "feedback-cancel": "отъмѣтаниѥ",
        "searchsuggest-search": "исканиѥ",
        "searchsuggest-containing": "сѥ дрьжащи···",
        "api-error-unknownerror": "нєвѣдома блаꙁна : ⁖ $1 ⁖",
index 6222974..ac70c5a 100644 (file)
        "savearticle": "Страницăна çырса хур",
        "preview": "Епле курăнĕ",
        "showpreview": "Маларах пăхни",
-       "showdiff": "Ð\9aÄ\95Ñ\80Ñ\82нÄ\95 Ñ\83лÑ\88Ä\83нÑ\83Ñ\81ем",
+       "showdiff": "УлÄ\83Ñ\88Ñ\82аÑ\80ниÑ\81ене ÐºÄ\83Ñ\82аÑ\80Ñ\82ни",
        "anoneditwarning": "'''Асăрхăр''': Эсир сайта хăвăр çинчен пĕлтермен, çавăнпа та ку страницăна улăштарнин журналне сирĕн IP-адреса çырса хума тивĕ.",
        "missingcommenttext": "Аяларах, тархасшăн, хăвар пĕлтерĕве çырăр.",
        "summary-preview": "Ăнлантару çапла пулĕ:",
        "recentchanges-label-bot": "Ку улшăнăва бот тунă",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (пăхăр [[Special:NewPages|çĕнĕ страницăсен списокĕ]])",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
-       "rclistfrom": "Юлашки улшăнусене $3 $2 вăхăтран пуçласа кăтартнă",
+       "recentchanges-submit": "Кăтарт",
+       "rclistfrom": "$2, $3 тытăнса çĕнĕ улăшăнисене кăтартни",
        "rcshowhideminor": "пĕчĕк тӳрлетнисене $1",
        "rcshowhideminor-show": "кăтартмалла",
        "rcshowhideminor-hide": "кăтартмалла мар",
        "rcshowhidemine": "хăвăн тӳрлетнисене $1",
        "rcshowhidemine-show": "кăтартмалла",
        "rcshowhidemine-hide": "кăтартмалла мар",
+       "rcshowhidecategorization-show": "Кăтарт",
        "rclinks": "Юлашки $2 кун хушшинче тунă $1 улшăнусене кăтартмалла<br />$3",
        "diff": "танл.",
        "hist": "ист",
        "unusedtemplateswlh": "ытти каçăсем",
        "randompage": "Ăнсăртран лекнĕ страница",
        "randomincategory-category": "Категори:",
+       "randomincategory-submit": "Куç",
        "randomredirect": "Ăнсăртран илнĕ куçару",
        "statistics": "Статистика",
        "statistics-header-users": "Хутшăнакансен статистики",
        "brokenredirectstext": "Ку куçару страницисем çук страницăна куçараççĕ:",
        "brokenredirects-edit": "тӳрлет",
        "brokenredirects-delete": "кăларса пăрах",
+       "withoutinterwiki-submit": "Кăтарт",
        "fewestrevisions": "Сахал тӳрлетнĕ статьясем",
        "nbytes": "$1 {{PLURAL:$1|байт|байтсем}}",
        "ncategories": "$1 {{PLURAL:$1|категори|категорисем}}",
        "mostimages": "Чи анлă усă куракан ӳкерчĕксем",
        "mostrevisions": "Чи нумай тӳрлетнĕ страницăсем",
        "prefixindex": "Сăмах пуçламăшĕсен кăтартмăшĕ",
+       "prefixindex-submit": "Кăтарт",
        "shortpages": "Кĕске статьясем",
        "longpages": "Вăрăм страницăсем",
        "deadendpages": "Нимĕнпе те çыхăнман страницăсем",
        "protectedtitles": "Юраман ятсем",
        "listusers": "Хутшăнакансен списокĕ",
        "newpages": "Çĕнĕ страницăсем",
+       "newpages-submit": "Кăтарт",
        "newpages-username": "Хутшăнакан:",
        "ancientpages": "Чи кивĕ статьясем",
        "move": "Ятне улăштар",
        "booksources-search": "Туп",
        "specialloguserlabel": "Хутшăнакан:",
        "log": "Логсем",
+       "logeventslist-submit": "Кăтарт",
        "all-logs-page": "Пĕтĕм логсем",
        "allpages": "Пĕтĕм страницăсем",
        "nextpage": "Тепĕр страницă ($1)",
        "allpages-bad-ns": "{{SITENAME}}-ра «$1» ят уçлăхĕ çук.",
        "cachedspecial-refresh-now": "Юлашкине пăх.",
        "categories": "Категорисем",
+       "categories-submit": "Кăтарт",
        "categoriespagetext": "Викинче çак категорисем пур.\n[[Special:UnusedCategories|Unused categories]] are not shown here.\nAlso see [[Special:WantedCategories|wanted categories]].",
        "special-categories-sort-count": "шучĕ тăрăх йĕркеле",
        "special-categories-sort-abc": "алфавит тăрăх йĕркеле",
        "unwatchthispage": "Сăнама пăрах",
        "notanarticle": "Ку статья мар",
        "watchlistall2": "пурте",
+       "watchlist-submit": "Кăтарт",
        "watching": "Сăнамаллисем шутне хушасси…",
        "unwatching": "Сăнав ят-йышĕнчен кăларса пăрахасси…",
        "enotif_reset": "Пур страницăсене те пăхнă пек палăрт",
        "confirm": "Çирĕплетни",
        "excontent": "ăшĕнче пулнă: \"$1\"",
        "excontentauthor": "ăшĕнче пулнă: \"$1\", пĕртен пĕр хушакан пулнă \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|talk]])",
+       "historyaction-submit": "Кăтарт",
        "actioncomplete": "Турăмăр",
        "deletedtext": "«$1» кăларса парахрăмăр.\nЮлашки кăларса пăрахнă статьясен списокне курмашкăн кунта пăхăр: $2.",
        "dellogpage": "Кăларса пăрахнисем",
        "rollback": "Тӳрлетнисене каялла куçарасси",
        "rollbacklink": "каялла тавăр",
        "rollbackfailed": "Каялла куçарнă çухна йăнăш тухнă",
-       "protectlogpage": "Ð¥Ó³Ñ\82Ä\95лев Ð¶Ñ\83Ñ\80налĕ",
+       "protectlogpage": "Ð¥Ó³Ñ\82Ä\95ленин Ð»Ð¾Ð³ĕ",
        "protectlogtext": "Аяларах эсир статьясене хӳтĕленин тата хӳтĕлев паллине пăрахаçланин журналне куратăр.",
        "protectedarticle": "«[[$1]]» страницăна хӳтĕлерĕмĕр",
        "unprotectedarticle": "«[[$1]]» страницăн хӳтĕлевне пăрахăçланă",
        "whatlinkshere-hideredirs": "куçарнисене $1",
        "whatlinkshere-hidelinks": "Каçаканнисене $1",
        "whatlinkshere-filters": "Аласем",
+       "whatlinkshere-submit": "Ту",
        "blockip": "{{GENDER:$1|хутшăнакана}} чар",
        "ipaddressorusername": "IP адрес е усă куракан ят:",
        "ipbreason": "Сăлтавĕ",
        "allmessages": "Система пĕлтерĕвĕсем",
        "allmessagesname": "Пĕлтерӳ",
        "allmessagescurrent": "Хальхи текст",
+       "allmessages-filter-submit": "Куç",
        "allmessages-filter-translate": "Куçар",
        "thumbnail-more": "Пысăклатмалли",
        "filemissing": "Файл тупăнмарĕ",
        "table_pager_limit_submit": "Ту",
        "table_pager_empty": "Тупăнмарĕ",
        "autosumm-blank": "Статьяна йăлтах пушатрĕ",
-       "autosumm-replace": "Страницăн ăшлăхне «$1» çине улăштарнă",
+       "autosumm-replace": "Ăшĕнчине улăштарнă \"$1\"",
        "autoredircomment": "[[$1]] çине куçарни",
        "autosumm-new": "Çĕнĕ страница \"$1\"",
        "watchlisttools-view": "Ку тӳрлетӳпе çыхăннăскерсем",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|сӳтсе явни]])",
        "version": "Верси",
+       "redirect-submit": "Куç",
        "fileduplicatesearch": "Пĕр пек файлсен шыравĕ",
        "fileduplicatesearch-legend": "Дубликатсен шыравĕ",
        "fileduplicatesearch-filename": "Файл ячĕ:",
index 65794d1..542fb47 100644 (file)
        "right-blockemail": "Blokere en brugers mulighed for at sende mail",
        "right-hideuser": "Blokere et brugernavn og skjule navnet",
        "right-ipblock-exempt": "Redigere fra blokerede IP-adresser",
-       "right-proxyunbannable": "Redigere gennem automatisk blokeret proxy",
        "right-unblockself": "Fjerne blokering af dig selv",
        "right-protect": "Ændre beskyttelsesniveauer og redigere cascade-beskyttede sider",
        "right-editprotected": "Redigere sider beskyttet som \"{{int:protect-level-sysop}}\"",
        "mimetype": "MIME-type:",
        "download": "DownloadHerunterladen",
        "unwatchedpages": "Ikke overvågede sider",
-       "listredirects": "Henvisningsliste",
+       "listredirects": "Liste over omdirigeringer",
        "listduplicatedfiles": "Liste over filer med dubletter",
        "listduplicatedfiles-summary": "Dette er en liste over filer, hvor den seneste version af filen er en kopi af den seneste version af en anden fil. Kun lokale filer er taget i betragtning.",
        "listduplicatedfiles-entry": "[[:File:$1|$1]] har [[$3|{{PLURAL:$2|en dublet|$2 dubletter}}]].",
        "logentry-newusers-create2": "Brugerkontoen $3 blev {{GENDER:$2|oprettet}} af $1",
        "logentry-newusers-byemail": "Brugerkontoen $3 blev {{GENDER:$2|oprettet}} af $1, og adgangskoden er sendt via e-mail",
        "logentry-newusers-autocreate": "Brugerkontoen $1 blev automatisk {{GENDER:$2|oprettet}}",
+       "logentry-protect-move_prot": "$1 {{GENDER:$2|flyttede}} beskyttelsesindstillinger fra $4 til $3",
+       "logentry-protect-protect": "$1 {{GENDER:$2|beskyttede}} $3 $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|beskyttede}} $3 $4 [kaskaderende]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|ændrede}} beskyttelsesniveau for $3 $4",
+       "logentry-protect-modify-cascade": "$1 {{GENDER:$2|ændrede}} beskyttelsesniveau for $3 $4 [kaskaderende]",
        "logentry-rights-rights": "$1 {{GENDER:$2|ændrede}} gruppemedlemskabet for $3 fra $4 til $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|ændrede}} gruppemedlemskabet for $3",
        "logentry-rights-autopromote": "$1 blev automatisk {{GENDER:$2|forfremmet}} fra $4 til $5",
        "special-characters-title-minus": "minustegn",
        "mw-widgets-dateinput-placeholder-day": "ÅÅÅÅ-MM-DD",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
-       "api-error-blacklisted": "Vælg venligst en anden, beskrivende titel."
+       "api-error-blacklisted": "Vælg venligst en anden, beskrivende titel.",
+       "randomrootpage": "Tilfældig stamside"
 }
index 050d208..d8336ae 100644 (file)
@@ -84,7 +84,8 @@
                        "J. 'mach' wust",
                        "R4c0r",
                        "MGChecker",
-                       "FriedhelmW"
+                       "FriedhelmW",
+                       "Schniggendiller"
                ]
        },
        "tog-underline": "Links unterstreichen:",
        "october-date": "$1. Oktober",
        "november-date": "$1. November",
        "december-date": "$1. Dezember",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategorie|Kategorien}}",
        "category_header": "Seiten in der Kategorie „$1“",
        "subcategories": "Unterkategorien",
        "virus-scanfailed": "Scan fehlgeschlagen (Code $1)",
        "virus-unknownscanner": "Unbekannter Virenscanner:",
        "logouttext": "<strong>Du bist nun abgemeldet.</strong>\n\nBeachte, dass einige Seiten noch anzeigen können, dass du angemeldet bist, solange du nicht deinen Browsercache geleert hast.",
+       "cannotlogoutnow-title": "Abmeldung nicht erfolgreich",
+       "cannotlogoutnow-text": "Eine Abmeldung ist mit Verwendung von $1 nicht möglich.",
        "welcomeuser": "Willkommen, $1!",
        "welcomecreation-msg": "Dein Benutzerkonto wurde erstellt.\nVergiss nicht, deine [[Special:Preferences|{{SITENAME}}-Einstellungen]] zu ändern.",
        "yourname": "Benutzername:",
        "remembermypassword": "Mit diesem Browser dauerhaft angemeldet bleiben (maximal $1 {{PLURAL:$1|Tag|Tage}})",
        "userlogin-remembermypassword": "Angemeldet bleiben",
        "userlogin-signwithsecure": "Sichere Verbindung verwenden",
+       "cannotloginnow-title": "Anmeldung nicht erfolgreich",
+       "cannotloginnow-text": "Eine Anmeldung ist mit Verwendung von $1 nicht möglich.",
        "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.",
        "resetpass_submit": "Passwort übermitteln und anmelden",
        "changepassword-success": "Dein Passwort wurde erfolgreich geändert!",
        "changepassword-throttled": "Du hast kürzlich zu viele Anmeldeversuche unternommen.\nBitte warte $1, bevor du es erneut versuchst.",
+       "botpasswords": "Botpasswörter",
+       "botpasswords-summary": "<em>Botpasswörter</em> erlauben Zugriff auf ein Benutzerkonto über die API, ohne die Hauptanmeldeinformationen des Benutzerkontos zu verwenden. Die verfügbaren Benutzerrechte bei der Anmeldung mit einem Botpasswort können beschränkt sein.\n\nWenn du nicht weist, warum du dies tun möchtest, solltest du dies wahrscheinlich nicht tun. Niemand soll dich jemals bitten, ein Passwort zu erzeugen und es an ihn zu übergeben.",
+       "botpasswords-disabled": "Botpasswörter sind deaktiviert.",
+       "botpasswords-no-central-id": "Um Botpasswörter zu verwenden, musst du bei einem zentralisierten Benutzerkonto angemeldet sein.",
+       "botpasswords-existing": "Vorhandene Botpasswörter",
+       "botpasswords-createnew": "Ein neues Botpasswort erstellen",
+       "botpasswords-editexisting": "Ein vorhandenes Botpasswort bearbeiten",
+       "botpasswords-label-appid": "Name des Bots:",
+       "botpasswords-label-create": "Erstellen",
+       "botpasswords-label-update": "Aktualisieren",
+       "botpasswords-label-cancel": "Abbrechen",
+       "botpasswords-label-delete": "Löschen",
+       "botpasswords-label-resetpassword": "Passwort zurücksetzen",
+       "botpasswords-label-grants": "Anwendbare Berechtigungen:",
+       "botpasswords-help-grants": "Jede Berechtigung gibt Zugriff auf gelistete Benutzerrechte, die ein Benutzerkonto bereits hat. Siehe die [[Special:ListGrants|Tabelle]] für weitere Informationen.",
+       "botpasswords-label-restrictions": "Verwendungsbeschränkungen:",
+       "botpasswords-label-grants-column": "Gewährt",
+       "botpasswords-bad-appid": "Der Botname „$1“ ist nicht gültig.",
+       "botpasswords-insert-failed": "Der Botname „$1“ konnte nicht hinzugefügt werden. Wurde er bereits hinzugefügt?",
+       "botpasswords-update-failed": "Der Botname „$1“ konnte nicht aktualisiert werden. Wurde er gelöscht?",
+       "botpasswords-created-title": "Botpasswort erstellt",
+       "botpasswords-created-body": "Das Botpasswort „$1“ wurde erfolgreich erstellt.",
+       "botpasswords-updated-title": "Botpasswort aktualisiert",
+       "botpasswords-updated-body": "Das Botpasswort „$1“ wurde erfolgreich aktualisiert.",
+       "botpasswords-deleted-title": "Botpasswort gelöscht",
+       "botpasswords-deleted-body": "Das Botpasswort „$1“ wurde gelöscht.",
+       "botpasswords-newpassword": "Das neue Passwort zur Anmeldung mit <strong>$1</strong> ist <strong>$2</strong>. <em>Bitte halte dies für die Zukunft fest.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider ist nicht verfügbar.",
+       "botpasswords-restriction-failed": "Beschränkungen des Botpassworts verhindern diese Anmeldung.",
+       "botpasswords-invalid-name": "Der angegebene Benutzername enthält keinen Botpassworttrenner („$1“).",
+       "botpasswords-not-exist": "Der Benutzer „$1“ hat kein Botpasswort mit dem Namen „$2“.",
        "resetpass_forbidden": "Das Passwort kann nicht geändert werden.",
        "resetpass-no-info": "Du musst dich anmelden, um auf diese Seite direkt zuzugreifen.",
        "resetpass-submit-loggedin": "Passwort ändern",
        "passwordreset-emailtext-ip": "Jemand mit der IP-Adresse $1, wahrscheinlich du selbst, hat eine Zurücksetzung deines Passworts bei {{SITENAME}} angefordert ($4). {{PLURAL:$3|Das folgende Benutzerkonto ist|Die folgenden Benutzerkonten sind}} mit dieser E-Mail-Adresse verknüpft:\n\n$2\n\n{{PLURAL:$3|Dieses temporäre Passwort läuft|Diese temporären Passwörter laufen}} innerhalb von {{PLURAL:$5|einem Tag|$5 Tagen}} ab.\nDu solltest dich anmelden und ein neues Passwort vergeben. Falls jemand anderes diese Anfrage getätigt hat oder du dich wieder an dein ursprüngliches Passwort erinnern kannst und es nicht länger ändern möchtest, kannst du diese Nachricht ignorieren und weiterhin dein altes Passwort benutzen.",
        "passwordreset-emailtext-user": "Benutzer $1 bei {{SITENAME}} hat eine Zurücksetzung deines Passworts bei {{SITENAME}} angefordert ($4). {{PLURAL:$3|Das folgende Benutzerkonto ist|Die folgenden Benutzerkonten sind}} mit dieser E-Mail-Adresse verknüpft:\n\n$2\n\n{{PLURAL:$3|Dieses temporäre Passwort läuft|Diese temporären Passwörter laufen}} innerhalb von {{PLURAL:$5|einem Tag|$5 Tagen}} ab. Du solltest dich anmelden und ein neues Passwort vergeben. Falls jemand anderes diese Anfrage getätigt hat oder du dich wieder an dein ursprüngliches Passwort erinnern kannst und es nicht ändern möchtest, kannst du diese Nachricht ignorieren und weiterhin dein altes Passwort benutzen.",
        "passwordreset-emailelement": "Benutzername: \n$1\n\nTemporäres Passwort: \n$2",
-       "passwordreset-emailsentemail": "Falls dies eine registrierte E-Mail-Adresse für dein Benutzerkonto ist, wird eine Passwortzurücksetzungs-E-Mail an diese Adresse versandt.",
-       "passwordreset-emailsentusername": "Falls es eine dazugehörige registrierte E-Mail-Adresse gibt, wird eine Passwort-Zurücksetzungs-E-Mail versandt.",
+       "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",
        "changeemail": "E-Mail-Adresse ändern oder entfernen",
        "userrights": "Benutzerrechte verwalten",
        "userrights-lookup-user": "Gruppenzugehörigkeit verwalten",
        "userrights-user-editname": "Benutzername:",
-       "editusergroup": "Benutzerrechte bearbeiten",
+       "editusergroup": "{{GENDER:$1|Benutzerrechte}} bearbeiten",
        "editinguser": "Ändere Benutzerrechte {{GENDER:$1|des Benutzers|der Benutzerin}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Benutzer-Gruppenzugehörigkeit bearbeiten",
-       "saveusergroups": "Gruppenzugehörigkeit ändern",
+       "saveusergroups": "{{GENDER:$1|Gruppenzugehörigkeit}} ändern",
        "userrights-groupsmember": "Mitglied von:",
        "userrights-groupsmember-auto": "Automatisch Mitglied von:",
        "userrights-groupsmember-type": "$2",
        "right-createpage": "Seiten erstellen (die keine Diskussionsseiten sind)",
        "right-createtalk": "Diskussionsseiten erstellen",
        "right-createaccount": "Benutzerkonto erstellen",
+       "right-autocreateaccount": "Automatische Anmeldung mit einem externen Benutzerkonto",
        "right-minoredit": "Bearbeitungen als klein markieren",
        "right-move": "Seiten verschieben",
        "right-move-subpages": "Seiten inklusive Unterseiten verschieben",
        "right-blockemail": "Benutzer am Versenden von E-Mails hindern",
        "right-hideuser": "Benutzernamen sperren und verbergen",
        "right-ipblock-exempt": "Ausnahme von IP-Sperren, automatischen Sperren und Rangesperren",
-       "right-proxyunbannable": "Ausnahme von automatischen Proxysperren",
        "right-unblockself": "Sich entsperren",
        "right-protect": "Seitenschutzstatus ändern und kaskadengeschützte Seiten bearbeiten",
        "right-editprotected": "Seiten bearbeiten, die als „{{int:protect-level-sysop}}“ geschützt sind",
        "right-managechangetags": "[[Special:Tags|Markierungen]] erstellen und aus der Datenbank löschen",
        "right-applychangetags": "[[Special:Tags|Markierungen]] zusammen mit den Änderungen anwenden",
        "right-changetags": "Beliebige [[Special:Tags|Markierungen]] zu einzelnen Versionen und Logbucheinträgen hinzufügen und entfernen",
+       "grant-generic": "Rechtegruppe „$1“",
+       "grant-group-page-interaction": "Mit Seiten interagieren",
+       "grant-group-file-interaction": "Mit Medien interagieren",
+       "grant-group-watchlist-interaction": "Mit deiner Beobachtungsliste interagieren",
+       "grant-group-email": "E-Mail versenden",
+       "grant-group-high-volume": "Massenaktivitäten ausführen",
+       "grant-group-customization": "Anpassung und Einstellungen",
+       "grant-group-administration": "Administrative Aktionen ausführen",
+       "grant-group-other": "Verschiedene Aktivitäten",
+       "grant-blockusers": "Benutzer sperren und freigeben",
+       "grant-createaccount": "Benutzerkonten erstellen",
+       "grant-createeditmovepage": "Seiten erstellen, bearbeiten und verschieben",
+       "grant-delete": "Seiten, Versionen und Logbucheinträge löschen",
+       "grant-editinterface": "MediaWiki-Namensraum und Benutzer-CSS/JavaScript bearbeiten",
+       "grant-editmycssjs": "Dein Benutzer-CSS/JavaScript bearbeiten",
+       "grant-editmyoptions": "Deine Benutzereinstellungen bearbeiten",
+       "grant-editmywatchlist": "Deine Beobachtungsliste bearbeiten",
+       "grant-editpage": "Vorhandene Seiten bearbeiten",
+       "grant-editprotected": "Geschützte Seiten bearbeiten",
+       "grant-highvolume": "Massenbearbeitungen",
+       "grant-oversight": "Benutzer verstecken und Versionen unterdrücken",
+       "grant-patrol": "Änderungen an Seiten kontrollieren",
+       "grant-protect": "Seiten schützen und freigeben",
+       "grant-rollback": "Änderungen an Seiten zurücksetzen",
+       "grant-sendemail": "E-Mails an andere Benutzer versenden",
+       "grant-uploadeditmovefile": "Dateien hochladen, ersetzen und verschieben",
+       "grant-uploadfile": "Neue Dateien hochladen",
+       "grant-basic": "Basisrechte",
+       "grant-viewdeleted": "Gelöschte Dateien und Seiten ansehen",
+       "grant-viewmywatchlist": "Deine Beobachtungsliste ansehen",
        "newuserlogpage": "Neuanmeldungs-Logbuch",
        "newuserlogpagetext": "Dies ist ein Logbuch der neu erstellten Benutzerkonten.",
        "rightslog": "Rechte-Logbuch",
        "action-createpage": "Seiten zu erstellen",
        "action-createtalk": "Diskussionsseiten zu erstellen",
        "action-createaccount": "ein Benutzerkonto zu erstellen",
+       "action-autocreateaccount": "automatisch dieses externe Benutzerkonto zu erstellen",
        "action-history": "die Versionsgeschichte dieser Seite anzusehen",
        "action-minoredit": "diese Bearbeitung als klein zu markieren",
        "action-move": "die Seite zu verschieben",
        "upload-form-label-select-file": "Datei auswählen",
        "upload-form-label-infoform-title": "Einzelheiten",
        "upload-form-label-infoform-name": "Name",
+       "upload-form-label-infoform-name-tooltip": "Ein eindeutiger erklärender Titel für die Datei, die als Dateiname angeboten wird. Du musst reine Sprache mit Leerzeichen verwenden. Nicht die Dateierweiterung einschließen.",
        "upload-form-label-infoform-description": "Beschreibung",
+       "upload-form-label-infoform-description-tooltip": "Beschreibe kurz alles bedeutende über das Werk.\nErwähne für ein Foto die abgebildeten hauptsächlichen Dinge, das Ereignis oder den Ort.",
        "upload-form-label-usage-title": "Verwendung",
        "upload-form-label-usage-filename": "Dateiname",
        "foreign-structured-upload-form-label-own-work": "Dies ist mein eigenes Werk",
        "log-title-wildcard": "Titel beginnt mit …",
        "showhideselectedlogentries": "Ausgewählte Logbucheinträge anzeigen/verstecken",
        "log-edit-tags": "Markierungen ausgewählter Logbucheinträge bearbeiten",
+       "checkbox-select": "Auswählen: $1",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Keine",
+       "checkbox-invert": "Umkehren",
        "allpages": "Alle Seiten",
        "nextpage": "Nächste Seite ($1)",
        "prevpage": "Vorherige Seite ($1)",
        "listgrouprights-namespaceprotection-header": "Namensraumbeschränkungen",
        "listgrouprights-namespaceprotection-namespace": "Namensraum",
        "listgrouprights-namespaceprotection-restrictedto": "Rechte, die dem Benutzer die Bearbeitung erlauben",
+       "listgrants": "Berechtigungen",
+       "listgrants-summary": "Es folgt eine Liste mit Berechtigungen mit ihrem verknüpften Zugriff auf Benutzerrechte. Benutzer können Anwendungen autorisieren, um ihr Benutzerkonto zu verwenden, aber mit beschränkten Berechtigungen basierend auf den Rechten, die der Benutzer der Anwendung gegeben hat. Eine Anwendung agiert im Namen eines Benutzers, die keine Rechte verwenden kann, die der Benutzer nicht hat.\nEs gibt [[{{MediaWiki:Listgrouprights-helppage}}|zusätzliche Informationen]] über einzelne Rechte.",
+       "listgrants-grant": "Berechtigung",
+       "listgrants-rights": "Rechte",
        "trackingcategories": "Tracking-Kategorien",
        "trackingcategories-summary": "Diese Seite listet Tracking-Kategorien auf, die von der MediaWiki-Software automatisch gefüllt werden. Ihre Namen können durch Änderung der entsprechenden Systemnachrichten im {{ns:8}}-Namensraum angepasst werden.",
        "trackingcategories-msg": "Tracking-Kategorie",
        "wlshowhideanons": "Anonyme Benutzer",
        "wlshowhidepatr": "Kontrollierte Bearbeitungen",
        "wlshowhidemine": "Meine Bearbeitungen",
+       "wlshowhidecategorization": "Seitenkategorisierung",
        "watchlist-options": "Anzeigeoptionen",
        "watching": "Beobachten …",
        "unwatching": "Nicht mehr beobachten …",
        "protect-otherreason-op": "Anderer Grund",
        "protect-dropdown": "* Allgemeine Schutzgründe\n** Edit-War\n** Wiederkehrender Vandalismus\n** Wiederholtes Einstellen von Werbung\n** Häufig eingebundene Vorlage\n** Seite mit hoher Besucherzahl",
        "protect-edit-reasonlist": "Schutzgründe bearbeiten",
-       "protect-expiry-options": "1 Stunde:1 hour,1 Tag:1 day,1 Woche:1 week,2 Wochen:2 weeks,1 Monat:1 month,3 Monaten:3 months,6 Monaten:6 months,1 Jahr:1 year,unbeschränkt:infinite",
+       "protect-expiry-options": "1 Stunde:1 hour,1 Tag:1 day,1 Woche:1 week,2 Wochen:2 weeks,1 Monat:1 month,3 Monate:3 months,6 Monate:6 months,1 Jahr:1 year,unbeschränkt:infinite",
        "restriction-type": "Schutzstatus:",
        "restriction-level": "Schutzhöhe:",
        "minimum-size": "Mindestgröße",
        "unblock": "Benutzer freigeben",
        "blockip": "IP-Adresse/{{GENDER:$1|Benutzer|Benutzerin}} sperren",
        "blockip-legend": "IP-Adresse/Benutzer sperren",
-       "blockiptext": "Mit diesem Formular sperrst du eine IP-Adresse oder einen Benutzernamen, so dass von dort keine Änderungen mehr vorgenommen werden können.\nDies sollte nur erfolgen, um Vandalismus zu verhindern und in Übereinstimmung mit den [[{{MediaWiki:Policy-url}}|Richtlinien]].\nBitte gib den Grund für die Sperre an.",
+       "blockiptext": "Mit diesem Formular sperrst du eine IP-Adresse oder einen Benutzernamen, so dass von dort keine Änderungen mehr vorgenommen werden können.\nDies sollte nur erfolgen, um Vandalismus zu verhindern und in Übereinstimmung mit den [[{{MediaWiki:Policy-url}}|Richtlinien]].\nBitte gib den Grund für die Sperre an.\nDu kannst IP-Bereiche mit der [https://de.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-Syntax sperren; der größte erlaubte Bereich ist /$1 für IPv4 und /$2 für IPv6.",
        "ipaddressorusername": "IP-Adresse oder Benutzername:",
        "ipbexpiry": "Sperrdauer:",
        "ipbreason": "Grund:",
        "block-log-flags-hiddenname": "Benutzername versteckt",
        "range_block_disabled": "Die Möglichkeit, ganze Adressräume zu sperren, ist nicht aktiviert.",
        "ipb_expiry_invalid": "Die eingegebene Dauer ist ungültig.",
+       "ipb_expiry_old": "Der Zeitpunkt des Ablaufs liegt in der Vergangenheit.",
        "ipb_expiry_temp": "Benutzernamens-Sperren mit der Verstecken-Option müssen permanent sein.",
        "ipb_hide_invalid": "Dieses Konto kann nicht unterdrückt werden, da es mehr als {{PLURAL:$1|eine Bearbeitung|$1 Bearbeitungen}} aufweist.",
        "ipb_already_blocked": "„$1“ ist bereits gesperrt",
        "lockedbyandtime": "(von $1 am $2 um $3 Uhr)",
        "move-page": "Verschieben von „$1“",
        "move-page-legend": "Seite verschieben",
-       "movepagetext": "Mit untenstehendem Formular kannst du eine Seite umbenennen, indem du sie mitsamt allen Versionen auf einen neuen Titel verschiebst.\nDer alte Titel wird danach zum neuen weiterleiten.\nDu kannst Weiterleitungen, die auf den Originaltitel verlinken, automatisch korrigieren lassen.\nStelle sicher, dass du im Anschluss alle [[Special:DoubleRedirects|doppelten]] oder [[Special:BrokenRedirects|defekten Weiterleitungen]] überprüfst.\nDu bist dafür verantwortlich, dass Links weiterhin auf das korrekte Ziel verweisen.\n\nDie Seite wird '''nicht''' verschoben, sofern es bereits eine Seite mit dem vorgesehenen Titel gibt, es sei denn, letztere ist eine Weiterleitung ohne Versionsgeschichte.\nDies bedeutet, dass du die Umbenennung rückgängig machen kannst, sofern du einen Fehler gemacht hast. Du kannst hingegen keine existierende Seite überschreiben.\n\n'''Warnung!'''\nDie Verschiebung kann weitreichende und unerwartete Folgen für häufig besuchte Seiten haben.\nDu solltest daher die Konsequenzen verstanden haben, bevor du jetzt fortfährst.",
-       "movepagetext-noredirectfixer": "Mit untenstehendem Formular kannst du eine Seite umbenennen, indem du sie mitsamt allen Versionen auf einen neuen Titel verschiebst.\nDer alte Titel wird danach zum neuen weiterleiten.\nStelle sicher, dass du im Anschluss alle [[Special:DoubleRedirects|doppelten]] oder [[Special:BrokenRedirects|defekten Weiterleitungen]] überprüfst.\nDu bist dafür verantwortlich, dass Links weiterhin auf das korrekte Ziel verweisen.\n\nDie Seite wird '''nicht''' verschoben, sofern es bereits eine Seite mit dem vorgesehenen Titel gibt, es sei denn, diese ist eine Weiterleitung ohne Versionsgeschichte.\nDies bedeutet, dass du die Umbenennung rückgängig machen kannst, sofern du einen Fehler gemacht hast. Du kannst hingegen keine existierende Seite überschreiben.\n\n'''Warnung!'''\nDie Verschiebung kann weitreichende und unerwartete Folgen für häufig besuchte Seiten haben.\nDu solltest daher die Konsequenzen verstanden haben, bevor du jetzt fortfährst.",
+       "movepagetext": "Mit untenstehendem Formular kannst du eine Seite umbenennen, indem du sie mitsamt allen Versionen auf einen neuen Titel verschiebst.\nDer alte Titel wird danach zum neuen weiterleiten.\nDu kannst Weiterleitungen, die auf den Originaltitel verlinken, automatisch korrigieren lassen.\nStelle sicher, dass du im Anschluss alle [[Special:DoubleRedirects|doppelten]] oder [[Special:BrokenRedirects|defekten Weiterleitungen]] überprüfst.\nDu bist dafür verantwortlich, dass Links weiterhin auf das korrekte Ziel verweisen.\n\nDie Seite wird <strong>nicht</strong> verschoben, sofern es bereits eine Seite mit dem vorgesehenen Titel gibt, es sei denn, letztere ist eine Weiterleitung ohne Versionsgeschichte.\nDies bedeutet, dass du die Umbenennung rückgängig machen kannst, sofern du einen Fehler gemacht hast. Du kannst hingegen keine existierende Seite überschreiben.\n\n<strong>Hinweis:</strong>\nDie Verschiebung kann weitreichende und unerwartete Folgen für häufig besuchte Seiten haben.\nDu solltest daher die Konsequenzen verstanden haben, bevor du jetzt fortfährst.",
+       "movepagetext-noredirectfixer": "Mit untenstehendem Formular kannst du eine Seite umbenennen, indem du sie mitsamt allen Versionen auf einen neuen Titel verschiebst.\nDer alte Titel wird danach zum neuen weiterleiten.\nStelle sicher, dass du im Anschluss alle [[Special:DoubleRedirects|doppelten]] oder [[Special:BrokenRedirects|defekten Weiterleitungen]] überprüfst.\nDu bist dafür verantwortlich, dass Links weiterhin auf das korrekte Ziel verweisen.\n\nDie Seite wird <strong>nicht</strong> verschoben, sofern es bereits eine Seite mit dem vorgesehenen Titel gibt, es sei denn, diese ist eine Weiterleitung ohne Versionsgeschichte.\nDies bedeutet, dass du die Umbenennung rückgängig machen kannst, sofern du einen Fehler gemacht hast. Du kannst hingegen keine existierende Seite überschreiben.\n\n<strong>Hinweis:</strong>\nDie Verschiebung kann weitreichende und unerwartete Folgen für häufig besuchte Seiten haben.\nDu solltest daher die Konsequenzen verstanden haben, bevor du jetzt fortfährst.",
        "movepagetalktext": "Falls du dieses Kästchen aktivierst, wird die dazugehörige Diskussionsseite automatisch auf den neuen Titel verschoben, sofern nicht bereits eine nicht-leere Diskussionsseite dort vorhanden ist.\n\nIn diesem Fall musst du die Seite manuell verschieben oder zusammenführen, falls erforderlich.",
        "moveuserpage-warning": "'''Warnung:''' Du bist dabei, eine Benutzerseite zu verschieben. Bitte bedenke, dass dadurch nur die Benutzerseite verschoben, '''nicht''' aber der Benutzer umbenannt wird.",
        "movecategorypage-warning": "<strong>Warnung:</strong> Du bist gerade dabei, eine Kategorieseite zu verschieben. Bitte sei dir bewusst, dass nur die Seite verschoben wird. Alle Seiten in der alten Kategorie werden <em>nicht</em> neu kategorisiert.",
        "movenosubpage": "Diese Seite hat keine Unterseiten.",
        "movereason": "Grund:",
        "revertmove": "zurück verschieben",
-       "delete_and_move_text": "== Löschung erforderlich ==\n\nDie Seite „[[:$1]]“ existiert bereits. Möchtest du diese löschen, um die Seite verschieben zu können?",
+       "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",
        "selfmove": "Ursprungs- und Zielname sind gleich.\nEine Seite kann nicht auf sich selbst verschoben werden.",
        "move-leave-redirect": "Weiterleitung erstellen",
        "protectedpagemovewarning": "'''Warnung:''' Diese Seite wurde so geschützt, dass sie nur von Benutzern mit Administratorenrechten verschoben werden kann.\nZur Information folgt der aktuelle Logbucheintrag:",
        "semiprotectedpagemovewarning": "'''Hinweis:''' Diese Seite wurde so geschützt, dass sie nur von angemeldeten Benutzern verschoben werden kann.\nZur Information folgt der aktuelle Logbucheintrag:",
-       "move-over-sharedrepo": "==Datei existiert==\n[[:$1]] existiert in einem gemeinsam genutzten Repositorium. Das Verschieben einer Datei zu diesem Titel überschreibt die gemeinsam genutzte Datei.",
+       "move-over-sharedrepo": "[[:$1]] existiert in einem gemeinsam genutzten Repositorium. Das Verschieben einer Datei zu diesem Titel überschreibt die gemeinsam genutzte Datei.",
        "file-exists-sharedrepo": "Der gewählte Dateiname wird bereits in einem gemeinsam genutzten Repositorium verwendet.\nBitte wähle einen anderen Namen.",
        "export": "Seiten exportieren",
        "exporttext": "Mit dieser Spezialseite kannst du den Text inklusive der Versionsgeschichte einzelner Seiten in eine XML-Datei exportieren.\nDie Datei kann in ein anderes MediaWiki-Wiki über die [[Special:Import|Importfunktion]] eingespielt werden.\n\nTrage den oder die entsprechenden Seitentitel in das folgende Textfeld ein (pro Zeile jeweils nur eine Seite).\n\nAlternativ ist der Export auch mit der Syntax [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] möglich, beispielsweise für die [[{{MediaWiki:Mainpage}}]].",
        "export-download": "Als XML-Datei speichern",
        "export-templates": "Inklusive Vorlagen",
        "export-pagelinks": "Verlinkte Seiten automatisch mit exportieren, bis zur Rekursionstiefe von:",
+       "export-manual": "Seiten manuell hinzufügen:",
        "allmessages": "MediaWiki-Systemnachrichten",
        "allmessagesname": "Name",
        "allmessagesdefault": "Standardtext",
        "javascripttest-pagetext-frameworks": "Bitte wähle eine der folgenden Prüfumgebungen aus: $1",
        "javascripttest-pagetext-skins": "Wähle eine Benutzeroberfläche zur Durchführung der Tests aus:",
        "javascripttest-qunit-intro": "Siehe die [$1 Dokumentation zu Tests] auf mediawiki.org",
-       "tooltip-pt-userpage": "Deine Benutzerseite",
+       "tooltip-pt-userpage": "{{GENDER:|Deine}} Benutzerseite",
        "tooltip-pt-anonuserpage": "Benutzerseite der IP-Adresse von der aus du Änderungen durchführst",
-       "tooltip-pt-mytalk": "Deine Diskussionsseite",
+       "tooltip-pt-mytalk": "{{GENDER:|Deine}} Diskussionsseite",
        "tooltip-pt-anontalk": "Diskussion über Änderungen von dieser IP-Adresse",
-       "tooltip-pt-preferences": "Deine Einstellungen",
+       "tooltip-pt-preferences": "{{GENDER:|Deine}} Einstellungen",
        "tooltip-pt-watchlist": "Liste der von dir beobachteten Seiten",
-       "tooltip-pt-mycontris": "Liste eigener Beiträge",
+       "tooltip-pt-mycontris": "Liste {{GENDER:|eigener}} Beiträge",
        "tooltip-pt-anoncontribs": "Eine Liste der Bearbeitungen, die von dieser IP-Adresse gemacht wurden",
        "tooltip-pt-login": "Sich anzumelden wird gerne gesehen, ist jedoch nicht zwingend erforderlich.",
        "tooltip-pt-logout": "Abmelden",
        "tooltip-t-recentchangeslinked": "Letzte Änderungen an Seiten, die von hier verlinkt sind",
        "tooltip-feed-rss": "RSS-Feed dieser Seite",
        "tooltip-feed-atom": "Atom-Feed dieser Seite",
-       "tooltip-t-contributions": "Liste der Beiträge dieses Benutzers ansehen",
-       "tooltip-t-emailuser": "Eine E-Mail an diesen Benutzer senden",
+       "tooltip-t-contributions": "Liste der Beiträge {{GENDER:$1|dieses Benutzers|dieser Benutzerin}} ansehen",
+       "tooltip-t-emailuser": "Eine E-Mail an {{GENDER:$1|diesen Benutzer|diese Benutzerin}} senden",
        "tooltip-t-info": "Weitere Informationen über diese Seite",
        "tooltip-t-upload": "Dateien hochladen",
        "tooltip-t-specialpages": "Liste aller Spezialseiten",
        "pageinfo-category-files": "Anzahl der Dateien",
        "markaspatrolleddiff": "Als kontrolliert markieren",
        "markaspatrolledtext": "Diese Seite als kontrolliert markieren",
+       "markaspatrolledtext-file": "Diese Dateiversion als kontrolliert markieren",
        "markedaspatrolled": "Als kontrolliert markiert",
        "markedaspatrolledtext": "Die ausgewählte Version von [[:$1]] wurde als kontrolliert markiert.",
        "rcpatroldisabled": "Kontrolle der letzten Änderungen gesperrt",
        "newimages-summary": "Diese Spezialseite zeigt die zuletzt hochgeladenen Dateien an.",
        "newimages-legend": "Filter",
        "newimages-label": "Dateiname (oder ein Teil davon):",
-       "newimages-showbots": "Uploads von Bots anzeigen",
+       "newimages-showbots": "Von Bots hochgeladene Dateien anzeigen",
+       "newimages-hidepatrolled": "Kontrollierte Dateien ausblenden",
        "noimages": "Keine Dateien gefunden.",
        "ilsubmit": "Suchen",
        "bydate": "nach Datum",
        "scarytranscludefailed-httpstatus": "[Vorlagenabruf fehlgeschlagen für $1: HTTP $2]",
        "scarytranscludetoolong": "[URL ist zu lang]",
        "deletedwhileediting": "Achtung: Diese Seite wurde gelöscht, nachdem du angefangen hast, sie zu bearbeiten!\nIm [{{fullurl:{{#special:Log}}|type=delete&page={{FULLPAGENAMEE}}}} Lösch-Logbuch] findest du den Grund für die Löschung. Wenn du die Seite speicherst, wird sie neu angelegt.",
-       "confirmrecreate": "Benutzer [[User:$1|$1]] ([[User talk:$1|Diskussion]]) hat diese Seite gelöscht, nachdem du angefangen hast, sie zu bearbeiten. Die Begründung lautete:\n:''$2''\nBitte bestätige, dass du diese Seite wirklich neu erstellen möchten.",
-       "confirmrecreate-noreason": "Benutzer [[User:$1|$1 ]] ([[User talk:$1|Diskussion]]) hat diese Seite gelöscht, nachdem du mit der Bearbeitung begonnen hast. Bitte bestätige, dass du die Seite wirklich erneut erstellen möchtest.",
+       "confirmrecreate": "{{GENDER:$1|Der Benutzer|Die Benutzerin}} [[User:$1|$1]] ([[User talk:$1|Diskussion]]) hat diese Seite gelöscht, nachdem du angefangen hast, sie zu bearbeiten. Die Begründung lautete:\n: <em>$2</em>\nBitte bestätige, dass du diese Seite wirklich neu erstellen möchtest.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Der Benutzer|Die Benutzerin}} [[User:$1|$1]] ([[User talk:$1|Diskussion]]) hat diese Seite gelöscht, nachdem du mit der Bearbeitung begonnen hast. Bitte bestätige, dass du die Seite wirklich erneut erstellen möchtest.",
        "recreate": "Neu erstellen",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Diese Seite aus dem Server-Cache löschen?",
        "hijri-calendar-m11": "Dhu l-qaʿda",
        "hijri-calendar-m12": "Dhu l-hiddscha",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Diskussion]])",
+       "timezone-local": "Lokal",
        "duplicate-defaultsort": "Achtung: Der Sortierungsschlüssel „$2“ überschreibt den vorher verwendeten Schlüssel „$1“.",
        "duplicate-displaytitle": "<strong>Warnung:</strong> Der Anzeigetitel „$2“ überschreibt den früheren Anzeigetitel „$1“.",
        "invalid-indicator-name": "<strong>Fehler:</strong> Das Attribut <code>name</code> des Seitenstatusindikators darf nicht leer sein.",
        "version-libraries-license": "Lizenz",
        "version-libraries-description": "Beschreibung",
        "version-libraries-authors": "Autoren",
-       "redirect": "Weiterleitung auf Benutzerseite, Seite, Seitenversion oder Datei",
-       "redirect-legend": "Weiterleitung auf eine Benutzerseite, Seite, Seitenversion oder Datei",
-       "redirect-summary": "Diese Spezialseite leitet auf eine Benutzerseite (numerische Benutzerkennung angegeben), Seite (Seitenkennung angegeben), Seitenversion (Versionskennung angegeben) oder Datei (Dateiname angegeben) weiter. Benutzung: [[{{#Special:Redirect}}/user/101]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] oder [[{{#Special:Redirect}}/file/Example.jpg]].",
+       "redirect": "Weiterleitung auf Benutzerseite, Seite, Seitenversion, Datei oder Logbucheintrag",
+       "redirect-legend": "Weiterleitung auf eine Benutzerseite, Seite, Seitenversion, Datei oder einen Logbucheintrag",
+       "redirect-summary": "Diese Spezialseite leitet auf eine Benutzerseite (numerische Benutzerkennung angegeben), Seite (Seitenkennung angegeben), Seitenversion (Versionskennung angegeben), Datei (Dateiname angegeben) oder einen Logbucheintrag (Logbuchkennung angegeben) weiter. Benutzung: [[{{#Special:Redirect}}/user/101]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/file/Example.jpg]] oder [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Los",
        "redirect-lookup": "Suchen:",
        "redirect-value": "Kennung oder Dateiname:",
        "redirect-page": "Seite",
        "redirect-revision": "Seitenversion",
        "redirect-file": "Datei",
+       "redirect-logid": "Logbucheintrag",
        "redirect-not-exists": "Der Wert wurde nicht gefunden",
        "fileduplicatesearch": "Dateiduplikatsuche",
        "fileduplicatesearch-summary": "Suche nach Dateiduplikaten auf Basis ihres Hashwertes.",
        "expand_templates_preview": "Vorschau",
        "expand_templates_preview_fail_html": "<em>Da {{SITENAME}} rohes HTML aktiviert hat und es einen Verlust deiner Sitzungsdaten gab, ist die Vorschau als Vorsichtsmaßnahme gegen JavaScript-Angriffe versteckt.</em>\n\n<strong>Falls dies ein zulässiger Vorschauversuch ist, versuche es bitte erneut.</strong>\nFalls dieses Problem weiterhin bestehen bleibt, versuche dich [[Special:UserLogout|abzumelden]] und erneut anzumelden.",
        "expand_templates_preview_fail_html_anon": "<em>Da {{SITENAME}} rohes HTML aktiviert hat und du nicht angemeldet bist, ist die Vorschau als Vorsichtsmaßnahme gegen JavaScript-Angriffe versteckt.</em>\n\n<strong>Falls dies ein zulässiger Vorschauversuch ist, [[Special:UserLogin|melde dich bitte an]] und versuche es erneut.</strong>",
+       "expand_templates_input_missing": "Du musst mindestens einen Eingabetext angeben.",
        "pagelanguage": "Seitensprachenauswahl",
        "pagelang-name": "Seite",
        "pagelang-language": "Sprache",
        "pagelang-use-default": "Standardsprache verwenden",
        "pagelang-select-lang": "Sprache auswählen",
+       "pagelang-submit": "Übermitteln",
        "right-pagelang": "Seitensprache ändern",
        "action-pagelang": "die Seitensprache zu ändern",
        "log-name-pagelang": "Sprachenänderungs-Logbuch",
        "mediastatistics-summary": "Statistiken über hochgeladene Dateitypen. Dies beinhaltet nur die aktuellste Version einer Datei. Alte oder gelöschte Dateiversionen sind ausgeschlossen.",
        "mediastatistics-nfiles": "$1 ($2 %)",
        "mediastatistics-nbytes": "{{PLURAL:$1|Ein Byte|$1 Bytes}} ($2; $3 %)",
+       "mediastatistics-bytespertype": "Gesamte Dateigröße für diesen Abschnitt: {{PLURAL:$1|Ein Byte|$1 Bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Gesamte Dateigröße für alle Dateien: {{PLURAL:$1|Ein Byte|$1 Bytes}} ($2).",
        "mediastatistics-table-mimetype": "MIME-Typ",
        "mediastatistics-table-extensions": "Mögliche Erweiterungen",
        "mediastatistics-table-count": "Anzahl der Dateien",
        "mediastatistics-header-text": "Text",
        "mediastatistics-header-executable": "Ausführbare Dateien",
        "mediastatistics-header-archive": "Komprimierte Formate",
+       "mediastatistics-header-total": "Alle Dateien",
        "json-warn-trailing-comma": "{{PLURAL:$1|Ein anhängendes Komma wurde|$1 anhängende Kommas wurden}} aus JSON entfernt",
        "json-error-unknown": "Es gab ein Problem mit dem JSON. Fehler: $1",
        "json-error-depth": "Die maximale Stapeltiefe wurde überschritten",
        "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."
+       "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",
+       "sessionprovider-nocookies": "Cookies sind eventuell deaktiviert. Stelle sicher, dass Cookies aktiviert sind und versuche es erneut.",
+       "randomrootpage": "Zufällige Stammseite"
 }
index 08e9c69..f2370d6 100644 (file)
        "disclaimers": "Redê mesuliyeti",
        "disclaimerpage": "Project:Reddê mesuliyetê bıngey",
        "edithelp": "Peştdariya vurnayışi",
-       "helppage-top-gethelp": "Desteg",
+       "helppage-top-gethelp": "Peşti",
        "mainpage": "Pela Seri",
        "mainpage-description": "Pela seri",
        "policy-url": "Project:Terzê hereketi",
        "content-model-text": "metno pan",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-json-empty-object": "Çiyo veng",
+       "content-json-empty-array": "Rêza venge",
        "expensive-parserfunction-warning": "Hişyari: No pel de fonksiyoni zaf esti.\n\nNo $2 daweti ra gani tay bıbo, na hel {{PLURAL:$1|1 dawet esto|$1 dawet esto}}.",
        "expensive-parserfunction-category": "Pelê ke tede zaf fonksiyoni esti",
        "post-expand-template-inclusion-warning": "Tembe: zerreyê şabloni zaf gırdo.\nTaye şabloni zerre pel de nêmociyayeni.",
        "intentionallyblankpage": "Ena pel bi zanayişî weng mendo.",
        "external_image_whitelist": "  #no satır zey xo verde/raverde<pre>\n#parçeyê ifadeya rêzbiyayeyani (têna zerreyê ıney de // ) u çıtayo/çiyo zi mende cêr de têare kerê.\n#ney URL ya (hotlink) resmê teberi de hemcıta benî.\n#Ê yê ke hemcıt (eşleşmek-hemçift) biyê zey resımi asenî, eqsê hal de zi zey gıreyê resmi aseno.\nsatır ê ke pê ney # # destpêkenê zey mışore/mıjore muamele vineno.\n#herfa gırd û qıci ferq nêkeno\n\n#parçeyê ifadeya rêzbiyayeyani bıerzê serê ney satıri. no satır zey xo verde/raverde </pre>",
        "tags": "Etiketê vurnayîşê raştî",
-       "tag-filter": "Avrêcê [[Special:Tags|Etiketi]]:",
+       "tag-filter": "Parzûnê [[Special:Tags|etiketi]]:",
        "tag-filter-submit": "Avrêc",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|Etiket|Etiketi}}]]: $2)",
        "tags-title": "Etiketan",
index 11403b3..6db5b77 100644 (file)
        "october-date": "$1 Οκτωβρίου",
        "november-date": "$1 Νοεμβρίου",
        "december-date": "$1 Δεκεμβρίου",
+       "period-am": "ΠΜ",
+       "period-pm": "ΜΜ",
        "pagecategories": "{{PLURAL:$1|Κατηγορία|Κατηγορίες}}",
        "category_header": "Σελίδες στην κατηγορία «$1»",
        "subcategories": "Υποκατηγορίες",
        "databaseerror-query": "Ερώτημα: $1",
        "databaseerror-function": "Λειτουργία: $1",
        "databaseerror-error": "Σφάλμα: $1",
-       "transaction-duration-limit-exceeded": "ΠÏ\81οκειμένοÏ\85 Î½Î± Î±Ï\80οÏ\86εÏ\85Ï\87θεί Î· Î´Î·Î¼Î¹Î¿Ï\85Ï\81γία Ï\85Ï\88ηλήÏ\82 Î±Î½Î±Ï\80αÏ\81αγÏ\89γήÏ\82 Î¿Ï\84δ, Î· Ï\83Ï\85ναλλαγή Î±Ï\85Ï\84ή Î¼Î±Ï\84αιÏ\8eθηκε, ÎµÏ\80ειδή Î· Î´Î¹Î¬Ï\81κεια ÎµÎ¹Ï\83αγÏ\89γήÏ\82 ÎºÎµÎ¹Î¼Î­Î½Î¿Ï\85 ($1) Î¾ÎµÏ\80έÏ\81αÏ\83ε Ï\84ο $2 Î´ÎµÏ\8dÏ\84εÏ\81ο Ï\8cÏ\81ιο.\nÎ\91ν Î¸Î­Î»ÎµÏ\84ε Î½Î± Î±Î»Î»Î¬Î¾ÎµÏ\84ε Ï\80ολλά Ï\83Ï\84οιÏ\87εία Ï\84αÏ\85Ï\84Ï\8cÏ\87Ï\81ονα, Ï\80Ï\81οÏ\83Ï\80αθήÏ\83Ï\84ε Î½Î± ÎºÎ¬Î½ÎµÏ\84ε Ï\80ολλαÏ\80λέÏ\82 Î¼Î¹ÎºÏ\81Ï\8cÏ\84εÏ\81εÏ\82 ÎµÏ\80ιÏ\87ειÏ\81ήÏ\83εις αντ ' αυτού.",
+       "transaction-duration-limit-exceeded": "Î\93ια Î½Î± Î±Ï\80οÏ\86εÏ\85Ï\87θεί Î· Î´Î·Î¼Î¹Î¿Ï\85Ï\81γία Î¼ÎµÎ³Î¬Î»Î¿Ï\85 ÎºÎµÎ½Î¿Ï\8d Î±Î½Î±Ï\80αÏ\81αγÏ\89γήÏ\82, Î· ÎµÎ½Î­Ï\81γεια Î±Ï\85Ï\84ή Î¼Î±Ï\84αιÏ\8eθηκε, ÎµÏ\80ειδή Î· Î´Î¹Î¬Ï\81κεια ÎµÎ¹Ï\83αγÏ\89γήÏ\82 ÎºÎµÎ¹Î¼Î­Î½Î¿Ï\85 ($1) Î¾ÎµÏ\80έÏ\81αÏ\83ε Ï\84ο $2 {{PLURAL:$2|δεÏ\8dÏ\84εÏ\81ο Ï\8cÏ\81ιο|δεÏ\8dÏ\84εÏ\81α Ï\8cÏ\81ια}}.\nÎ\91ν Î¸Î­Î»ÎµÏ\84ε Î½Î± Î±Î»Î»Î¬Î¾ÎµÏ\84ε Ï\80ολλά Ï\83Ï\84οιÏ\87εία Ï\84αÏ\85Ï\84Ï\8cÏ\87Ï\81ονα, Ï\80Ï\81οÏ\83Ï\80αθήÏ\83Ï\84ε Î½Î± ÎºÎ¬Î½ÎµÏ\84ε Ï\80ολλαÏ\80λέÏ\82 Î¼Î¹ÎºÏ\81Ï\8cÏ\84εÏ\81εÏ\82 Î±Ï\80Ï\8cÏ\80ειÏ\81ες αντ ' αυτού.",
        "laggedslavemode": "'''Προειδοποίηση:''' Η σελίδα μπορεί να μην περιέχει πρόσφατες ενημερώσεις.",
        "readonly": "Κλειδωμένη βάση δεδομένων",
        "enterlockreason": "Εισαγάγετε μια αιτία για το κλείδωμα και μια εκτίμησή για το πότε το κλείδωμα αυτό θα αρθεί",
        "passwordreset-emailtext-ip": "Κάποιος (πιθανώς εσείς, από την διεύθυνση IP $1) ζήτησε την επαναφορά του κωδικού σας σε {{SITENAME}} ($4).  {{PLURAL:$3|Ο ακόλουθος λογαριασμός|Οι ακόλουθοι λογαριασμοί}} χρήστη συνδέονται με αυτή τη διεύθυνση e-mail:\n\n$2\n\n{{PLURAL:$3|Αυτός ο προσωρινός κωδικός πρόσβασης θα λήξει|Αυτοί οι προσωρινοί κωδικοί πρόσβασης θα λήξουν}} σε {{PLURAL:$5|μία ημέρα|$5 ημέρες}}.\nΘα πρέπει να συνδεθείτε τώρα και να επιλέξετε ένα νέο κωδικό. Αν κάποιος άλλος έκανε αυτό το αίτημα ή αν έχετε θυμηθεί τον αρχικό κωδικό πρόσβασής σας, και δεν επιθυμείτε πια να τον αλλάξετε, μπορείτε να αγνοήσετε αυτό το μήνυμα και να συνεχίσετε να χρησιμοποιείτε τον παλιό σας κωδικό πρόσβασης.",
        "passwordreset-emailtext-user": "Ο χρήστης $1 στη {{SITENAME}} ζήτησε μια επαναφορά του κωδικού πρόσβασης σας σε {{SITENAME}} ($4). {{PLURAL:$3|Ο ακόλουθος λογαριασμός|Οι ακόλουθοι λογαριασμοί}} χρήστη συνδέονται με αυτή τη διεύθυνση e-mail:\n\n$2\n\n{{PLURAL:$3|Αυτός ο προσωρινός κωδικός πρόσβασης θα λήξει| Αυτοί οι προσωρινοί κωδικοί πρόσβασης θα λήξουν}} σε {{PLURAL:$5| μία ημέρα| $5 ημέρες}}.\nΘα πρέπει να συνδεθείτε τώρα και να επιλέξετε ένα νέο κωδικό. Αν κάποιος άλλος έκανε αυτό το αίτημα ή αν έχετε θυμηθεί τον αρχικό κωδικό πρόσβασής σας, και δεν επιθυμείτε πια να τον αλλάξετε, μπορείτε να αγνοήσετε αυτό το μήνυμα και να συνεχίσετε να χρησιμοποιείτε τον παλιό σας κωδικό πρόσβασης.",
        "passwordreset-emailelement": "Όνομα χρήστη: \n$1\n\nΠροσωρινός κωδικός πρόσβασης:\n$2",
-       "passwordreset-emailsentemail": "Αν αυτό είναι καταχωρημένη διεύθυνση ηλεκτρονικού ταχυδρομείου για το λογαριασμό σας και, στη συνέχεια,  θα σας αποσταλεί μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
-       "passwordreset-emailsentusername": "Î\91ν Ï\85Ï\80άÏ\81Ï\87ει ÎºÎ±Ï\84αÏ\87Ï\89Ï\81ημένη Î¼Î¹Î± Î±Î½Ï\84ίÏ\83Ï\84οιÏ\87η  Î´Î¹ÎµÏ\8dθÏ\85νÏ\83η Î·Î»ÎµÎºÏ\84Ï\81ονικοÏ\8d Ï\84αÏ\87Ï\85δÏ\81ομείοÏ\85 Ï\83αÏ\82, τότε θα σας αποσταλεί ένα μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
+       "passwordreset-emailsentemail": "Αν αυτή η διεύθυνση ηλεκτρονικού ταχυδρομείου συνδέεται με το  λογαριασμό σας, τότε  θα σας αποσταλεί μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
+       "passwordreset-emailsentusername": "Î\91ν Ï\85Ï\80άÏ\81Ï\87ει Î¼Î¹Î± Î´Î¹ÎµÏ\8dθÏ\85νÏ\83η Î·Î»ÎµÎºÏ\84Ï\81ονικοÏ\8d Ï\84αÏ\87Ï\85δÏ\81ομείοÏ\85 Ï\80οÏ\85 Ï\83Ï\85νδέεÏ\84αι Î¼Îµ Î±Ï\85Ï\84Ï\8c Ï\84ο Ï\8cνομα Ï\87Ï\81ήÏ\83Ï\84η, τότε θα σας αποσταλεί ένα μήνυμα ηλεκτρονικού ταχυδρομείου για την επαναφορά του κωδικού πρόσβασης.",
        "passwordreset-emailsent-capture": "Έχει αποσταλεί email επαναφοράς κωδικού, το οποίο φαίνεται πιο κάτω.",
        "passwordreset-emailerror-capture": "Ένα email επαναφοράς κωδικού έχει δημιουργηθεί, το οποίο φαίνεται πιο κάτω, αλλά απέτυχε η αποστολή του στο  {{GENDER:$2|χρήστη}}: $1",
        "changeemail": "Αλλαγή ή αφαίρεση της διεύθυνσης ηλεκτρονικού ταχυδρομείου",
        "right-blockemail": "Φραγή ενός χρήστη από την αποστολή ηλεκτρονικών μηνυμάτων",
        "right-hideuser": "Φραγή ενός ονόματος χρήστη, αποκρύπτοντάς το από το κοινό",
        "right-ipblock-exempt": "Παράκαμψη φραγών σε διευθύνσεις IP, αυτόματων φραγών και φραγών σε IP range",
-       "right-proxyunbannable": "Παράκαμψη αυτόματων φραγών σε proxies",
        "right-unblockself": "Αναίρεση φραγής του εαυτού",
        "right-protect": "Αλλαγή των επιπέδων προστασίας και επεξεργασία προστατευμένων σελίδων",
        "right-editprotected": "Επεξεργασία προστατευμένων σελίδων (χωρίς διαδοχική προστασία)",
        "foreign-structured-upload-form-label-not-own-work-message-default": "Εάν δεν είστε σε θέση να ανεβάσετε αυτό το αρχείο στο πλαίσιο των πολιτικών της shared repository, παρακαλώ κλείστε αυτό το παράθυρο διαλόγου και να επιχειρήσετε μια άλλη μέθοδος.",
        "foreign-structured-upload-form-label-not-own-work-local-default": "Επίσης, μπορεί να θέλετε να δοκιμάσετε χρησιμοποιώντας το [[Special:Upload|τη σελίδα ανεβάσματος για το {{SITENAME}}]], αν αυτό το αρχείο μπορεί να φορτωθεί κάτω σύμφωνα με τις πολιτικές τους.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Δηλώνω ότι κατέχω τα πνευματικά δικαιώματα για αυτό το αρχείο, και συμφωνώ αμετάκλητα στην απελευθέρωση  αυτού του  αρχείου στο Wikimedia Commons με άδεια  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], και συμφωνώ με την [https://wikimediafoundation.org/wiki/Terms_of_Use Όρους Χρήσης].",
-       "foreign-structured-upload-form-label-not-own-work-message-shared": "Αν δεν κατέχει τα πνευματικά δικαιώματα για αυτό το αρχείο, ή επιθυμείτε να το δημοσιεύσετε υπό μια διαφορετική άδεια χρήσης, μπορείτε να χρησιμοποιήσετε τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγό Ανεβάσματος των Κοινών].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Αν δεν κατέχει τα πνευματικά δικαιώματα για αυτό το αρχείο, ή επιθυμείτε να το δημοσιεύσετε υπό μια διαφορετική άδεια χρήσης, μπορείτε να χρησιμοποιήσετε τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγό Ανεβάσματος των Wikimedia Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Επίσης, μπορεί να θέλετε να δοκιμάσετε να χρησιμοποιήσετε  το [[Special:Upload|τη σελίδα ανεβάσματος για το {{SITENAME}}]], αν αυτό το αρχείο μπορεί να φορτωθεί σύμφωνα με  τις πολιτικές τους.",
        "foreign-structured-upload-form-2-label-intro": "Σας ευχαριστούμε για τη δωρεά μιας εικόνας που θα χρησιμοποιηθεί στο {{SITENAME}}. Θα πρέπει να συνεχίσετε  μόνο εφόσον πληροί μια σειρά  προϋποθέσεων:",
        "foreign-structured-upload-form-2-label-ownwork": "Πρέπει να είναι εξ ολοκλήρου <strong>δική σας δημιουργία</strong>, όχι απλά παρμένο από το Internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Î\94εν Ï\80Ï\81έÏ\80ει Î½Î±  Ï\80εÏ\81ιέÏ\87οÏ\85ν <strong>κανένα Î­Ï\81γο Î±Ï\80Ï\8c Î¿Ï\80οιονδήÏ\80οÏ\84ε Î¬Î»Î»Î¿Î½</strong>, Î® ÎµÎ¼Ï\80νέονÏ\84αι Î±Ï\80' Î±Ï\85Ï\84οÏ\8dÏ\82",
+       "foreign-structured-upload-form-2-label-noderiv": "Î\94εν Ï\80Ï\81έÏ\80ει Î½Î±  Ï\80εÏ\81ιέÏ\87ει <strong>κανένα Î­Ï\81γο Î±Ï\80Ï\8c Î¿Ï\80οιονδήÏ\80οÏ\84ε Î¬Î»Î»Î¿Î½</strong>, Î® Î¼Îµ Î­Î¼Ï\80νεÏ\85Ï\83η Î±Ï\80Ï\8c Î±Î»Î»Î¿Ï\8d",
        "foreign-structured-upload-form-2-label-useful": "Θα πρέπει να είναι <strong>εκπαιδευτικό και χρήσιμο</strong> για διδασκαλία άλλων",
        "foreign-structured-upload-form-2-label-ccbysa": "Πρέπει να είναι <strong>ΕΝΤΑΞΕΙ για δημοσίευση για πάντα</strong> στο Διαδίκτυο υπό τους όρους της [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] άδειας",
-       "foreign-structured-upload-form-2-label-alternative": "Εάν όλα τα παραπάνω δεν είναι αλήθεια, μπορείτε ακόμα να είστε σε θέση να ανεβάσετε αυτό το αρχείο χρησιμοποιώντας τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγό Ανεβάσματος] στα Κοινά, αρκεί να είναι διαθέσιμο υπό μια ελεύθερη άδεια χρήσης.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Î\9cε Ï\84ο Î±Î½Î­Î²Î±Ï\83μα Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85, ÎµÏ\80ιβεβαιÏ\8eνεÏ\84ε Ï\8cÏ\84ι Î­Ï\87εÏ\84ε Ï\84α Ï\80νεÏ\85μαÏ\84ικά Î´Î¹ÎºÎ±Î¹Ï\8eμαÏ\84α Î³Î¹Î± Î±Ï\85Ï\84Ï\8c Ï\84ο Î±Ï\81Ï\87είο, ÎºÎ±Î¹ Ï\83Ï\85μÏ\86Ï\89νείÏ\84ε Î±Î¼ÎµÏ\84άκληÏ\84α Î³Î¹Î± Ï\84ην Î±Ï\80ελεÏ\85θέÏ\81Ï\89Ï\83η Î±Ï\85Ï\84οÏ\8d Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85 Ï\83Ï\84α Î\9aοινά Ï\84οÏ\85  Wikimedia υπό την άδεια Creative Commons Attribution-ShareAlike 4.0, και συμφωνείτε με τους [https://wikimediafoundation.org/wiki/Terms_of_Use Όρους Χρήσης].",
+       "foreign-structured-upload-form-2-label-alternative": "Εάν όλα τα παραπάνω δεν είναι αλήθεια, μπορείτε ακόμα να είστε σε θέση να ανεβάσετε αυτό το αρχείο χρησιμοποιώντας τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγό Ανεβάσματος] στα Wikimedia Commons, αρκεί να είναι διαθέσιμο υπό μια ελεύθερη άδεια χρήσης.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Î\9cε Ï\84ο Î±Î½Î­Î²Î±Ï\83μα Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85, ÎµÏ\80ιβεβαιÏ\8eνεÏ\84ε Ï\8cÏ\84ι Î­Ï\87εÏ\84ε Ï\84α Ï\80νεÏ\85μαÏ\84ικά Î´Î¹ÎºÎ±Î¹Ï\8eμαÏ\84α Î³Î¹Î± Î±Ï\85Ï\84Ï\8c Ï\84ο Î±Ï\81Ï\87είο, ÎºÎ±Î¹ Ï\83Ï\85μÏ\86Ï\89νείÏ\84ε Î±Î¼ÎµÏ\84άκληÏ\84α Î³Î¹Î± Ï\84ην Î´Î·Î¼Î¿Ï\83ίεÏ\85Ï\83η Î±Ï\85Ï\84οÏ\8d Ï\84οÏ\85 Î±Ï\81Ï\87είοÏ\85 Ï\83Ï\84α Wikimedia Commons υπό την άδεια Creative Commons Attribution-ShareAlike 4.0, και συμφωνείτε με τους [https://wikimediafoundation.org/wiki/Terms_of_Use Όρους Χρήσης].",
        "foreign-structured-upload-form-3-label-question-website": "Μήπως κατεβάσατε αυτή την εικόνα από μια ιστοσελίδα, ή την πήραμε μετά από αναζήτηση εικόνων;",
        "foreign-structured-upload-form-3-label-question-ownwork": "Δημιουργήσατε αυτή την εικόνα (τραβήξατε φωτογραφία, κάνατε ένα σκίτσο κ.τ.λ.) μόνος σας;",
        "foreign-structured-upload-form-3-label-question-noderiv": "Περιέχει, ή είναι εμπνευσμένο από έργο που ανήκει σε κάποιον άλλο, όπως ένα λογότυπο;",
        "foreign-structured-upload-form-3-label-yes": "Ναι",
        "foreign-structured-upload-form-3-label-no": "Όχι",
-       "foreign-structured-upload-form-3-label-alternative": "Δυστυχώς, σε αυτή την περίπτωση, αυτό το εργαλείο δεν υποστηρίζει το ανέβασμα αυτού του αρχείου. Μπορείτε ακόμα να είστε  σε θέση να το ανεβάσετε χρησιμοποιώντας τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγός Ανεβάσματος] στα Κοινά, αρκεί να είναι διαθέσιμο υπό μια ελεύθερη άδεια χρήσης.",
+       "foreign-structured-upload-form-3-label-alternative": "Δυστυχώς, σε αυτή την περίπτωση, αυτό το εργαλείο δεν υποστηρίζει το ανέβασμα αυτού του αρχείου. Μπορείτε ακόμα να είστε  σε θέση να το ανεβάσετε χρησιμοποιώντας τον [https://commons.wikimedia.org/wiki/Special:UploadWizard Οδηγός Ανεβάσματος] στα Wikimedia Commons, αρκεί να είναι διαθέσιμο υπό μια ελεύθερη άδεια χρήσης.",
        "foreign-structured-upload-form-4-label-good": "Χρησιμοποιώντας αυτό το εργαλείο, μπορείτε να ανεβάσετε εκπαιδευτικά διαγράμματα που έχετε δημιουργήσει και φωτογραφίες, που δεν περιέχουν έργο που ανήκει σε κάποιον άλλο.",
        "foreign-structured-upload-form-4-label-bad": "Δεν μπορείτε να ανεβάσετε εικόνες που βρέθηκαν σε μια μηχανή αναζήτησης ή που έχετε κατεβάσει από άλλες ιστοσελίδες.",
        "backend-fail-stream": "Αδύνατη η μετάδοση του αρχείου $1.",
        "wlshowhideanons": "ανώνυμοι χρήστες",
        "wlshowhidepatr": "επιτηρούμενες επεξεργασίες",
        "wlshowhidemine": "οι επεξεργασίες μου",
+       "wlshowhidecategorization": "κατηγοριοποίηση σελίδας",
        "watchlist-options": "Επιλογές λίστας παρακολούθησης",
        "watching": "Παρακολούθηση...",
        "unwatching": "Μη παρακολούθηση...",
        "unblock": "Κατάργηση αποκλεισμού χρήστη",
        "blockip": "Φραγή {{GENDER:$1|χρήστη|χρήστριας}}",
        "blockip-legend": "Φραγή του χρήστη",
-       "blockiptext": "ΧÏ\81ηÏ\83ιμοÏ\80οιήÏ\83Ï\84ε Ï\84ην Ï\80αÏ\81ακάÏ\84Ï\89 Ï\86Ï\8cÏ\81μα Î³Î¹Î± Î½Î± ÎµÎ¼Ï\80οδίÏ\83εÏ\84ε Ï\80αÏ\81εμβάÏ\83ειÏ\82 Ï\83Ï\84ο ÎºÎµÎ¯Î¼ÎµÎ½Î¿ Î±Ï\80Ï\8c Î¼Î¹Î± Ï\83Ï\85γκεκÏ\81ιμένη Î´Î¹ÎµÏ\8dθÏ\85νÏ\83η IP Î® Ï\8cνομα Ï\87Ï\81ήÏ\83Ï\84η.\nΤο Î¼Î­Ï\84Ï\81ο Î±Ï\85Ï\84Ï\8c Ï\80Ï\81έÏ\80ει Î½Î± Î»Î±Î¼Î²Î¬Î½ÎµÏ\84αι Î¼Ï\8cνο Ï\83ε Ï\80εÏ\81ιÏ\80Ï\84Ï\8eÏ\83ειÏ\82 Î²Î±Î½Î´Î±Î»Î¹Ï\83μοÏ\8d Ï\83ελίδÏ\89ν ÎºÎ±Î¹ Ï\80άνÏ\84α Ï\83Ï\8dμÏ\86Ï\89να Î¼Îµ Ï\84ην [[{{MediaWiki:Policy-url}}|Ï\80ολιÏ\84ική]].\nΠαÏ\81ακαλοÏ\8dμε Î½Î± Î±Î¹Ï\84ιολογήÏ\83εÏ\84ε Ï\84ην ÎµÎ½Î­Ï\81γειά Ï\83αÏ\82 (Ï\80αÏ\81αÏ\80έμÏ\80ονÏ\84αÏ\82 Ï\80\87. Ï\83ε Ï\83Ï\85γκεκÏ\81ιμένεÏ\82 Ï\83ελίδεÏ\82 Ï\80οÏ\85 Ï\85Ï\80έÏ\83Ï\84ηÏ\83αν Î²Î±Î½Î´Î±Î»Î¹Ï\83μÏ\8c).",
+       "blockiptext": "ΧÏ\81ηÏ\83ιμοÏ\80οιήÏ\83Ï\84ε Ï\84ην Ï\80αÏ\81ακάÏ\84Ï\89 Ï\86Ï\8cÏ\81μα Î³Î¹Î± Î½Î± ÎµÎ¼Ï\80οδίÏ\83εÏ\84ε Ï\84ην Ï\80Ï\81Ï\8cÏ\83βαÏ\83η Ï\83Ï\84ο ÎºÎµÎ¯Î¼ÎµÎ½Î¿ Î±Ï\80Ï\8c Î¼Î¹Î± Ï\83Ï\85γκεκÏ\81ιμένη Î´Î¹ÎµÏ\8dθÏ\85νÏ\83η IP Î® Ï\8cνομα Ï\87Ï\81ήÏ\83Ï\84η.\nΤο Î¼Î­Ï\84Ï\81ο Î±Ï\85Ï\84Ï\8c Ï\80Ï\81έÏ\80ει Î½Î± Î»Î±Î¼Î²Î¬Î½ÎµÏ\84αι Î¼Ï\8cνο Ï\83ε Ï\80εÏ\81ιÏ\80Ï\84Ï\8eÏ\83ειÏ\82 Î²Î±Î½Î´Î±Î»Î¹Ï\83μοÏ\8d Ï\83ελίδÏ\89ν ÎºÎ±Î¹ Ï\80άνÏ\84α Ï\83Ï\8dμÏ\86Ï\89να Î¼Îµ Ï\84ην [[{{MediaWiki:Policy-url}}|Ï\80ολιÏ\84ική]].\nΠαÏ\81ακαλοÏ\8dμε Î½Î± Î±Î¹Ï\84ιολογήÏ\83εÏ\84ε Ï\84ην ÎµÎ½Î­Ï\81γειά Ï\83αÏ\82 (Ï\80αÏ\81αÏ\80έμÏ\80ονÏ\84αÏ\82 Ï\80\87. Ï\83ε Ï\83Ï\85γκεκÏ\81ιμένεÏ\82 Ï\83ελίδεÏ\82 Ï\80οÏ\85 Ï\85Ï\80έÏ\83Ï\84ηÏ\83αν Î²Î±Î½Î´Î±Î»Î¹Ï\83μÏ\8c).\n\nÎ\9cÏ\80οÏ\81είÏ\84ε Î½Î± Î¼Ï\80λοκάÏ\81εÏ\84ε IP ranges Ï\87Ï\81ηÏ\83ιμοÏ\80οιÏ\8eνÏ\84αÏ\82 Ï\84ο Ï\83Ï\85νÏ\84ακÏ\84ικÏ\8c [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]. Î¤Î¿ Î¼Î­Î³Î¹Ï\83Ï\84ο ÎµÏ\80ιÏ\84Ï\81εÏ\80Ï\8cμενο ÎµÏ\8dÏ\81οÏ\82 ÎµÎ¯Î½Î±Î¹ /$1 Î³Î¹Î± IPv4 ÎºÎ±Î¹ /$2 Î³Î¹Î± IPv6.",
        "ipaddressorusername": "Διεύθυνση IP ή όνομα χρήστη",
        "ipbexpiry": "Λήξη",
        "ipbreason": "Αιτία:",
        "export-download": "Αποθήκευση ως αρχείο",
        "export-templates": "Συμπερίληψη προτύπων",
        "export-pagelinks": "Συμπερίληψη συνδεδεμένων σελίδων σε ένα βάθος:",
+       "export-manual": "Προσθέστε σελίδες χειροκίνητα:",
        "allmessages": "Μηνύματα συστήματος",
        "allmessagesname": "Όνομα",
        "allmessagesdefault": "Προεπιλεγμένο κείμενο μηνύματος",
        "pageinfo-category-files": "Αριθμός αρχείων",
        "markaspatrolleddiff": "Να σημειωθεί 'υπό παρακολούθηση'",
        "markaspatrolledtext": "Σήμανση αυτής της σελίδας ως ελεγμένης",
+       "markaspatrolledtext-file": "Επισημάνετε αυτή τη έκδοση του αρχείου ως ελεγμένη",
        "markedaspatrolled": "Σημειωμένο ως 'υπό παρακολούθηση'",
        "markedaspatrolledtext": "Η επιλεγμένη αναθεώρηση της [[:$1]] έχει σημειωθεί ως ελεγμένη.",
        "rcpatroldisabled": "Η λειτουργία 'Παρακολούθηση Πρόσφατων Αλλαγών' έχει απενεργοποιηθεί.",
        "watchlisttools-edit": "Προβολή και επεξεργασία λίστας παρακολούθησης",
        "watchlisttools-raw": "Επεξεργασία πρωτογενούς λίστας παρακολούθησης",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|συζήτηση]])",
+       "timezone-local": "Τοπικό",
        "duplicate-defaultsort": "'''Προειδοποίηση:''' Το προεπιλεγμένο κλειδί ταξινόμησης «$2» υπερισχύει του προηγούμενου προεπιλεγμένου κλειδιού «$1».",
        "duplicate-displaytitle": "<strong>Προειδοποίηση:</strong> Ο εμφανιζόμενος τίτλος «$2» παρακάμπτει τον προηγούμενο «$1».",
        "invalid-indicator-name": "<strong>Σφάλμα:</strong> Η ιδιότητα <code>name</code> των δεικτών κατάστασης σελίδων δεν πρέπει να είναι κενή.",
        "tags-update-add-not-allowed-one": "Η ετικέτα «$1» δεν επιτρέπεται να προστεθεί με το χέρι.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Η ακόλουθη ετικέτα δεν επιτρέπεται να προστεθεί|Οι ακόλουθες ετικέτες δεν επιτρέπεται να προστεθούν}} με το χέρι: $1",
        "tags-update-remove-not-allowed-one": "Η ετικέτα «$1» δεν επιτρέπεται να αφαιρεθεί.",
+       "tags-update-remove-not-allowed-multi": "{{PLURAL:$2|Η ακόλουθη ετικέτα|Οι ακόλουθες ετικέτες}} δεν επιτρέπεται να {{PLURAL:$2|μετακινηθεί|μετακινηθούν}} χειροκίνητα: $1",
        "tags-edit-title": "Επεξεργασία ετικετών",
        "tags-edit-manage-link": "Διαχείριση ετικετών",
+       "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 καταχωρίσεις}} του αρχείου καταγραφής",
        "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 byte|$1 bytes}} ($2, $3%)",
+       "mediastatistics-bytespertype": "Συνολικό μέγεθος του αρχείων για αυτή την ενότητα: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Το συνολικό μέγεθος αρχείων για όλα τα αρχεία: {{PLURAL:$1|$1 byte|$1 bytes}} ($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": "Το μέγιστο βάθος στοίβας έχει ξεπεραστεί",
index 3f761f6..2849963 100644 (file)
        "october-date": "October $1",
        "november-date": "November $1",
        "december-date": "December $1",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Category|Categories}}",
        "pagecategorieslink": "Special:Categories",
        "category_header": "Pages in category \"$1\"",
        "unprotectthispage": "Change protection of this page",
        "newpage": "New page",
        "talkpage": "Discuss this page",
-       "talkpagelinktext": "Talk",
+       "talkpagelinktext": "talk",
        "specialpage": "Special page",
        "personaltools": "Personal tools",
        "addsection": "+",
        "virus-scanfailed": "scan failed (code $1)",
        "virus-unknownscanner": "unknown antivirus:",
        "logouttext": "<strong>You are now logged out.</strong>\n\nNote that some pages may continue to be displayed as if you were still logged in, until you clear your browser cache.",
+       "cannotlogoutnow-title": "Cannot log out now",
+       "cannotlogoutnow-text": "Logging out is not possible when using $1.",
        "welcomeuser": "Welcome, $1!",
        "welcomecreation-msg": "Your account has been created.\nYou can change your {{SITENAME}} [[Special:Preferences|preferences]] if you wish.",
        "yourname": "Username:",
        "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",
+       "cannotloginnow-title": "Cannot log in now",
+       "cannotloginnow-text": "Logging in is not possible when using $1.",
        "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.",
        "resetpass_submit": "Set password and log in",
        "changepassword-success": "Your password has been changed successfully!",
        "changepassword-throttled": "You have made too many recent login attempts.\nPlease wait $1 before trying again.",
+       "botpasswords": "Bot passwords",
+       "botpasswords-summary": "<em>Bot passwords</em> allow access to a user account via the API without using the account's main login credentials. The user rights available when logged in with a bot password may be restricted.\n\nIf you don't know why you might want to do this, you should probably not do it. No one should ever ask you to generate one of these and give it to them.",
+       "botpasswords-disabled": "Bot passwords are disabled.",
+       "botpasswords-no-central-id": "To use bot passwords, you must be logged in to a centralized account.",
+       "botpasswords-existing": "Existing bot passwords",
+       "botpasswords-createnew": "Create a new bot password",
+       "botpasswords-editexisting": "Edit an existing bot password",
+       "botpasswords-label-appid": "Bot name:",
+       "botpasswords-label-create": "Create",
+       "botpasswords-label-update": "Update",
+       "botpasswords-label-cancel": "Cancel",
+       "botpasswords-label-delete": "Delete",
+       "botpasswords-label-resetpassword": "Reset the password",
+       "botpasswords-label-grants": "Applicable grants:",
+       "botpasswords-help-grants": "Each grant gives access to listed user rights that a user account already has. See the [[Special:ListGrants|table of grants]] for more information.",
+       "botpasswords-label-restrictions": "Usage restrictions:",
+       "botpasswords-label-grants-column": "Granted",
+       "botpasswords-bad-appid": "The bot name \"$1\" is not valid.",
+       "botpasswords-insert-failed": "Failed to add bot name \"$1\". Was it already added?",
+       "botpasswords-update-failed": "Failed to update bot name \"$1\". Was it deleted?",
+       "botpasswords-created-title": "Bot password created",
+       "botpasswords-created-body": "The bot password \"$1\" was created successfully.",
+       "botpasswords-updated-title": "Bot password updated",
+       "botpasswords-updated-body": "The bot password \"$1\" was updated successfully.",
+       "botpasswords-deleted-title": "Bot password deleted",
+       "botpasswords-deleted-body": "The bot password \"$1\" was deleted.",
+       "botpasswords-newpassword": "The new password to log in with <strong>$1</strong> is <strong>$2</strong>. <em>Please record this for future reference.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider is not available.",
+       "botpasswords-restriction-failed": "Bot password restrictions prevent this login.",
+       "botpasswords-invalid-name": "The username specified does not contain the bot password separator (\"$1\").",
+       "botpasswords-not-exist": "User \"$1\" does not have a bot password named \"$2\".",
        "resetpass_forbidden": "Passwords cannot be changed",
        "resetpass-no-info": "You must be logged in to access this page directly.",
        "resetpass-submit-loggedin": "Change password",
        "passwordreset-emailtext-ip": "Someone (probably you, from IP address $1) requested a reset of your\npassword for {{SITENAME}} ($4). The following user {{PLURAL:$3|account is|accounts are}}\nassociated with this email address:\n\n$2\n\n{{PLURAL:$3|This temporary password|These temporary passwords}} will expire in {{PLURAL:$5|one day|$5 days}}.\nYou should log in and choose a new password now. If someone else made this\nrequest, or if you have remembered your original password, and you no longer\nwish to change it, you may ignore this message and continue using your old\npassword.",
        "passwordreset-emailtext-user": "User $1 on {{SITENAME}} requested a reset of your password for {{SITENAME}}\n($4). The following user {{PLURAL:$3|account is|accounts are}} associated with this email address:\n\n$2\n\n{{PLURAL:$3|This temporary password|These temporary passwords}} will expire in {{PLURAL:$5|one day|$5 days}}.\nYou should log in and choose a new password now. If someone else made this\nrequest, or if you have remembered your original password, and you no longer\nwish to change it, you may ignore this message and continue using your old\npassword.",
        "passwordreset-emailelement": "Username:\n$1\n\nTemporary password:\n$2",
-       "passwordreset-emailsentemail": "If this is a registered email address for your account, then a password reset email will be sent.",
-       "passwordreset-emailsentusername": "If there is a corresponding registered email address, then a password reset email will be sent.",
+       "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",
        "changeemail": "Change or remove email address",
        "userrights-summary": "",
        "userrights-lookup-user": "Manage user groups",
        "userrights-user-editname": "Enter a username:",
-       "editusergroup": "Edit user groups",
+       "editusergroup": "Edit {{GENDER:$1|user}} groups",
        "editinguser": "Changing user rights of {{GENDER:$1|user}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Edit user groups",
-       "saveusergroups": "Save user groups",
+       "saveusergroups": "Save {{GENDER:$1|user}} groups",
        "userrights-groupsmember": "Member of:",
        "userrights-groupsmember-auto": "Implicit member of:",
        "userrights-groupsmember-type": "$1",
        "right-createpage": "Create pages (which are not discussion pages)",
        "right-createtalk": "Create discussion pages",
        "right-createaccount": "Create new user accounts",
+       "right-autocreateaccount": "Automatically log in with an external user account",
        "right-minoredit": "Mark edits as minor",
        "right-move": "Move pages",
        "right-move-subpages": "Move pages with their subpages",
        "right-blockemail": "Block a user from sending email",
        "right-hideuser": "Block a username, hiding it from the public",
        "right-ipblock-exempt": "Bypass IP blocks, auto-blocks and range blocks",
-       "right-proxyunbannable": "Bypass automatic blocks of proxies",
        "right-unblockself": "Unblock oneself",
        "right-protect": "Change protection levels and edit cascade-protected pages",
        "right-editprotected": "Edit pages protected as \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Create and delete [[Special:Tags|tags]] from the database",
        "right-applychangetags": "Apply [[Special:Tags|tags]] along with one's changes",
        "right-changetags": "Add and remove arbitrary [[Special:Tags|tags]] on individual revisions and log entries",
+       "grant-generic": "\"$1\" rights bundle",
+       "grant-group-page-interaction": "Interact with pages",
+       "grant-group-file-interaction": "Interact with media",
+       "grant-group-watchlist-interaction": "Interact with your watchlist",
+       "grant-group-email": "Send email",
+       "grant-group-high-volume": "Perform high volume activity",
+       "grant-group-customization": "Customization and preferences",
+       "grant-group-administration": "Perform administrative actions",
+       "grant-group-other": "Miscellaneous activity",
+       "grant-blockusers": "Block and unblock users",
+       "grant-createaccount": "Create accounts",
+       "grant-createeditmovepage": "Create, edit, and move pages",
+       "grant-delete": "Delete pages, revisions, and log entries",
+       "grant-editinterface": "Edit the MediaWiki namespace and user CSS/JavaScript",
+       "grant-editmycssjs": "Edit your user CSS/JavaScript",
+       "grant-editmyoptions": "Edit your user preferences",
+       "grant-editmywatchlist": "Edit your watchlist",
+       "grant-editpage": "Edit existing pages",
+       "grant-editprotected": "Edit protected pages",
+       "grant-highvolume": "High-volume editing",
+       "grant-oversight": "Hide users and suppress revisions",
+       "grant-patrol": "Patrol changes to pages",
+       "grant-protect": "Protect and unprotect pages",
+       "grant-rollback": "Rollback changes to pages",
+       "grant-sendemail": "Send email to other users",
+       "grant-uploadeditmovefile": "Upload, replace, and move files",
+       "grant-uploadfile": "Upload new files",
+       "grant-basic": "Basic rights",
+       "grant-viewdeleted": "View deleted files and pages",
+       "grant-viewmywatchlist": "View your watchlist",
        "newuserlogpage": "User creation log",
        "newuserlogpagetext": "This is a log of user creations.",
        "rightslog": "User rights log",
        "action-createpage": "create pages",
        "action-createtalk": "create discussion pages",
        "action-createaccount": "create this user account",
+       "action-autocreateaccount": "automatically create this external user account",
        "action-history": "view the history of this page",
        "action-minoredit": "mark this edit as minor",
        "action-move": "move this page",
        "upload-too-many-redirects": "The URL contained too many redirects",
        "upload-http-error": "An HTTP error occurred: $1",
        "upload-copy-upload-invalid-domain": "Copy uploads are not available from this domain.",
+       "upload-foreign-cant-upload": "This wiki is not configured to upload files to the requested foreign file repository.",
        "upload-dialog-title": "Upload file",
        "upload-dialog-button-cancel": "Cancel",
        "upload-dialog-button-done": "Done",
        "upload-form-label-select-file": "Select file",
        "upload-form-label-infoform-title": "Details",
        "upload-form-label-infoform-name": "Name",
+       "upload-form-label-infoform-name-tooltip": "A unique descriptive title for the file, which will serve as a filename. You may use plain language with spaces. Do not include the file extension.",
        "upload-form-label-infoform-description": "Description",
+       "upload-form-label-infoform-description-tooltip": "Briefly describe everything notable about the work.\nFor a photo, mention the main things that are depicted, the occasion, or the place.",
        "upload-form-label-usage-title": "Usage",
        "upload-form-label-usage-filename": "File name",
        "foreign-structured-upload-form-label-own-work": "This is my own work",
        "log-title-wildcard": "Search titles starting with this text",
        "showhideselectedlogentries": "Change visibility of selected log entries",
        "log-edit-tags": "Edit tags of selected log entries",
+       "checkbox-select": "Select: $1",
+       "checkbox-all": "All",
+       "checkbox-none": "None",
+       "checkbox-invert": "Invert",
        "allpages": "All pages",
        "allpages-summary": "",
        "nextpage": "Next page ($1)",
        "listgrouprights-namespaceprotection-header": "Namespace restrictions",
        "listgrouprights-namespaceprotection-namespace": "Namespace",
        "listgrouprights-namespaceprotection-restrictedto": "Right(s) allowing user to edit",
+       "listgrants": "Grants",
+       "listgrants-summary": "The following is a list of grants with their associated access to user rights. Users can authorize applications to use their account, but with limited permissions based on the grants the user gave to the application. An application acting on behalf of a user cannot actually use rights that the user does not have however.\nThere may be [[{{MediaWiki:Listgrouprights-helppage}}|additional information]] about individual rights.",
+       "listgrants-grant": "Grant",
+       "listgrants-rights": "Rights",
        "trackingcategories": "Tracking categories",
        "trackingcategories-summary": "This page lists tracking categories which are automatically populated by the MediaWiki software. Their names can be changed by altering the relevant system messages in the {{ns:8}} namespace.",
        "trackingcategories-msg": "Tracking category",
        "unblock-summary": "",
        "blockip": "Block {{GENDER:$1|user}}",
        "blockip-legend": "Block user",
-       "blockiptext": "Use the form below to block write access from a specific IP address or username.\nThis should be done only to prevent vandalism, and in accordance with [[{{MediaWiki:Policy-url}}|policy]].\nFill in a specific reason below (for example, citing particular pages that were vandalized).",
+       "blockiptext": "Use the form below to block write access from a specific IP address or username.\nThis should be done only to prevent vandalism, and in accordance with [[{{MediaWiki:Policy-url}}|policy]].\nFill in a specific reason below (for example, citing particular pages that were vandalized).\nYou can block IP ranges using the [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] syntax; the largest allowed range is /$1 for IPv4 and /$2 for IPv6.",
        "ipaddressorusername": "IP address or username:",
        "ipbexpiry": "Expiry:",
        "ipbreason": "Reason:",
        "block-log-flags-hiddenname": "username hidden",
        "range_block_disabled": "The administrator ability to create range blocks is disabled.",
        "ipb_expiry_invalid": "Expiry time invalid.",
+       "ipb_expiry_old": "Expiry time is in the past.",
        "ipb_expiry_temp": "Hidden username blocks must be permanent.",
        "ipb_hide_invalid": "Unable to suppress this account; it has more than {{PLURAL:$1|one edit|$1 edits}}.",
        "ipb_already_blocked": "\"$1\" is already blocked.",
        "move-page": "Move $1",
        "movepage-summary": "",
        "move-page-legend": "Move page",
-       "movepagetext": "Using the form below will rename a page, moving all of its history to the new name.\nThe old title will become a redirect page to the new title.\nYou can update redirects that point to the original title automatically.\nIf you choose not to, be sure to check for [[Special:DoubleRedirects|double]] or [[Special:BrokenRedirects|broken redirects]].\nYou are responsible for making sure that links continue to point where they are supposed to go.\n\nNote that the page will <strong>not</strong> be moved if there is already a page at the new title, unless the latter is a redirect and has no past edit history.\nThis means that you can rename a page back to where it was renamed from if you make a mistake, and you cannot overwrite an existing page.\n\n<strong>Warning!</strong>\nThis can be a drastic and unexpected change for a popular page;\nplease be sure you understand the consequences of this before proceeding.",
-       "movepagetext-noredirectfixer": "Using the form below will rename a page, moving all of its history to the new name.\nThe old title will become a redirect page to the new title.\nBe sure to check for [[Special:DoubleRedirects|double]] or [[Special:BrokenRedirects|broken redirects]].\nYou are responsible for making sure that links continue to point where they are supposed to go.\n\nNote that the page will <strong>not</strong> be moved if there is already a page at the new title, unless it is a redirect and has no past edit history.\nThis means that you can rename a page back to where it was renamed from if you make a mistake, and you cannot overwrite an existing page.\n\n<strong>Warning!</strong>\nThis can be a drastic and unexpected change for a popular page;\nplease be sure you understand the consequences of this before proceeding.",
+       "movepagetext": "Using the form below will rename a page, moving all of its history to the new name.\nThe old title will become a redirect page to the new title.\nYou can update redirects that point to the original title automatically.\nIf you choose not to, be sure to check for [[Special:DoubleRedirects|double]] or [[Special:BrokenRedirects|broken redirects]].\nYou are responsible for making sure that links continue to point where they are supposed to go.\n\nNote that the page will <strong>not</strong> be moved if there is already a page at the new title, unless the latter is a redirect and has no past edit history.\nThis means that you can rename a page back to where it was renamed from if you make a mistake, and you cannot overwrite an existing page.\n\n<strong>Note:</strong>\nThis can be a drastic and unexpected change for a popular page;\nplease be sure you understand the consequences of this before proceeding.",
+       "movepagetext-noredirectfixer": "Using the form below will rename a page, moving all of its history to the new name.\nThe old title will become a redirect page to the new title.\nBe sure to check for [[Special:DoubleRedirects|double]] or [[Special:BrokenRedirects|broken redirects]].\nYou are responsible for making sure that links continue to point where they are supposed to go.\n\nNote that the page will <strong>not</strong> be moved if there is already a page at the new title, unless it is a redirect and has no past edit history.\nThis means that you can rename a page back to where it was renamed from if you make a mistake, and you cannot overwrite an existing page.\n\n<strong>Note:</strong>\nThis can be a drastic and unexpected change for a popular page;\nplease be sure you understand the consequences of this before proceeding.",
        "movepagetalktext": "If you check this box, the associated talk page will be automatically moved to new title, unless a non-empty talk page already exists there.\n\nIn this case, you will have to move or merge the page manually if desired.",
        "moveuserpage-warning": "<strong>Warning:</strong> You are about to move a user page. Please note that only the page will be moved and the user will <em>not</em> be renamed.",
        "movecategorypage-warning": "<strong>Warning:</strong> You are about to move a category page. Please note that only the page will be moved and any pages in the old category will <em>not</em> be recategorized into the new one.",
        "move-redirect-text": "",
        "category-move-redirect-override": "-",
        "revertmove": "revert",
-       "delete_and_move_text": "== Deletion required ==\nThe destination page \"[[:$1]]\" already exists.\nDo you want to delete it to make way for the move?",
+       "delete_and_move_text": "The destination page \"[[:$1]]\" already exists.\nDo you want to delete it to make way for the move?",
        "delete_and_move_confirm": "Yes, delete the page",
        "delete_and_move_reason": "Deleted to make way for move from \"[[$1]]\"",
        "selfmove": "Source and destination titles are the same;\ncannot move a page over itself.",
        "move-leave-redirect": "Leave a redirect behind",
        "protectedpagemovewarning": "<strong>Warning:</strong> This page has been protected so that only users with administrator privileges can move it.\nThe latest log entry is provided below for reference:",
        "semiprotectedpagemovewarning": "<strong>Note:</strong> This page has been protected so that only registered users can move it.\nThe latest log entry is provided below for reference:",
-       "move-over-sharedrepo": "== File exists ==\n[[:$1]] exists on a shared repository. Moving a file to this title will override the shared file.",
+       "move-over-sharedrepo": "[[:$1]] exists on a shared repository. Moving a file to this title will override the shared file.",
        "file-exists-sharedrepo": "The filename chosen is already in use on a shared repository.\nPlease choose another name.",
        "export": "Export pages",
        "export-summary": "",
        "export-download": "Save as file",
        "export-templates": "Include templates",
        "export-pagelinks": "Include linked pages to a depth of:",
+       "export-manual": "Add pages manually:",
        "allmessages": "System messages",
        "allmessagesname": "Name",
        "allmessagesdefault": "Default message text",
        "accesskey-import": "s",
        "accesskey-watchlistedit-normal-submit": "s",
        "accesskey-watchlistedit-raw-submit": "s",
-       "tooltip-pt-userpage": "Your user page",
+       "tooltip-pt-userpage": "{{GENDER:|Your user}} page",
        "tooltip-pt-anonuserpage": "The user page for the IP address you are editing as",
-       "tooltip-pt-mytalk": "Your talk page",
+       "tooltip-pt-mytalk": "{{GENDER:|Your}} talk page",
        "tooltip-pt-anontalk": "Discussion about edits from this IP address",
-       "tooltip-pt-preferences": "Your preferences",
+       "tooltip-pt-preferences": "{{GENDER:|Your}} preferences",
        "tooltip-pt-watchlist": "A list of pages you are monitoring for changes",
-       "tooltip-pt-mycontris": "A list of your contributions",
+       "tooltip-pt-mycontris": "A list of {{GENDER:|your}} contributions",
        "tooltip-pt-anoncontribs": "A list of edits made from this IP address",
        "tooltip-pt-login": "You are encouraged to log in; however, it is not mandatory",
        "tooltip-pt-logout": "Log out",
        "tooltip-t-recentchangeslinked": "Recent changes in pages linked from this page",
        "tooltip-feed-rss": "RSS feed for this page",
        "tooltip-feed-atom": "Atom feed for this page",
-       "tooltip-t-contributions": "A list of contributions by this user",
-       "tooltip-t-emailuser": "Send an email to this user",
+       "tooltip-t-contributions": "A list of contributions by {{GENDER:$1|this user}}",
+       "tooltip-t-emailuser": "Send an email to {{GENDER:$1|this user}}",
        "tooltip-t-info": "More information about this page",
        "tooltip-t-upload": "Upload files",
        "tooltip-t-specialpages": "A list of all special pages",
        "markaspatrolleddiff": "Mark as patrolled",
        "markaspatrolledlink": "[$1]",
        "markaspatrolledtext": "Mark this page as patrolled",
+       "markaspatrolledtext-file": "Mark this file version as patrolled",
        "markedaspatrolled": "Marked as patrolled",
        "markedaspatrolledtext": "The selected revision of [[:$1]] has been marked as patrolled.",
        "rcpatroldisabled": "Recent changes patrol disabled",
        "newimages-legend": "Filter",
        "newimages-label": "Filename (or a part of it):",
        "newimages-showbots": "Show uploads by bots",
+       "newimages-hidepatrolled": "Hide patrolled uploads",
        "noimages": "Nothing to see.",
        "ilsubmit": "Search",
        "bydate": "by date",
        "scarytranscludefailed-httpstatus": "[Template fetch failed for $1: HTTP $2]",
        "scarytranscludetoolong": "[URL is too long]",
        "deletedwhileediting": "<strong>Warning:</strong> This page was deleted after you started editing!",
-       "confirmrecreate": "User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing with reason:\n: <em>$2</em>\nPlease confirm that you really want to recreate this page.",
-       "confirmrecreate-noreason": "User [[User:$1|$1]] ([[User talk:$1|talk]]) deleted this page after you started editing. Please confirm that you really want to recreate this page.",
+       "confirmrecreate": "User [[User:$1|$1]] ([[User talk:$1|talk]]) {{GENDER:$1|deleted}} this page after you started editing with reason:\n: <em>$2</em>\nPlease confirm that you really want to recreate this page.",
+       "confirmrecreate-noreason": "User [[User:$1|$1]] ([[User talk:$1|talk]]) {{GENDER:$1|deleted}} this page after you started editing. Please confirm that you really want to recreate this page.",
        "recreate": "Recreate",
        "unit-pixel": "px",
        "confirm_purge_button": "OK",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|talk]])",
        "signature-anon": "[[{{#special:Contributions}}/$1|$2]]",
        "timezone-utc": "UTC",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "<strong>Warning:</strong> Default sort key \"$2\" overrides earlier default sort key \"$1\".",
        "duplicate-displaytitle": "<strong>Warning:</strong> Display title \"$2\" overrides earlier display title \"$1\".",
        "invalid-indicator-name": "<strong>Error:</strong> Page status indicators' <code>name</code> attribute must not be empty.",
        "version-libraries-license": "License",
        "version-libraries-description": "Description",
        "version-libraries-authors": "Authors",
-       "redirect": "Redirect by file, user, page or revision ID",
+       "redirect": "Redirect by file, user, page, revision, or log ID",
        "redirect-legend": "Redirect to a file or page",
        "redirect-text": "",
-       "redirect-summary": "This special page redirects to a file (given the filename), a page (given a revision ID or page ID), or a user page (given a numeric user ID). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "This special page redirects to a file (given the filename), a page (given a revision ID or page ID), a user page (given a numeric user ID), or a log entry (given the log ID). Usage: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], or [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Go",
        "redirect-lookup": "Lookup:",
        "redirect-value": "Value:",
        "redirect-page": "Page ID",
        "redirect-revision": "Page revision",
        "redirect-file": "Filename",
+       "redirect-logid": "Log ID",
        "redirect-not-exists": "Value not found",
        "fileduplicatesearch": "Search for duplicate files",
        "fileduplicatesearch-summary": "Search for duplicate files based on hash values.",
        "expand_templates_preview": "Preview",
        "expand_templates_preview_fail_html": "<em>Because {{SITENAME}} has raw HTML enabled and there was a loss of session data, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate preview attempt, please try again.</strong>\nIf it still does not work, try [[Special:UserLogout|logging out]] and logging back in.",
        "expand_templates_preview_fail_html_anon": "<em>Because {{SITENAME}} has raw HTML enabled and you are not logged in, the preview is hidden as a precaution against JavaScript attacks.</em>\n\n<strong>If this is a legitimate preview attempt, please [[Special:UserLogin|log in]] and try again.</strong>",
+       "expand_templates_input_missing": "You need to provide at least some input text.",
        "pagelanguage": "Page language selector",
        "pagelang-name": "Page",
        "pagelang-language": "Language",
        "pagelang-use-default": "Use default language",
        "pagelang-select-lang": "Select language",
+       "pagelang-submit": "Submit",
        "right-pagelang": "Change page language",
        "action-pagelang": "change the page language",
        "log-name-pagelang": "Change language log",
        "mediastatistics-summary": "Statistics about uploaded file types. This only includes the most recent version of a file. Old or deleted versions of files are excluded.",
        "mediastatistics-nfiles": "$1 ($2%)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
-       "mediastatistics-bytespertype": "Total file size for this section: $1 bytes.",
-       "mediastatistics-allbytes": "Total file size for all files: $1 bytes.",
+       "mediastatistics-bytespertype": "Total file size for this section: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Total file size for all files: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "MIME type",
        "mediastatistics-table-extensions": "Possible extensions",
        "mediastatistics-table-count": "Number of files",
        "mediastatistics-header-text": "Textual",
        "mediastatistics-header-executable": "Executables",
        "mediastatistics-header-archive": "Compressed formats",
+       "mediastatistics-header-total": "All files",
        "json-warn-trailing-comma": "$1 trailing {{PLURAL:$1|comma was|commas were}} removed from JSON",
        "json-error-unknown": "There was a problem with the JSON. Error: $1",
        "json-error-depth": "The maximum stack depth has been exceeded",
        "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."
+       "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",
+       "sessionprovider-nocookies": "Cookies may be disabled. Ensure you have cookies enabled and start again.",
+       "randomrootpage": "Random root page"
 }
-
-
index 0a1ba36..5468580 100644 (file)
                        "Macofe",
                        "Matma Rex",
                        "Xð",
-                       "Robin van der Vliet"
+                       "Robin van der Vliet",
+                       "Zciric"
                ]
        },
        "tog-underline": "Substreki ligilojn",
        "tog-hideminor": "Kaŝi malgrandajn redaktetojn ĉe <i>Lastaj ŝanĝoj</i>",
        "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-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",
@@ -80,6 +82,7 @@
        "tog-watchlisthideliu": "Kaŝi redaktojn de ensalutitaj uzantoj de la atentaro",
        "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-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",
        "october-date": "$1-a de oktobro",
        "november-date": "$1-a de novembro",
        "december-date": "$1-a de decembro",
+       "period-am": "ATM",
+       "period-pm": "PTM",
        "pagecategories": "{{PLURAL:$1|Kategorio|Kategorioj}}",
        "category_header": "Artikoloj en kategorio \"$1\"",
        "subcategories": "Subkategorioj",
        "laggedslavemode": "Avertu: la paĝo eble ne enhavas lastatempajn ĝisdatigojn.",
        "readonly": "Datumbazo ŝlosita, nurlega",
        "enterlockreason": "Bonvolu klarigi, kial oni ŝlosas la datumbazon, kaj\nla estimatan tempon de malŝlosado.",
-       "readonlytext": "La datumbazo de {{SITENAME}} estas nun ŝlosita kontraŭ\nnovaj aldonaj kaj aliaj ŝanĝoj, verŝajne pro laŭkutima flegado de la datumbazo.\nBonvolu reprovi post iom da tempo.\n\nLa ŝlosinta administranto lasis la jenan klarigon:\n<p>$1</p>",
+       "readonlytext": "La datumbazo estas nuntempe ŝlosita kontraŭ novaj aldonaj kaj aliaj ŝanĝoj, verŝajne pro kutima bontenado de la datumbazo, post kiu ĝi denove normale funkcios.\n\nLa sistema administranto, kiu ŝlosis ĝin, oferis tiun klarigon: $1",
        "missing-article": "La datumbazo ne trovis la tekston de paĝo kiun ĝi devas trovi, nomita \"$1\" $2.\n\nĈi tio ofte estas kaŭzita de sekvado de malfreŝa ''diff'' aŭ historia ligilo al paĝo kiu estis forigita.\n\nSe ĉi tio ne okazis, verŝajne vi trovis cimon en la softvaro.\nBonvolu raporti ĉi tiun al [[Special:ListUsers/sysop|administranto]], notante la TTT-adreson.",
        "missingarticle-rev": "(versio#: $1)",
        "missingarticle-diff": "(Diferenco inter versioj: $1, $2)",
        "mypreferencesprotected": "Vi ne havas permeson por redakti viajn preferojn.",
        "ns-specialprotected": "Paĝoj en la {{ns:special}} nomspaco ne povas esti redaktataj.",
        "titleprotected": "Ĉi tiu titolo estas protektita de kreado de [[User:$1|$1]].\nLa kialo donata estis ''$2''.",
-       "filereadonlyerror": "La dosiero \"$1\" ne estas modifebla, ĉar la datumbazujo \"$2\" estas en nurlegebla modo.\n\nLa administranto, kiu ŝlosis ĝin, proponis tiun klarigon: \"$3\".",
+       "filereadonlyerror": "La dosiero \"$1\" ne estas modifebla, ĉar la dosiera deponejo \"$2\" estas en nurlegebla reĝimo.\n\nLa sistema administranto, kiu ŝlosis ĝin, oferis tiun klarigon: \"$3\".",
        "invalidtitle-knownnamespace": "Nevalida titolo kun nomspaco \"$2\" kaj teksto \"$3\"",
        "invalidtitle-unknownnamespace": "Nevalida titolo kun nekonata nomspaca numero $1 kaj teksto \"$2\"",
        "exception-nologin": "Ne ensalutinta",
        "missingcommenttext": "Bonvolu entajpi komenton malsupre.",
        "missingcommentheader": "<strong>Atenton:</strong> Vi ne provizis temon aŭ subtitolon por ĉi tiu komento.\nSe vi klakos \"Konservi\" denove, via redakto estos konservita sen ĝi.",
        "summary-preview": "Resuma antaŭrigardo:",
-       "subject-preview": "Antaŭrigardo de Temo/Subitolo:",
+       "subject-preview": "Antaŭrigardo de temo:",
        "previewerrortext": "Dum provo antaŭrigardi viajn ŝanĝojn okazis eraro.",
        "blockedtitle": "La uzanto estas forbarita.",
        "blockedtext": "'''Via konto aŭ IP-adreso estis forbarita'''\n\nLa forbaro estis farita de $1.\nLa skribita kialo estas ''$2''.\n\n* Komenco de forbaro: $8\n* Findato de forbarado: $6\n* Intencita forbarito: $7\n\nVi rajtas kontakti $1 aŭ alian [[{{MediaWiki:Grouppage-sysop}}|administranton]] por pridiskuti la forbaradon.\nVi ne povas uzi la 'retpoŝtan' funkcion, escepte se vi indikis validan retpoŝtan adreson en viaj [[Special:Preferences|kontaj agordoj]] kaj vi ne estas blokita uzi ĝin.\nVia IP-adreso estas $3 kaj la ID de la forbarado estas $5.\nBonvolu mencii jenajn indikojn en viaj ĉi-temaj kontaktoj.",
        "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": "En mencii vin, la programaro uzos vortojn de neŭtra genro, ĉiam ol estu ebla",
+       "gender-unknown": "Menciate vin, la programaro uzos vortojn de neŭtra genro, ĉiam kiam estos ebla",
        "gender-male": "Vira",
        "gender-female": "Ina",
        "prefs-help-gender": "Nedeviga: uzita por sekseca salutado de la programaro. Ĉi tiu informo montriĝos publike.",
        "right-blockemail": "Forbari uzanton de retpoŝta sendado",
        "right-hideuser": "Forbari salutnomon, kaŝante ĝin de la publiko",
        "right-ipblock-exempt": "Preterpasi IP-forbarojn, aŭtomatajn forbarojn, kaj ĝeneralajn forbarojn",
-       "right-proxyunbannable": "Preterpasi aŭtomatajn forbarojn de prokuriloj",
        "right-unblockself": "Malforbari oni mem",
        "right-protect": "Ŝanĝi protektniveloj kaj redakti protektitajn paĝojn",
        "right-editprotected": "Redakti protektitajn paĝojn (sen kaskada protektado)",
        "special-characters-title-minus": "minus-signo",
        "mw-widgets-dateinput-placeholder-day": "JJJJ-MM-TT",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
-       "api-error-blacklisted": "Bonvolu elekti alian, priskriban titolon."
+       "api-error-blacklisted": "Bonvolu elekti alian, priskriban titolon.",
+       "randomrootpage": "Hazarda radika paĝo"
 }
index 0d08b3b..18d7a20 100644 (file)
                        "Nelson6e65",
                        "Matiia",
                        "SinNovedades",
-                       "Rodm23"
+                       "Rodm23",
+                       "Yllelder",
+                       "Syum90",
+                       "Cindie.Capel",
+                       "ElGatoSaez"
                ]
        },
        "tog-underline": "Subrayar los enlaces:",
        "october-date": "$1 de octubre",
        "november-date": "$1 de noviembre",
        "december-date": "$1 de diciembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoría|Categorías}}",
        "category_header": "Páginas en la categoría «$1»",
        "subcategories": "Subcategorías",
        "databaseerror-query": "Consulta: $1",
        "databaseerror-function": "Función: $1",
        "databaseerror-error": "Error: $1",
+       "transaction-duration-limit-exceeded": "Para evitar la creación de lentitud alta de respuesta, la transacción fue abortada porque la duración de escritura ($1) excedió el límite de $2 {{PLURAL:$2|segundo|segundos}}.\nSi estás cambiando muchos elementos a la vez, trata de hacer operaciones similares más pequeñas.",
        "laggedslavemode": "<strong>Advertencia:</strong> puede que falten las actualizaciones más recientes en esta página.",
        "readonly": "Base de datos bloqueada",
        "enterlockreason": "Explica el motivo del bloqueo, incluyendo una estimación de cuándo se producirá el desbloqueo",
        "missingarticle-rev": "(revisión: $1)",
        "missingarticle-diff": "(diferencia: $1, $2)",
        "readonly_lag": "La base de datos se ha bloqueado automáticamente mientras sus servidores esclavos se sincronizan con el maestro.",
+       "nonwrite-api-promise-error": "El encabezado HTTP 'Promise-Non-Write-API-Action' ha sido enviado pero la petición fue hacia un módulo de escritura de la API.",
        "internalerror": "Error interno",
        "internalerror_info": "Error interno: $1",
        "internalerror-fatal-exception": "Excepción grave de tipo \"$1\"",
        "mypreferencesprotected": "No tienes permiso para editar tus preferencias.",
        "ns-specialprotected": "No se pueden editar las páginas especiales.",
        "titleprotected": "Este título ha sido protegido contra creación por [[User:$1|$1]].\nEl motivo proporcionado es \"<em>$2</em>\".",
-       "filereadonlyerror": "No se puede modificar el archivo \"$1\" porque el repositorio de archivos \"$2\" es de solo lectura.\nEl administrador que lo ha bloqueado ofrece esta explicación: \"$3\".",
+       "filereadonlyerror": "No se puede modificar el archivo \"$1\" porque el repositorio de archivos \"$2\" es de solo lectura.\n\nEl administrador del sistema que lo ha bloqueado ofrece esta explicación: \"$3\".",
        "invalidtitle-knownnamespace": "El título con el espacio de nombres «$2» y el texto «$3» no es válido",
        "invalidtitle-unknownnamespace": "El título con el espacio de nombres desconocido (n.º $1) y el texto «$2» no es válido",
        "exception-nologin": "No has iniciado sesión",
        "virus-scanfailed": "ha fallado el análisis (código $1)",
        "virus-unknownscanner": "antivirus desconocido:",
        "logouttext": "<strong>Tu sesión ha finalizado.</strong>\n\nPuede que algunas páginas continúen mostrándose como si la sesión estuviera iniciada hasta que actualices la caché de tu navegador.",
+       "cannotlogoutnow-title": "No se puede cerrar sesión ahora",
+       "cannotlogoutnow-text": "No se puede cerrar sesión cuando se usa $1.",
        "welcomeuser": "¡Bienvenido/a, $1!",
        "welcomecreation-msg": "Se ha creado tu cuenta.\nSi lo deseas, puedes cambiar tus [[Special:Preferences|preferencias]] para {{SITENAME}}.",
        "yourname": "Usuario:",
        "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",
+       "cannotloginnow-text": "No se puede iniciar sesión cuando se usa $1.",
        "yourdomainname": "Tu dominio:",
        "password-change-forbidden": "No puedes cambiar las contraseñas en este wiki.",
        "externaldberror": "Hubo un error de autenticación en la base de datos, o bien no tienes autorización para actualizar tu cuenta externa.",
        "wrongpasswordempty": "No has escrito una contraseña.\nInténtalo de nuevo.",
        "passwordtooshort": "Las contraseñas deben tener al menos {{PLURAL:$1|1 carácter|$1 caracteres}}.",
        "passwordtoolong": "Las contraseñas no deben tener más de {{PLURAL:$1|1 carácter|$1 caracteres}}.",
+       "passwordtoopopular": "No se pueden usar las contraseñas más comunes. Elige una menos popular.",
        "password-name-match": "Tu contraseña debe ser diferente de tu nombre de usuario.",
        "password-login-forbidden": "El uso de este nombre de usuario y contraseña han sido prohibidos.",
        "mailmypassword": "Restablecer la contraseña",
        "resetpass_submit": "Establecer contraseña e iniciar sesión",
        "changepassword-success": "La contraseña se modificó correctamente.",
        "changepassword-throttled": "Has intentado acceder demasiadas veces recientemente.\nEspera $1 antes de intentarlo de nuevo.",
+       "botpasswords": "Contraseñas de bots",
+       "botpasswords-summary": "Las <em>contraseñas de bots</em> permiten el acceso a una cuenta de usuario mediante la API sin usar las credenciales principales de la cuenta. Los derechos de un usuario mientras haya iniciado sesión con una contraseña de bot pueden estar restringidos.\n\nSi no sabes por qué querrías hacer esto, probablemente no deberías hacerlo. Nadie debería pedirte que generes una de estas claves y que se la entregues.",
+       "botpasswords-disabled": "Las contraseñas de bot están desactivadas.",
+       "botpasswords-no-central-id": "Para usar una contraseña de bot, debes estar conectado a una cuenta centralizada.",
+       "botpasswords-existing": "Contraseñas de bots existentes",
+       "botpasswords-createnew": "Crear una nueva contraseña de bot",
+       "botpasswords-editexisting": "Editar una contraseña de bot existente",
+       "botpasswords-label-appid": "Nombre del bot:",
+       "botpasswords-label-create": "Crear",
+       "botpasswords-label-update": "Actualizar",
+       "botpasswords-label-cancel": "Cancelar",
+       "botpasswords-label-delete": "Borrar",
+       "botpasswords-label-resetpassword": "Restablecer la contraseña",
+       "botpasswords-label-grants": "Permisos aplicables:",
+       "botpasswords-label-restrictions": "Restricciones de uso:",
+       "botpasswords-label-grants-column": "Concedido",
+       "botpasswords-bad-appid": "El nombre del bot \"$1\" no es válido.",
+       "botpasswords-insert-failed": "No se pudo agregar el nombre del bot \"$1\". ¿Ya ha sido añadido?",
+       "botpasswords-update-failed": "No se pudo actualizar el nombre del bot \"$1\". ¿Ha sido borrado?",
+       "botpasswords-created-title": "Se creó la contraseña de bot",
+       "botpasswords-created-body": "La contraseña de bot \"$1\" se creó correctamente.",
+       "botpasswords-updated-title": "La contraseña de bot ha sido actualizada",
+       "botpasswords-deleted-title": "La contraseña de bot ha sido eliminada",
+       "botpasswords-deleted-body": "La contraseña de bot \"$1\" ha sido eliminada.",
+       "botpasswords-newpassword": "La nueva contraseña para iniciar sesión con <strong>$1</strong> es <strong>$2</strong>. <em>Conserva estos datos para usos futuros.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider no está disponible.",
+       "botpasswords-restriction-failed": "Las restricciones de la contraseña de bot impiden este inicio de sesión.",
+       "botpasswords-invalid-name": "El nombre de usuario especificado no contiene el separador de contraseña de bot (\"$1\").",
+       "botpasswords-not-exist": "El usuario \"$1\" no tiene una contraseña de bot llamada \"$2\".",
        "resetpass_forbidden": "No se pueden cambiar las contraseñas",
        "resetpass-no-info": "Debes iniciar sesión para acceder directamente a esta página.",
        "resetpass-submit-loggedin": "Cambiar contraseña",
        "passwordreset-emailtext-ip": "Alguien (probablemente tú, desde la dirección IP $1) ha solicitado el restablecimiento de tu contraseña en {{SITENAME}} ($4). {{PLURAL:$3|La siguiente cuenta está asociada|Las siguientes cuentas están asociadas}}\na esta dirección de correo electrónico:\n\n$2\n\n{{PLURAL:$3|Esta contraseña temporal|Estas contraseñas temporales}} caducarán en {{PLURAL:$5|un día|$5 días}}.\nAhora puedes iniciar sesión y establecer una nueva contraseña. Si fue otra persona la que realizó esta solicitud, o si ya recuerdas tu contraseña original y, por tanto, no deseas cambiarla, puedes ignorar este mensaje y continuar usando tu contraseña anterior.",
        "passwordreset-emailtext-user": "El usuario $1 de {{SITENAME}} solicitó el restablecimiento de tu contraseña en {{SITENAME}}\n($4). {{PLURAL:$3|La siguiente cuenta está asociada|Las siguientes cuentas están asociadas}} a esta dirección de correo electrónico:\n\n$2\n\n{{PLURAL:$3|Esta contraseña temporal|Estas contraseñas temporales}} caducarán en {{PLURAL:$5|un día|$5 días}}.\nAhora puedes iniciar sesión y establecer una nueva contraseña. Si fue otra persona la que realizó esta solicitud, o si ya recuerdas tu contraseña original y, por tanto, no deseas cambiarla, puedes ignorar este mensaje y continuar usando tu contraseña anterior.",
        "passwordreset-emailelement": "Nombre de {{GENDER:$1|usuario|usuaria}}: \n$1\n\nContraseña temporal: \n$2",
-       "passwordreset-emailsentemail": "Si esta es una dirección de correo electrónico registrada para tu cuenta, entonces se enviará un correo electrónico para el restablecimiento de tu contraseña.",
-       "passwordreset-emailsentusername": "Si hay una dirección de correo electrónico conectada a esta cuenta, entonces se enviará un correo electrónico para el restablecimiento de tu contraseña.",
+       "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",
        "changeemail": "Cambiar o eliminar la dirección de correo electrónico",
        "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:",
+       "changeemail-newemail-help": "Este campo debería dejarse en blanco si quieres quitar tu dirección de correo electrónico. No podrás restablecer una contraseña olvidada y no recibirás correos de esta wiki si se quita la dirección de correo electrónico.",
        "changeemail-none": "(ninguna)",
        "changeemail-password": "Tu contraseña en {{SITENAME}}:",
        "changeemail-submit": "Cambiar correo electrónico",
        "userrights": "Gestión de permisos de usuario",
        "userrights-lookup-user": "Configurar grupos de usuarios",
        "userrights-user-editname": "Escribe un nombre de usuario:",
-       "editusergroup": "Modificar grupos del usuario",
+       "editusergroup": "Modificar grupos {{GENDER:$1|del usuario|de la usuaria}}",
        "editinguser": "Cambio de los permisos {{GENDER:$1|del usuario|de la usuaria}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Modificar grupos {{GENDER:$1|del usuario| de la usuaria}}",
-       "saveusergroups": "Guardar grupos del usuario",
+       "saveusergroups": "Guardar grupos {{GENDER:$1|del usuario|de la usuaria}}",
        "userrights-groupsmember": "Miembro de:",
        "userrights-groupsmember-auto": "Miembro implícito de:",
        "userrights-groups-help": "Puedes modificar los grupos a los que pertenece {{GENDER:$1|este usuario|esta usuaria}}:\n* Un recuadro marcado significa que {{GENDER:$1|el usuario|la usuaria}} está en ese grupo.\n* Un recuadro no marcado significa que {{GENDER:$1|el usuario|la usuaria}} no está en ese grupo.\n* Un * indica que no podrás eliminar el grupo una vez que lo agregues, o viceversa.",
        "right-createpage": "Crear páginas que no sean de discusión",
        "right-createtalk": "Crear páginas de discusión",
        "right-createaccount": "Crear cuentas de usuario nuevas",
+       "right-autocreateaccount": "Iniciar sesión automáticamente con una cuenta de usuario externa",
        "right-minoredit": "Marcar ediciones como menores",
        "right-move": "Trasladar páginas",
        "right-move-subpages": "Trasladar páginas con sus subpáginas",
        "right-blockemail": "Bloquear a un usuario para que no pueda mandar correos electrónicos",
        "right-hideuser": "Bloquear un nombre de usuario, haciéndolo invisible",
        "right-ipblock-exempt": "Quedar exento de bloqueos a IPs, autobloqueos y bloqueos de rango.",
-       "right-proxyunbannable": "Quedar exento de bloqueos automáticos a proxies",
        "right-unblockself": "Desbloquearse a sí mismo",
        "right-protect": "Cambiar niveles de protección y editar páginas protegidas en cascada",
        "right-editprotected": "Editar páginas protegidas como «{{int:protect-level-sysop}}»",
        "right-managechangetags": "Crear y eliminar [[Special:Tags|etiquetas]] en la base de datos",
        "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] junto con los cambios propios",
        "right-changetags": "Agregar y quitar [[Special:Tags|etiquetas]] arbitrarias a revisiones individuales y entradas del registro",
+       "grant-generic": "Paquete de derechos \"$1\"",
+       "grant-group-page-interaction": "Interactuar con páginas",
+       "grant-group-file-interaction": "Interactuar con multimedia",
+       "grant-group-watchlist-interaction": "Interactuar con tu lista de seguimiento",
+       "grant-group-email": "Enviar correo electrónico",
+       "grant-group-high-volume": "Realizar actividad de volumen alto",
+       "grant-group-customization": "Personalización y preferencias",
+       "grant-group-administration": "Realizar acciones administrativas",
+       "grant-group-other": "Actividades diversas",
+       "grant-blockusers": "Bloquear y desbloquear usuarios",
+       "grant-createaccount": "Crear cuentas",
+       "grant-createeditmovepage": "Crear, editar y trasladar páginas",
+       "grant-delete": "Borrar páginas, revisiones y entradas del registro",
+       "grant-editinterface": "Editar el espacio de nombres MediaWiki y el CSS/JavaScript de usuarios",
+       "grant-editmycssjs": "Editar tu CSS o JavaScript",
+       "grant-editmyoptions": "Editar tus preferencias de usuario",
+       "grant-editmywatchlist": "Editar tu lista de seguimiento",
+       "grant-editpage": "Editar páginas existentes",
+       "grant-editprotected": "Editar páginas protegidas",
+       "grant-highvolume": "Gran cantidad de ediciones",
+       "grant-oversight": "Ocultar a los usuarios y suprimir las revisiones",
+       "grant-patrol": "Verificar cambios a páginas",
+       "grant-protect": "Proteger y desproteger páginas",
+       "grant-rollback": "Revertir cambios a páginas",
+       "grant-sendemail": "Enviar un correo electrónico a otros usuarios",
+       "grant-uploadeditmovefile": "Subir, reemplazar y renombrar archivos",
+       "grant-uploadfile": "Subir archivos nuevos",
+       "grant-basic": "Permisos básicos",
+       "grant-viewdeleted": "Ver archivos y páginas eliminados",
+       "grant-viewmywatchlist": "Ver tu lista de seguimiento",
        "newuserlogpage": "Registro de creación de usuarios",
        "newuserlogpagetext": "Este es un registro de creación de usuarios.",
        "rightslog": "Registro de permisos de usuario",
        "action-createpage": "crear páginas",
        "action-createtalk": "crear páginas de discusión",
        "action-createaccount": "crear esta cuenta de usuario",
+       "action-autocreateaccount": "crear automáticamente esta cuenta de usuario externa",
        "action-history": "ver el historial de esta página",
        "action-minoredit": "marcar este cambio como menor",
        "action-move": "trasladar esta página",
        "upload-form-label-select-file": "Seleccionar archivo",
        "upload-form-label-infoform-title": "Detalles",
        "upload-form-label-infoform-name": "Nombre",
+       "upload-form-label-infoform-name-tooltip": "Un título único descriptivo para el archivo, que servirá como un nombre de archivo. Puedes usar un lenguaje claro con espacios. No incluyas la extensión del archivo.",
        "upload-form-label-infoform-description": "Descripción",
+       "upload-form-label-infoform-description-tooltip": "Describe brevemente todo lo destacable acerca del trabajo.\nPara una foto, menciona las cosas principales que se representan, la ocasión o el lugar.",
        "upload-form-label-usage-title": "Uso",
        "upload-form-label-usage-filename": "Nombre del archivo",
        "foreign-structured-upload-form-label-own-work": "Esto es mi trabajo propio",
        "foreign-structured-upload-form-2-label-useful": "Debe ser <strong>educativo y útil</strong> para enseñarle a otros",
        "foreign-structured-upload-form-2-label-ccbysa": "Debe estar <strong>aceptado para publicarse por siempre</strong> en Internet bajo la licencia [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
        "foreign-structured-upload-form-2-label-alternative": "Si no todos los criterios de arriba son ciertos, aún puede ser capaz de subir este archivo usando el[https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de Carga de Commons], mientras esté disponible bajo una licencia libre.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Subiendo el archivo, usted da fe de que es dueño de los derechos de autor en este archivo, y acepta a irrevocablemente liberar este archivo a Wikimedia Commons bajo la licencia Creative Commons Attribution-ShareAlike 4.0, y acepta los [https://wikimediafoundation.org/wiki/Terms_of_Use Términos de Uso].",
-       "foreign-structured-upload-form-3-label-question-website": "¿Descargó esta imagen de un sitio web, o la obtuvo de una búsqueda de imágenes?",
+       "foreign-structured-upload-form-2-label-termsofuse": "Al subir el archivo, das fe de que eres dueño de los derechos de autor en este archivo, y aceptas irrevocablemente liberar este archivo a Wikimedia Commons bajo la licencia Creative Commons Attribution-ShareAlike 4.0, y aceptas los [https://wikimediafoundation.org/wiki/Terms_of_Use Términos de uso].",
+       "foreign-structured-upload-form-3-label-question-website": "¿Has descargado esta imagen de un sitio web, o la has obtenido de una búsqueda de imágenes?",
        "foreign-structured-upload-form-3-label-question-ownwork": "¿Creó esta imagen (tomó la foto, hizo el dibujo, etc) usted mismo?",
        "foreign-structured-upload-form-3-label-question-noderiv": "¿Contiene, o está inspirada por, trabajo de propiedad de cualquier otra persona, como un logotipo?",
        "foreign-structured-upload-form-3-label-yes": "Sí",
        "foreign-structured-upload-form-3-label-no": "No",
-       "foreign-structured-upload-form-3-label-alternative": "Desafortunadamente, en este caso, esta herramienta no es compatible subiendo este archivo. Aún puede ser capaz de subir este archivo usando el[https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de Carga de Commons], mientras esté disponible bajo una licencia libre.",
-       "foreign-structured-upload-form-4-label-good": "Usando esta herramienta, puede subir gráficos educaciones que ha creado y fotografías que ha tomado, que no contienen trabajo de alguien más.",
-       "foreign-structured-upload-form-4-label-bad": "No puede subir imágenes encontradas en un motor de búsqueda o descargadas de otros sitios web.",
+       "foreign-structured-upload-form-3-label-alternative": "Desafortunadamente, en este caso, esta herramienta no admite la subida de este archivo. Pero lo puedes hacer usando el [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subidas de Commons], mientras esté disponible bajo una licencia libre.",
+       "foreign-structured-upload-form-4-label-good": "Usando esta herramienta, puedes subir gráficos educativos que hayas creado y fotografías que hayas tomado, que no contengan trabajo de alguien más.",
+       "foreign-structured-upload-form-4-label-bad": "No puedes subir imágenes encontradas en un motor de búsqueda o descargadas de otros sitios web.",
        "backend-fail-stream": "No se pudo transmitir el archivo «$1».",
        "backend-fail-backup": "No se pudo hacer copia de seguridad del archivo «$1».",
        "backend-fail-notexists": "El archivo  $1  no existe.",
        "ntransclusions": "usado en {{PLURAL:$1|una página|$1 páginas}}",
        "specialpage-empty": "Esta página está vacía.",
        "lonelypages": "Páginas huérfanas",
-       "lonelypagestext": "Las siguientes páginas no están enlazadas ni transcluídas en otras páginas de {{SITENAME}}.",
+       "lonelypagestext": "Las siguientes páginas no están enlazadas ni transcluidas en otras páginas de {{SITENAME}}.",
        "uncategorizedpages": "Páginas sin categorizar",
        "uncategorizedcategories": "Categorías sin categorizar",
        "uncategorizedimages": "Imágenes sin categorizar",
        "wantedtemplates": "Plantillas requeridas",
        "mostlinked": "Artículos más enlazados",
        "mostlinkedcategories": "Categorías más enlazadas",
-       "mostlinkedtemplates": "Páginas más transcluídas",
+       "mostlinkedtemplates": "Páginas más transcluidas",
        "mostcategories": "Páginas con más categorías",
        "mostimages": "Imágenes más usadas",
        "mostinterwikis": "Páginas con más interwikis",
        "log-title-wildcard": "Buscar títulos que empiecen con este texto",
        "showhideselectedlogentries": "Cambiar la visibilidad de las entradas del registro seleccionadas",
        "log-edit-tags": "Editar las etiquetas de las entradas del registro seleccionadas",
+       "checkbox-select": "Seleccionar: $1",
+       "checkbox-all": "Todas",
+       "checkbox-none": "Ninguna",
+       "checkbox-invert": "Invertir",
        "allpages": "Todas las páginas",
        "nextpage": "Siguiente página ($1)",
        "prevpage": "Página anterior ($1)",
        "listgrouprights-namespaceprotection-header": "Restricciones del espacio de nombres",
        "listgrouprights-namespaceprotection-namespace": "Espacio de nombres",
        "listgrouprights-namespaceprotection-restrictedto": "Derechos de usuario para editar",
+       "listgrants-rights": "Conceder",
        "trackingcategories": "Categorías de seguimiento",
        "trackingcategories-summary": "Esta página lista categorías de seguimiento que han sido generadas automáticamente por el software MediaWiki. Sus nombres pueden cambiarse editando su mensaje correspondiente en el espacio de nombres {{ns:8}}.",
        "trackingcategories-msg": "Categoría de seguimiento",
        "wlshowhideanons": "usuarios anónimos",
        "wlshowhidepatr": "ediciones verificadas",
        "wlshowhidemine": "mis ediciones",
+       "wlshowhidecategorization": "categorización de página",
        "watchlist-options": "Opciones de la lista de seguimiento",
        "watching": "Vigilando...",
        "unwatching": "Eliminando de la lista de seguimiento...",
        "unblock": "Desbloquear usuario",
        "blockip": "Bloquear {{GENDER:$1|al usuario|a la usuaria}}",
        "blockip-legend": "Bloquear usuario",
-       "blockiptext": "Usa el siguiente formulario para bloquear el acceso de escritura desde una dirección IP específica o nombre de usuario.\nEsto debería hacerse sólo para prevenir vandalismos, y de acuerdo a las [[{{MediaWiki:Policy-url}}|políticas]].\nExplica la razón específica del bloqueo (por ejemplo, citando las páginas en particular que han sido objeto de vandalismo).",
+       "blockiptext": "Usa el siguiente formulario para bloquear el acceso de escritura desde una dirección IP específica o nombre de usuario.\nEsto debería hacerse sólo para prevenir vandalismos, y de acuerdo a las [[{{MediaWiki:Policy-url}}|políticas]].\nExplica la razón específica del bloqueo (por ejemplo, citando las páginas en particular que han sido objeto de vandalismo).\nPuedes bloquear intervalos IP con la sintaxis [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; el intervalo más grande permitido es /$1 para IPv4 y /$2 para IPv6.",
        "ipaddressorusername": "Dirección IP o nombre de usuario:",
        "ipbexpiry": "Caducidad:",
        "ipbreason": "Motivo:",
        "ipbreason-dropdown": "*Motivos comunes de bloqueo\n** Añadir información falsa\n** Eliminar contenido de las páginas\n** Publicitar enlaces a otras páginas web\n** Añadir basura a las páginas\n** Comportamiento intimidatorio u hostil\n** Abuso de múltiples cuentas\n** Nombre de usuario inaceptable",
        "ipb-hardblock": "Impedir que los usuarios identificados editen desde esta dirección IP",
        "ipbcreateaccount": "Prevenir la creación de cuentas de usuario",
-       "ipbemailban": "Prevenir que el usuario envíe correo electrónico",
+       "ipbemailban": "Impedir que el usuario envíe correo electrónico",
        "ipbenableautoblock": "Bloquear automáticamente la última dirección IP usada por este usuario y cualquier IP posterior desde la cual intente editar",
        "ipbsubmit": "Bloquear a este usuario",
        "ipbother": "Especificar caducidad",
        "block-log-flags-hiddenname": "nombre de usuario ocultado",
        "range_block_disabled": "La facultad de administrador de crear bloqueos por rangos está deshabilitada.",
        "ipb_expiry_invalid": "El tiempo de caducidad no es válido.",
+       "ipb_expiry_old": "El tiempo de expiración está en el pasado.",
        "ipb_expiry_temp": "Los bloqueos a nombres de usuario ocultos deben ser permanentes.",
        "ipb_hide_invalid": "No se puede suprimir esta cuenta; tiene más de {{PLURAL:$1|una edición|$1 ediciones}}.",
        "ipb_already_blocked": "La cuenta «$1» ya está bloqueada.",
        "movenosubpage": "Esta página no tiene subpáginas.",
        "movereason": "Motivo:",
        "revertmove": "revertir",
-       "delete_and_move_text": "==Se necesita borrado==\n\nLa página de destino (\"[[:$1]]\") ya existe. ¿Quiere borrarla para permitir al traslado?",
+       "delete_and_move_text": "La página de destino «[[:$1]]» ya existe.\n¿Quieres borrarla para permitir el traslado?",
        "delete_and_move_confirm": "Sí, borrar la página",
        "delete_and_move_reason": "Borrada para permitir el traslado de \"[[$1]]\"",
        "selfmove": "Los títulos de origen y destino son los mismos;\nno se puede trasladar una página sobre sí misma.",
        "export-download": "Guardar como archivo",
        "export-templates": "Incluir plantillas",
        "export-pagelinks": "Incluir páginas enlazadas a una profundidad de:",
+       "export-manual": "Añadir páginas manualmente:",
        "allmessages": "Todos los mensajes de MediaWiki",
        "allmessagesname": "Nombre",
        "allmessagesdefault": "Texto predeterminado",
        "javascripttest-pagetext-frameworks": "Selecciona uno de los marcos de pruebas siguientes: $1",
        "javascripttest-pagetext-skins": "Elige una apariencia con la que ejecutar las pruebas:",
        "javascripttest-qunit-intro": "Consulta la [$1 documentación sobre las pruebas] en mediawiki.org.",
-       "tooltip-pt-userpage": "Tu página de {{gender:|usuario|usuaria}}",
+       "tooltip-pt-userpage": "Tu página de {{GENDER:|usuario|usuaria}}",
        "tooltip-pt-anonuserpage": "La página de usuario de la IP desde la que editas",
-       "tooltip-pt-mytalk": "Tu página de discusión",
+       "tooltip-pt-mytalk": "{{GENDER:|Tu}} página de discusión",
        "tooltip-pt-anontalk": "Discusión sobre ediciones hechas desde esta dirección IP",
-       "tooltip-pt-preferences": "Tus preferencias",
+       "tooltip-pt-preferences": "{{GENDER:|Tus}} preferencias",
        "tooltip-pt-watchlist": "Lista de páginas cuyos cambios vigilas",
-       "tooltip-pt-mycontris": "Lista de tus contribuciones",
+       "tooltip-pt-mycontris": "Una lista de {{GENDER:|tus}} contribuciones",
        "tooltip-pt-anoncontribs": "Una lista de modificaciones hechas desde esta dirección IP",
        "tooltip-pt-login": "Te recomendamos iniciar sesión, aunque no es obligatorio",
        "tooltip-pt-logout": "Salir de la sesión",
        "tooltip-t-recentchangeslinked": "Cambios recientes en las páginas que enlazan con esta",
        "tooltip-feed-rss": "Sindicación RSS de esta página",
        "tooltip-feed-atom": "Sindicación Atom de esta página",
-       "tooltip-t-contributions": "Lista de contribuciones de este usuario",
-       "tooltip-t-emailuser": "Enviar un mensaje de correo a este usuario",
+       "tooltip-t-contributions": "Lista de contribuciones de {{GENDER:$1|este usuario|esta usuaria}}",
+       "tooltip-t-emailuser": "Enviar un mensaje de correo a {{GENDER:$1|este usuario|esta usuaria}}",
        "tooltip-t-info": "Más información sobre esta página",
        "tooltip-t-upload": "Subir archivos",
        "tooltip-t-specialpages": "Lista de todas las páginas especiales",
        "pageinfo-category-files": "Número de archivos",
        "markaspatrolleddiff": "Marcar como verificada",
        "markaspatrolledtext": "Marcar esta página como verificada",
+       "markaspatrolledtext-file": "Marcar esta versión de archivo como verificada",
        "markedaspatrolled": "Marcado como revisado",
        "markedaspatrolledtext": "La revisión seleccionada de [[:$1|$1]] ha sido marcada como verificada.",
        "rcpatroldisabled": "Se ha desactivado la supervisión de cambios recientes",
        "newimages-legend": "Filtro",
        "newimages-label": "Nombre del archivo (o una parte):",
        "newimages-showbots": "Mostrar cargas de bots",
+       "newimages-hidepatrolled": "Ocultar las subidas verificadas",
        "noimages": "No hay nada que ver.",
        "ilsubmit": "Buscar",
        "bydate": "por fecha",
        "exif-compression-6": "JPEG (antiguo)",
        "exif-copyrighted-true": "Con derechos de autor",
        "exif-copyrighted-false": "No se ha definido el estado del copyright",
+       "exif-photometricinterpretation-1": "Blanco y negro (el negro es 0)",
        "exif-unknowndate": "Fecha desconocida",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Volteada horizontalmente",
        "scarytranscludefailed-httpstatus": "[Error de recuperación de plantilla para $1: HTTP $2]",
        "scarytranscludetoolong": "[El URL es demasiado largo]",
        "deletedwhileediting": "<strong>Aviso</strong>: se borró esta página después de que empezaras a editarla.",
-       "confirmrecreate": "El usuario [[User:$1|$1]] ([[User talk:$1|disc.]]) borró esta página después de que comenzaras a editarla, por este motivo:\n: ''$2''\nConfirma que realmente quieres volver a crear esta página.",
-       "confirmrecreate-noreason": "El usuario [[User:$1|$1]] ([[User talk:$1|discusión]]) borró esta página después de que comenzaras a editarla. Por favor confirma que realmente quieres recrear esta página.",
+       "confirmrecreate": "{{GENDER:$1|El usuario|La usuaria}} [[User:$1|$1]] ([[User talk:$1|discusión]]) borró esta página después de que comenzaras a editarla, por este motivo:\n: <em>$2</em>\nConfirma que realmente quieres volver a crear esta página.",
+       "confirmrecreate-noreason": "{{GENDER:$1|El usuario|La usuaria}} [[User:$1|$1]] ([[User talk:$1|discusión]]) borró esta página después de que comenzaras a editarla. Confirma que realmente quieres recrear esta página.",
        "recreate": "Recrear",
        "confirm_purge_button": "Aceptar",
        "confirm-purge-top": "¿Limpiar la caché de esta página?",
        "watchlisttools-edit": "Ver y editar tu lista de seguimiento",
        "watchlisttools-raw": "Editar lista de seguimiento en crudo",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discusión]])",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "<strong>Advertencia:</strong> la clave de ordenamiento predeterminada «$2» anula la clave de ordenamiento anterior «$1».",
        "duplicate-displaytitle": "<strong>Advertencia:</strong> El título visualizado \"$2\" sobreescribe al anterior \"$1\".",
        "invalid-indicator-name": "<strong>Error:</strong> el atributo <code>name</code> de los indicadores de estado de página no debe estar vacío.",
        "version-libraries-license": "Licencia",
        "version-libraries-description": "Descripción",
        "version-libraries-authors": "Autores",
-       "redirect": "Redirigir por archivo, usuario, página o ID de revisión",
+       "redirect": "Redirigir por archivo, o identificador de usuario, página, revisión o registro",
        "redirect-legend": "Redirigir a un archivo o página",
-       "redirect-summary": "Esta página especial redirige a un archivo (dado un nombre), a una página (dado un identificador de revisión o de página) o a una página de usuario (dado un identificador numérico de usuario). Uso: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] o [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Esta página especial redirige a un archivo (dado un nombre), a una página (dado un identificador de revisión o de página), a una página de usuario (dado un identificador numérico de usuario) o a una entrada del registro (dado un identificador de registro). Uso: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] o [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Ir",
        "redirect-lookup": "Buscar:",
        "redirect-value": "Valor:",
        "redirect-page": "Identificador de la página",
        "redirect-revision": "Revisión de página",
        "redirect-file": "Nombre de archivo",
+       "redirect-logid": "Identificador de registro",
        "redirect-not-exists": "No se encontró el valor",
        "fileduplicatesearch": "Búsqueda de archivos duplicados",
        "fileduplicatesearch-summary": "Búsqueda de archivos duplicados en base a su valor hash.",
        "logentry-suppress-block": "$1 {{GENDER:$2|bloqueó}} {{GENDER:$4|$3}} durante un plazo de $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|cambió}} la configuración del bloqueo de {{GENDER:$4|$3}} durante un plazo de $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|importó}} $3 subiendo un archivo",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|importó}} $3 subiendo un archivo ($4 {{PLURAL:$4|revisión|revisiones}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|importó}} $3 desde otro wiki",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|importó}} $3 desde $5 ($4 {{PLURAL:$4|revisión|revisiones}})",
        "logentry-merge-merge": "$1 {{GENDER:$2|combinó}} $3 en $4 (revisiones hasta el $5)",
        "logentry-move-move": "$1 {{GENDER:$2|trasladó}} la página $3 a $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|trasladó}} la página $3 a $4 sin dejar una redirección",
        "expand_templates_preview": "Previsualización",
        "expand_templates_preview_fail_html": "<em>Se ha ocultado la previsualización como precaución frente a ataques JavaScript. Esto se debe a que {{SITENAME}} tiene habilitada la característica de código HTML en bruto, y se perdieron los datos de la sesión.</em>\n\n<strong>Si se trata de un intento de previsualización legítimo, inténtalo de nuevo.</strong>\nSi aun así no funciona, intenta [[Special:UserLogout|cerrar sesión]] y volver a acceder.",
        "expand_templates_preview_fail_html_anon": "<em>Se ha ocultado la previsualización como precaución frente a ataques JavaScript. Esto se debe a que {{SITENAME}} tiene habilitada la característica de código HTML en bruto, y no has iniciado sesión.</em>\n\n<strong>Si se trata de un intento de previsualización legítimo, [[Special:UserLogin|inicia sesión]] e inténtalo de nuevo.</strong>",
+       "expand_templates_input_missing": "Necesitas proporcionar al menos algún texto de entrada.",
        "pagelanguage": "Selector de idioma de página",
        "pagelang-name": "Página",
        "pagelang-language": "Idioma",
        "pagelang-use-default": "Utilizar el idioma predeterminado",
        "pagelang-select-lang": "Seleccionar idioma",
+       "pagelang-submit": "Enviar",
        "right-pagelang": "Cambiar el idioma de la página",
        "action-pagelang": "cambiar el idioma de la página",
        "log-name-pagelang": "Registro de cambios en idiomas",
        "mediastatistics-summary": "Estadísticas sobre los tipos de archivos cargados. Sólo se incluyen las versiones más recientes. Los archivos antiguos o eliminados están excluidos.",
        "mediastatistics-nfiles": "$1 ($2 %)",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 ''byte''|$1 ''bytes''}} ($2; $3 %)",
+       "mediastatistics-bytespertype": "Tamaño de archivo total para esta sección: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Tamaño de archivo total para todos los archivos: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "Tipo MIME",
        "mediastatistics-table-extensions": "Extensiones posibles",
        "mediastatistics-table-count": "Número de archivos",
        "mediastatistics-header-text": "Textual",
        "mediastatistics-header-executable": "Ejecutables",
        "mediastatistics-header-archive": "Formatos comprimidos",
+       "mediastatistics-header-total": "Todos los archivos",
        "json-warn-trailing-comma": "Se {{PLURAL:$1|eliminó una coma|eliminaron $1 comas}} al final en el archivo JSON",
        "json-error-unknown": "Ocurrió un problema con el código JSON. Error: $1",
        "json-error-depth": "Se ha superado la profundidad máxima de la pila",
        "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."
+       "api-error-blacklisted": "Elige un título diferente, más descriptivo.",
+       "sessionprovider-generic": "sesiones $1",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "sesiones basadas en cookies",
+       "sessionprovider-nocookies": "Puede que las cookies estén desactivadas. Actívalas y comienza de nuevo.",
+       "randomrootpage": "Página raíz aleatoria"
 }
index d2fa121..c4057a3 100644 (file)
        "right-blockemail": "Keelata kasutajal e-kirjade saatmine",
        "right-hideuser": "Blokeerida kasutajanimi, peites selle avalikkuse eest",
        "right-ipblock-exempt": "Mööduda automaatsetest blokeeringutest ning aadressivahemiku- ja IP-blokeeringutest",
-       "right-proxyunbannable": "Mööduda automaatsetest puhverserveri blokeeringutest",
        "right-unblockself": "Eemaldada enda blokeeringut",
        "right-protect": "Muuta kaitsetasemeid ja redigeerida kaskaadkaitsega lehekülgi",
        "right-editprotected": "Muuta lehekülgi kaitsetasemega \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Koostada [[Special:Tags|märgiseid]] ja kustutada neid andmebaasist",
        "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-group-file-interaction": "Interaktsioon meediafailidega",
+       "grant-group-watchlist-interaction": "Interaktsioon sinu jälgimisloendiga",
+       "grant-group-email": "E-kirja saatmine",
+       "grant-group-high-volume": "Suuremahuline tegevus",
+       "grant-group-customization": "Kohandamine ja eelistused",
+       "grant-group-administration": "Administraatori toimingud",
+       "grant-group-other": "Mitmesugused toimingud",
+       "grant-blockusers": "Kasutajate blokeerimine ja blokeeringute eemaldamine",
+       "grant-createaccount": "Kontode loomine",
+       "grant-createeditmovepage": "Lehekülgede alustamine, muutmine ja teisaldamine",
+       "grant-delete": "Lehekülgede, redaktsioonide ja logisissekannete kustutamine",
+       "grant-editinterface": "MediaWiki nimeruumi ning kasutaja CSSi ja JavaScripti redigeerimine",
+       "grant-editmycssjs": "Oma CSSi või JavaScripti muutmine",
+       "grant-editmyoptions": "Enda eelistuste muutmine",
+       "grant-editmywatchlist": "Oma jälgimisloendi muutmine",
+       "grant-editpage": "Olemasolevate lehekülgede redigeerimine",
+       "grant-editprotected": "Kaitstud lehekülgede redigeerimine",
+       "grant-highvolume": "Suuremahuline redigeerimine",
+       "grant-patrol": "Lehekülgede muudatuste kontroll",
+       "grant-protect": "Lehekülgede kaitsmine ja kaitse eemaldamine",
+       "grant-rollback": "Lehekülgede muudatuste tühistamine",
+       "grant-sendemail": "Kasutajatele e-kirjade saatmine",
+       "grant-uploadeditmovefile": "Failide üleslaadimine, asendamine ja teisaldamine",
+       "grant-uploadfile": "Uute failide üleslaadimine",
+       "grant-viewdeleted": "Kustutatud failide ja lehekülgede vaatamine",
+       "grant-viewmywatchlist": "Oma jälgimisloendi vaatamine",
        "newuserlogpage": "Konto loomise logi",
        "newuserlogpagetext": "Siin on logitud kasutajate registreerimine.",
        "rightslog": "Kasutajaõiguste logi",
        "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",
        "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",
        "wlshowhideanons": "anonüümsed kasutajad",
        "wlshowhidepatr": "kontrollitud muudatused",
        "wlshowhidemine": "minu muudatused",
+       "wlshowhidecategorization": "kategoriseerimine",
        "watchlist-options": "Jälgimisloendi seaded",
        "watching": "Jälgimine...",
        "unwatching": "Jälgimise lõpetamine...",
        "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-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."
+       "api-error-blacklisted": "Palun vali muu pealkiri, mis on kirjeldav.",
+       "randomrootpage": "Juhuslik juurlehekülg"
 }
index 15ba46f..b80513d 100644 (file)
        "image_tip": "Txertatutako irudia",
        "media_sample": "Adibidea.ogg",
        "media_tip": "Media fitxategi lotura",
-       "sig_tip": "Zure sinadura data eta orduarekin",
+       "sig_tip": "Zure sinadura, gehi data eta ordua",
        "hr_tip": "Lerro horizontala (gutxitan erabili)",
        "summary": "Laburpena:",
        "subject": "Gaia:",
        "right-blockemail": "Erabiltzaile bati blokeatu mezu elektronikoak bidaltzeko aukera",
        "right-hideuser": "Blokeatu erabiltzaile izen bat, publikotik ezkutatuta",
        "right-ipblock-exempt": "IP blokeoen, auto-blokeoen eta maila blokeoen gainetik pasa.",
-       "right-proxyunbannable": "Proxyen blokeo automatikoen gainetik pasa",
        "right-unblockself": "Beren burua desblokeatu",
        "right-protect": "Orrialde babestuak aldatu eta babes maila aldatu",
        "right-editprotected": "Babestutako orrialdeak aldatu (babes jauzirik gabe)",
        "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-uploadfile": "Igotako fitxategi berriak",
        "newuserlogpage": "Erabiltzaile erregistroa",
        "newuserlogpagetext": "Hau azken erabiltzaileen sorreren erregistroa da.",
        "rightslog": "Erabiltzaile eskubideen erregistroa",
        "enotif_body_intro_moved": "{{SITENAME}}(e)ko $1 orrialdea {{GENDER:$2|mugitu}} du $2 erabiltzaileak $PAGEEDITDATE datan, ikus $3 oraingo bertsiorako.",
        "enotif_body_intro_restored": "{{SITENAME}}(e)ko $1 orrialdea {{GENDER:$2|berrezarri}} du $2 erabiltzaileak $PAGEEDITDATE datan, ikus $3 oraingo bertsiorako.",
        "enotif_body_intro_changed": "{{SITENAME}}(e)ko $1 orrialdea {{GENDER:$2|aldatu}} du $2 erabiltzaileak $PAGEEDITDATE datan, ikus $3 oraingo bertsiorako.",
-       "enotif_lastvisited": "Jo $1 orrialdera zure azken bisitaz geroztik izandako aldaketa guztiak ikusteko.",
+       "enotif_lastvisited": "Ikus «$1» zure azken bisitaz geroztik izandako aldaketa guztiak ikusteko.",
        "enotif_lastdiff": "Jo $1(e)ra aldaketa hau ikusteko.",
        "enotif_anon_editor": "$1 erabiltzaile anonimoa",
        "enotif_body": "Kaixo $WATCHINGUSERNAME,\n\n{{SITENAME}}-(e)ko $PAGETITLE orrialdea $CHANGEDORCREATED egin da $PAGEEDITOR-(e)k une honetan: $PAGEEDITDATE, ikus $PAGETITLE_URL azken bertsiorako.\n\n$NEWPAGE\n\nEgilearen laburpena: $PAGESUMMARY $PAGEMINOREDIT\n\nEgilearekin harremanetan jarri:\nposta: $PAGEEDITOR_EMAIL\nwiki: $PAGEEDITOR_WIKI\n\nEz dira oharpen gehiago bidaliko orrialde hau berriz bisitatzen ez baduzu.\nHorrez gain, orrialdeen oharpen konfigurazioa leheneratu dezakezu jarraipen zerrendatik.\n\n             Adeitasunez {{SITENAME}}(e)ko oharpen sistema\n\n--\nZure epostaren jakinarazpenen konfigurazioa aldatzeko, ikus\n{{canonicalurl:{{#special:Preferences}}}}\n\nZure jarraipen zerrendako konfigurazioa aldatzeko, ikus\n{{canonicalurl:{{#special:EditWatchlist}}}}\n\nOrrialdea zure jarraipen zerrendatik ezabatzeko, ikus\n$UNWATCHURL\n\nLaguntza:\n$HELPPAGE",
        "immobile-target-namespace": "Orrialdeak ezin dira \"$1\" motara mugitu",
        "immobile-target-namespace-iw": "Interwiki lotura ez da baliagarria orrialdea mugitu ahal izateko.",
        "immobile-source-page": "Orrialde hau mugiezina da.",
-       "immobile-target-page": "Helburuko orrialdera ezin da mugitu.",
+       "immobile-target-page": "Helburuko orri horretara ezin da mugitu.",
        "imagenocrossnamespace": "Ezin da mugitu fitxategia fitxategiena ez den izen batera",
        "imagetypemismatch": "Fitxategiaren luzapen berriak ez du bere motako fitxategiekin bat egiten",
        "imageinvalidfilename": "Xede-artxiboaren izenak ez du balio",
        "tooltip-ca-watch": "Orrialde hau jarraipen zerrendan gehitu",
        "tooltip-ca-unwatch": "Orrialde hau jarraipen zerrendatik kendu",
        "tooltip-search": "Wiki honetan bilatu",
-       "tooltip-search-go": "Baldin balego zehazki izen honetako orrialdera joan",
+       "tooltip-search-go": "Izen zehatz hori duen orrira joan, existitzen bada",
        "tooltip-search-fulltext": "Textu honetarako orriak bilatu",
        "tooltip-p-logo": "Azala",
        "tooltip-n-mainpage": "Azala bisitatu",
        "imgmultipageprev": "&larr; aurreko orrialdea",
        "imgmultipagenext": "hurrengo orrialdea &rarr;",
        "imgmultigo": "Joan!",
-       "imgmultigoto": "$1 orrialdera joan",
+       "imgmultigoto": "Joan «$1» orrira",
        "img-lang-default": "(berezko hizkuntza)",
        "img-lang-info": "Irudi hau $1 hizkuntzan renderizatu $2.",
        "img-lang-go": "Joan",
        "table_pager_empty": "Emaitzik ez",
        "autosumm-blank": "Orritik eduki guztia ezabatuta",
        "autosumm-replace": "Orriaren edukiaren ordez, «$1» jarri da",
-       "autoredircomment": "[[$1]] orrialdera birzuzentzentzen",
+       "autoredircomment": "«[[$1]]» orrira birzuzendua",
        "autosumm-new": "Orria sortu da. Edukia: $1",
        "autosumm-newblank": "Orrialde zuria sortu da",
        "lag-warn-normal": "{{PLURAL:$1|segundu $1|$1 segundu}} baino berriagoak diren aldaketak ez dira zerrenda honetan agertuko.",
        "mediastatistics-header-text": "Testuala",
        "mediastatistics-header-executable": "Exekutagarriak",
        "mediastatistics-header-archive": "Formatu konprimatuak",
+       "mediastatistics-header-total": "Fitxategi guztiak",
        "json-error-syntax": "Sintaxi-errorea",
        "headline-anchor-title": "Lotura sekzio honetara",
        "special-characters-group-latin": "Latina",
index 52a6d6c..f713c6d 100644 (file)
@@ -10,7 +10,8 @@
                        "Babanwalia",
                        "Henares",
                        "MarcoAurelio",
-                       "Macofe"
+                       "Macofe",
+                       "Fitoschido"
                ]
        },
        "tog-underline": "Surrayal atihus:",
        "wlheader-showupdated": "Las páhinas que s'án emburacau dendi la úrtima vezi que las visoreasti son muestrás en '''negrina'''",
        "wlnote": "Embahu {{PLURAL:$1|es el úrtimu chambu|son los úrtimus '''$1''' chambus}} enas úrtimas {{PLURAL:$2|oras|'''$2''' oras}}.",
        "wlshowlast": "Muestral úrtimus $1 oras $2 dias",
+       "watchlistall2": "tó",
        "watchlist-options": "Ocionis de la mi lista e seguimientu",
        "watching": "Vehilandu...",
        "unwatching": "Abaldonandu la vehiláncia en...",
        "modifiedarticleprotection": "chambau el nivel de proteción a \"[[$1]]\"",
        "unprotectedarticle": "\"[[$1]]\" esprotehiu",
        "protect-title": "Estableciendu nivel de proteción pa \"$1\"",
-       "prot_1movedto2": "[[$1]] s´á moviu a [[$2]]",
+       "prot_1movedto2": "[[$1]] sá moviu a [[$2]]",
        "protect-legend": "Confirmal proteción",
        "protectcomment": "Razón:",
        "protectexpiry": "Acabiha:",
        "newtitle": "Nuevu entítulu:",
        "move-watch": "Vehilal esta páhina",
        "movepagebtn": "Movel páhina",
-       "pagemovedsub": "S´á moviu la páhina",
+       "pagemovedsub": "Sá moviu la páhina",
        "movepage-moved": "S'á muau '''\"$1\" a \"$2\"'''",
        "movepage-moved-redirect": "Á siu criá una redireción.",
        "articleexists": "Ya desisti una páhina con esi nombri u nu se premiti el nombri qu´as lihiu.\nPol favol, escrebi otru entítulu.",
        "movelogpagetext": "Embahu ai una lista colas páhinas movias.",
        "movereason": "Razón:",
        "revertmove": "revertil",
-       "delete_and_move": "Esborral i movel",
        "delete_and_move_text": "==Es mestel esborral==\n\nYa desisti la páhina \"[[:$1]]\". Te petaria esborrala pa premitil el treslau?",
        "delete_and_move_confirm": "Sí, esborral la páhina",
        "delete_and_move_reason": "Esborrá pa premitil el treslau",
index b014450..516c186 100644 (file)
        "october-date": "$1 اکتبر",
        "november-date": "$1 نوامبر",
        "december-date": "$1 دسامبر",
+       "period-am": "صبح",
+       "period-pm": "عصر",
        "pagecategories": "{{PLURAL:$1|رده|رده‌ها}}",
        "category_header": "صفحه‌های ردهٔ «$1»",
        "subcategories": "زیررده‌ها",
        "nstab-template": "الگو",
        "nstab-help": "صفحهٔ راهنما",
        "nstab-category": "رده",
-       "mainpage-nstab": "صفحه اصلی",
+       "mainpage-nstab": "صفحهٔ اصلی",
        "nosuchaction": "چنین عملی وجود ندارد",
        "nosuchactiontext": "عمل مشخص‌شده در نشانی اینترنتی نامجاز است.\nممکن است نشانی اینترنتی را اشتباه وارد کرده باشید یا پیوند مشکل‌داری را دنبال کرده باشید.\nهمچنین ممکن است ایرادی در نرم‌افزار استفاده‌شده در {{SITENAME}} وجود داشته باشد.",
        "nosuchspecialpage": "چنین صفحهٔ ویژه‌ای وجود ندارد",
        "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": "الان امکان وررود به سامانه نیست",
+       "cannotloginnow-text": "در زمان استفاده از $1 امکان ورود به سامانه وجود ندارد.",
        "yourdomainname": "دامنهٔ شما:",
        "password-change-forbidden": "شما نمی‌توانید گذرواژه‌ها را در این ویکی تغییر دهید.",
        "externaldberror": "خطایی در ارتباط با پایگاه داده رخ داده است یا اینکه شما اجازهٔ به‌روزرسانی حساب خارجی خود را ندارید.",
        "resetpass_submit": "تنظیم گذرواژه و ورود به سامانه",
        "changepassword-success": "گذرواژهٔ شما با موفقیت تغییر داده شد!",
        "changepassword-throttled": "شما به تازگی چندین‌بار برای ثبت ورود تلاش کرده‌اید.\nلطفاً پیش از آنکه دوباره تلاش کنید $1 صبر کنید.",
+       "botpasswords": "گذرواژه ربات",
+       "botpasswords-summary": "<em>گذرواژه‌های رباتی</em> اجازه دسترسی به یک حساب کاربری با ای‌پی‌آی بدون استفاده از رمز اصلی حساب را می‌دهد. دسترسی‌های کاربری موجود هنگامی که با گذرواژهٔ رباتیک وارد می‌شوید ممکن است محدود باشند.\n\nاگر نمی‌دانید که ممکن است با این چه کنید، احتمالاً نباید هیچ کاری کنید. هیچ‌کس نباید از شما خواسته باشد که یکی از این‌ها درست کنید به آن‌ها بدهید.",
+       "botpasswords-disabled": "گذرواژه‌های ربات غیرفعال شده‌است.",
+       "botpasswords-no-central-id": "برای استفاده از گذرواژه‌های رباتی، شما ابتدا می‌بایست به یک حساب متمرکز وارد شود.",
+       "botpasswords-existing": "گذرواژه‌های موجود ربات",
+       "botpasswords-createnew": "ایجاد گذرواژه ربات",
+       "botpasswords-editexisting": "ویرایش گذرواژه موجود ربات",
+       "botpasswords-label-appid": "نام ربات:",
+       "botpasswords-label-create": "ایجاد",
+       "botpasswords-label-update": "به‌روز",
+       "botpasswords-label-cancel": "لغو",
+       "botpasswords-label-delete": "حذف",
+       "botpasswords-label-resetpassword": "بازگردانی گذرواژه",
+       "botpasswords-label-grants": "اعطاهای اجرا شدنی:",
+       "botpasswords-help-grants": "هر اجازه به حقوق کاربری که یک حساب کاربری دارد. [[Special:ListGrants|table of 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": "گذرواژهٔ رباتی «$1» با موفقیت ایجاد شد.",
+       "botpasswords-updated-title": "گذرواژه ربات به‌روز شد",
+       "botpasswords-updated-body": "گذرواژهٔ رباتی «$1» با موفقیت به‌روز شد.",
+       "botpasswords-deleted-title": "گذرواژه ربات حذف شد",
+       "botpasswords-deleted-body": "گذرواژهٔ رباتی «$1» حذف شد.",
+       "botpasswords-newpassword": "<strong>$2</strong> گذرواژهٔ جدید برای ورود با <strong>$1</strong> است. <em>لطفاً این را برای ارجاع در آینده ذخیره کنید.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider موجود نیست.",
+       "botpasswords-restriction-failed": "محدودیت‌های گذرواژهٔ ربات از این ورود جلوگیری می‌کند.",
+       "botpasswords-invalid-name": "نام کاربری مشخص شده دارای جداکنندهٔ گذرواژهٔ رباتی نیست (\"$1\").",
+       "botpasswords-not-exist": "کاربر «$1» گذرواژهٔ رباتی نام‌دهی شدهٔ «$2» ندارد.",
        "resetpass_forbidden": "نمی‌توان گذرواژه‌ها را تغییر داد",
        "resetpass-no-info": "برای دسترسی مستقیم به این صفحه شما باید به سامانه وارد شده باشید.",
        "resetpass-submit-loggedin": "تغییر گذرواژه",
        "missingcommentheader": "<strong>یادآوری:</strong> شما موضوع/عنوان این یادداشت را مشخص نکرده‌اید.\nاگر دوباره دکمهٔ «{{int:savearticle}}» را فشار دهید ویرایش شما بدون آن ذخیره خواهد شد.",
        "summary-preview": "پیش‌نمایش خلاصه:",
        "subject-preview": "پیش‌نمایش موضوع:",
-       "previewerrortext": "در زمان تلاش برای نمایش دادن تغییرات شما، خطای رخ داد.",
+       "previewerrortext": "در زمان تلاش برای نمایش دادن تغییرات شما، خطایی رخ داد.",
        "blockedtitle": "کاربر بسته شده‌است",
        "blockedtext": "<strong>دسترسی حساب کاربری یا نشانی آی‌پی شما بسته شده‌است.</strong>\n\nاین قطع دسترسی توسط $1 انجام شده است.\nدلیل ارائه‌شده چنین است: <em>$2</em>\n\n* شروع قطع دسترسی: $8\n* پایان قطع دسترسی: $6\n* کاربری هدف قطع دسترسی: $7\n\nشما می‌توانید با $1 یا [[{{MediaWiki:Grouppage-sysop}}|مدیری]] دیگر تماس بگیرید و در این باره صحبت کنید.\nتوجه کنید که شما نمی‌توانید از قابلیت «ایمیل به این کاربر» استفاده کنید مگر آنکه آدرس ایمیل معتبری در [[Special:Preferences|ترجیحات کاربری]] خودتان ثبت کرده باشید و نیز باید امکان استفاده از این قابلیت برای شما قطع نشده باشد.\nنشانی آی‌پی فعلی شما $3 و شمارهٔ قطع دسترسی شما $5 است.\nلطفاً تمامی جزئیات فوق را در کلیهٔ درخواست‌هایی که در این باره مطرح می‌کنید ذکر کنید.",
        "autoblockedtext": "دسترسی نشانی آی‌پی شما قطع شده‌است، زیرا این نشانی آی‌پی توسط کاربر دیگری استفاده شده که دسترسی او توسط $1 قطع شده‌است.\nدلیل ارائه‌شده چنین است:\n\n:''$2''\n\n* شروع قطع دسترسی: $8\n* پایان قطع دسترسی: $6\n* کاربری هدف قطع دسترسی: $7\n\nشما می‌توانید با $1 یا [[{{MediaWiki:Grouppage-sysop}}|مدیری]] دیگر تماس بگیرید و در این باره صحبت کنید.\nتوجه کنید که شما نمی‌توانید از قابلیت «ایمیل به این کاربر» استفاده کنید مگر آنکه نشانی ایمیل معتبری در [[Special:Preferences|ترجیحات کاربری]] خودتان ثبت کرده باشید و نیز باید امکان استفاده از این قابلیت برای شما قطع نشده باشد.\nنشانی آی‌پی فعلی شما $3 و شمارهٔ قطع دسترسی شما $5 است.\nلطفاً تمامی جزئیات فوق را در کلیهٔ درخواست‌هایی که در این باره مطرح می‌کنید ذکر کنید.",
        "userrights": "مدیریت اختیارات کاربر",
        "userrights-lookup-user": "مدیریت گروه‌های کاربری",
        "userrights-user-editname": "یک نام کاربری وارد کنید:",
-       "editusergroup": "ویرایش گروه‌های کاربری",
+       "editusergroup": "ویرایش گروه‌های {{GENDER:$1|کاربری}}",
        "editinguser": "تغییر اختیارات کاربری کاربر {{GENDER:$1|کاربر}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "ویرایش گروه‌های کاربری",
-       "saveusergroups": "ثبت گروه‌های کاربری",
+       "saveusergroups": "ثبت گروه‌های {{GENDER:$1|کاربری}}",
        "userrights-groupsmember": "عضو:",
        "userrights-groupsmember-auto": "عضو ضمنی:",
        "userrights-groups-help": "شما می‌توانید گروه‌هایی را که کاربر در آن قرار دارد تغییر دهید:\n* جعبهٔ علامت‌خورده نشانهٔ بودن کاربر در آن گروه است.\n* جعبهٔ خالی نشانهٔ نبودن کاربر در آن گروه است.\n* علامت * به این معنی‌است که اگر آن گروه را بیفزایید نمی‌توانید بعداً برش دارید، و برعکس.",
        "right-createpage": "ایجاد صفحه (در مورد صفحه‌های غیر بحث)",
        "right-createtalk": "ایجاد صفحه‌های بحث",
        "right-createaccount": "ایجاد حساب‌های کاربری",
+       "right-autocreateaccount": "ورود خودکار با یک حساب کاربری خارجی",
        "right-minoredit": "علامت زدن ویرایش‌ها به عنوان جزئی",
        "right-move": "انتقال صفحه",
        "right-move-subpages": "انتقال صفحات به همراه زیر‌صفحات‌شان",
        "right-blockemail": "قطع دسترسی دیگر کاربران برای ارسال ایمیل",
        "right-hideuser": "قطع دسترسی کاربر و پنهان کردن آن از دید عموم",
        "right-ipblock-exempt": "تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
-       "right-proxyunbannable": "تاثیر نپذیرفتن از قطع دسترسی خودکار پروکسی‌ها",
        "right-unblockself": "بازکردن دسترسی خود",
        "right-protect": "تغییر میزان محافظت صفحات و ویرایش صفحات محافظت‌شده آبشاری",
        "right-editprotected": "ویرایش صفحه‌های محافظت‌شده به عنوان «{{int:protect-level-sysop}}»",
        "right-managechangetags": "ایجاد و حذف [[Special:Tags|برچسب‌ها]] از پایگاه داده",
        "right-applychangetags": "تائید [[Special:Tags|برچسب]] بر روی تغییرات یک نفر",
        "right-changetags": "افزودن یا حذف [[Special:Tags|برچسب]] قراردادی بر روی نسخه یا سیاهه ورودی‌ها",
+       "grant-generic": "\" $1 \" حقوق بسته",
+       "grant-group-page-interaction": "تعامل با صفحات",
+       "grant-group-file-interaction": "تعامل با رسانه",
+       "grant-group-watchlist-interaction": "تعامل با فهرست پیگیری‌های شما",
+       "grant-group-email": "ارسال ایمیل",
+       "grant-group-high-volume": "انجام فعالیت‌های حجم بالا",
+       "grant-group-customization": "سفارشی‌سازی و تنظیمات",
+       "grant-group-administration": "انجام اقدامات مدیریتی",
+       "grant-group-other": "فعالیت‌های متفرقه",
+       "grant-blockusers": "بستن و باز کردن کاربرها",
+       "grant-createaccount": "ایجاد حساب‌های کاربری",
+       "grant-createeditmovepage": "ایجاد، ویرایش و انتقال صفحات",
+       "grant-delete": "حذف صفحات، نسخه‌های ویرایش و سیاهه ورودی",
+       "grant-editinterface": "ویرایش CSS کاربر/جاوااسکریپت  و فضای نام مدیاویکی",
+       "grant-editmycssjs": "ویرایش  CSS /جاوااسکریپت  کاربری",
+       "grant-editmyoptions": "اولویت‌های کاربری را ویرایش کنید",
+       "grant-editmywatchlist": "ویرایش فهرست پی‌گیری‌هایتان",
+       "grant-editpage": "ویرایش صفحات موجود",
+       "grant-editprotected": "ویرایش صفحه حفاظت شده",
+       "grant-highvolume": "ویرایش با حجم بالا",
+       "grant-oversight": "پنهان کردن ویرایش‌ها",
+       "grant-patrol": "تغییرات گشت صفحات",
+       "grant-protect": "حفاظت و عدم حفاظت صفحات",
+       "grant-rollback": "واگردانی  تغییرات صفحات",
+       "grant-sendemail": "ارسال ایمیل به دیگر کاربران",
+       "grant-uploadeditmovefile": "بارگذاری، جایگزینی و انتقال پرونده‌ها",
+       "grant-uploadfile": "بازگذاری پرونده‌های جدید",
+       "grant-basic": "دسترسی‌های اولیه",
+       "grant-viewdeleted": "مشاهدهٔ پرونده و صفحات حذف شده",
+       "grant-viewmywatchlist": "مشاهدۀ فهرست پیگیری‌هایتان",
        "newuserlogpage": "سیاههٔ ایجاد کاربر",
        "newuserlogpagetext": "این سیاهه‌ای از نام‌های کاربری تازه‌ساخته‌شده است.",
        "rightslog": "سیاههٔ اختیارات کاربر",
        "action-createpage": "ایجاد صفحه",
        "action-createtalk": "ایجاد صفحه‌های بحث",
        "action-createaccount": "ایجاد این حساب کاربری",
+       "action-autocreateaccount": "حساب کاربری خارجی به صورت خودکار ساخته شد",
        "action-history": "مشاهده تاریخچه این صفحه",
        "action-minoredit": "علامت زدن این ویرایش به عنوان جزئی",
        "action-move": "انتقال این صفحه",
        "upload-form-label-select-file": "یک فایل انتخاب کنید",
        "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": "نام پرونده",
        "foreign-structured-upload-form-label-own-work": "این کار خودم است",
        "log-title-wildcard": "صفحه‌هایی را جستجو کن که عنوانشان با این عبارت آغاز می‌شود",
        "showhideselectedlogentries": "تغییر پدیداری موارد انتخاب‌شده سیاهه",
        "log-edit-tags": "ویرایش برچسب سیاههٔ انتخاب شده",
+       "checkbox-select": "انتخاب: $1",
+       "checkbox-all": "همه",
+       "checkbox-none": "هیچ‌کدام",
+       "checkbox-invert": "معکوس",
        "allpages": "همهٔ صفحات",
        "nextpage": "صفحهٔ بعد ($1)",
        "prevpage": "صفحهٔ قبلی ($1)",
        "listgrouprights-namespaceprotection-header": "محدودیت فضای نام",
        "listgrouprights-namespaceprotection-namespace": "فضای نام",
        "listgrouprights-namespaceprotection-restrictedto": "دسترسی(های) مجاز کاربر برای ویرایش",
+       "listgrants": "اعطا",
+       "listgrants-summary": "فهرست روبرو دسترسی‌های اعطاشده با دسترسی‌هایشان به دسترسی‌های کاربری است. کاربران می‌توانند برنامه‌ها را به حسابشان دسترسی دهند ولی با دسترسی محدود بر پایهٔ اعطاشده‌های کاربر به برنامه. یک برنامه از طرف کاربر عمل می‌کند و نمی‌تواند کاری را انجام دهد که کاربر بدان دسترسی ندارد.\nاینجا احتمالاً [[{{MediaWiki:Listgrouprights-helppage}}|اطلاعات بیشتری]] دربارهٔ دسترسی‌های منحصر به‌فرد وجود دارد.",
+       "listgrants-grant": "اعطا",
+       "listgrants-rights": "دسترسی‌ها",
        "trackingcategories": "رده‌های ردیابی",
        "trackingcategories-summary": "این صفحه فهرست رده‌هایی ردیابی است که به‌صورت خودکار توسط مدیاویکی پر می‌شوند است. نام‌هایشان می‌تواند پس از تغییر پیام‌های سامانه‌ای مرتبط در فضای نام {{ns:8}} تغییر یابد.",
        "trackingcategories-msg": "ردهٔ ردیابی",
        "wlshowhideanons": "کاربران ناشناس",
        "wlshowhidepatr": "ویرایش‌های گشت‌خورده",
        "wlshowhidemine": "ویرایش‌های من",
+       "wlshowhidecategorization": "دسته‌بندی صفحه",
        "watchlist-options": "گزینه‌های پی‌گیری",
        "watching": "پی‌گیری...",
        "unwatching": "توقف پی‌گیری...",
        "unblock": "بازکردن کاربر",
        "blockip": "بستن {{GENDER:$1|کاربر}}",
        "blockip-legend": "بستن کاربر",
-       "blockiptext": "از فرم زیر برای بستن دسترسی ویرایش یک نشانی آی‌پی یا نام کاربری مشخص استفاده کنید.\nاین کار فقط فقط باید برای جلوگیری از خرابکاری و بر اساس [[{{MediaWiki:Policy-url}}|سیاست قطع دسترسی]] انجام شود.\nدلیل مشخص این کار را در زیر ذکر کنید (مثلاً با ذکر صفحه‌های به‌خصوصی که مورد خرابکاری واقع شده‌اند).",
+       "blockiptext": "از فرم زیر برای بستن دسترسی ویرایش یک نشانی آی‌پی یا نام کاربری مشخص استفاده کنید.\nاین کار فقط فقط باید برای جلوگیری از خرابکاری و بر اساس [[{{MediaWiki:Policy-url}}|سیاست قطع دسترسی]] انجام شود.\nدلیل مشخص این کار را در زیر ذکر کنید (مثلاً با ذکر صفحه‌های به‌خصوصی که مورد خرابکاری واقع شده‌اند).\nشما می‌توانید بازرهٔ آی‌پی که از ساختار [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] استفاده می‌کنید را ببندید. بزرگترین بازه /$1 برای IPv4 و /$2 برای IPv6 است.",
        "ipaddressorusername": "نشانی آی‌پی یا نام کاربری:",
        "ipbexpiry": "زمان سرآمدن:",
        "ipbreason": "دلیل:",
        "block-log-flags-hiddenname": "نام کاربری پنهان",
        "range_block_disabled": "بستن یک بازه توسط مدیران غیر فعال است.",
        "ipb_expiry_invalid": "زمان سرآمدن نامعتبر.",
+       "ipb_expiry_old": "زمان سرآمدن در گذشته‌است.",
        "ipb_expiry_temp": "قطع دسترسی کاربرهای پهنان باید همیشگی باشد.",
        "ipb_hide_invalid": "قادر به سرکوب این حساب نیست; این بیشتر از {{PLURAL:$1|یک ویرایش|$1 ویرایش‌ها}} دارد.",
        "ipb_already_blocked": "«$1» همین الان هم بسته‌است",
        "export-download": "ذخیره به صورت پرونده",
        "export-templates": "شامل شدن الگوها",
        "export-pagelinks": "شامل شدن صفحه‌های پیوند شده تا این عمق:",
+       "export-manual": "افزودن صفحات به صورت دستی:",
        "allmessages": "پیغام‌های سامانه",
        "allmessagesname": "نام",
        "allmessagesdefault": "متن پیش‌فرض پیغام",
        "javascripttest-pagetext-frameworks": "لطفاً یکی از چارچوب‌های آزمایش زیر را انتخاب کنید: $1",
        "javascripttest-pagetext-skins": "پوسته‌ای را برای اجرای آزمایش‌ها انتخاب کنید:",
        "javascripttest-qunit-intro": "[$1 مستندات آزمایش] را در mediawiki.org ببینید.",
-       "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-anoncontribs": "فهرست ویرایش‌ها انجام شده از این نشانی آی‌پی",
        "tooltip-pt-login": "توصیه می‌شود که به سامانه وارد شوید، گرچه اجباری نیست",
        "tooltip-pt-logout": "خروج از سامانه",
        "tooltip-t-recentchangeslinked": "تغییرات اخیر صفحه‌هایی که این صفحه به آن‌ها پیوند دارد",
        "tooltip-feed-rss": "خبرنامه آراس‌اس برای این صفحه",
        "tooltip-feed-atom": "خبرنامهٔ اتم برای این صفحه",
-       "tooltip-t-contributions": "فهرست مشارکت‌های این کاربر",
-       "tooltip-t-emailuser": "ارسال ایمیل به این کاربر",
+       "tooltip-t-contributions": "فهرست مشارکت‌ها توسط {{GENDER:$1|این کاربر}}",
+       "tooltip-t-emailuser": "ارسال ایمیل به {{GENDER:$1|این کاربر}}",
        "tooltip-t-info": "اطلاعات بیشتر دربارهٔ این صفحه",
        "tooltip-t-upload": "بارگذاری تصاویر و پرونده‌های دیگر",
        "tooltip-t-specialpages": "فهرستی از همهٔ صفحه‌های ویژه",
        "pageinfo-category-files": "تعداد پرونده‌ها",
        "markaspatrolleddiff": "برچسب گشت بزن",
        "markaspatrolledtext": "به این صفحه برچسب گشت بزن",
+       "markaspatrolledtext-file": "انتخاب این نسخهٔ پرونده به عنوان گشت خورده",
        "markedaspatrolled": "برچسب گشت زده شد",
        "markedaspatrolledtext": "به نسخهٔ انتخاب شده از [[:$1]] برچسب گشت زده شد.",
        "rcpatroldisabled": "گشت‌زنی تغییرات اخیر غیرفعال است",
        "newimages-legend": "پالودن",
        "newimages-label": "نام پرونده (یا قسمتی از آن):",
        "newimages-showbots": "نمایش بارگذاری‌ها توسط ربات‌ها",
+       "newimages-hidepatrolled": "مخفی کردن بارگذاری گشت‌زن‌ها",
        "noimages": "چیزی برای دیدن نیست.",
        "ilsubmit": "جستجو",
        "bydate": "از روی تاریخ",
        "scarytranscludefailed-httpstatus": "[فراخوانی الگو برای $1 میسر نشد: خطای اچ‌تی‌تی‌پی $2]",
        "scarytranscludetoolong": "[نشانی اینترنتی مورد نظر (URL) بیش از اندازه بلند بود]",
        "deletedwhileediting": "'''هشدار''': این صفحه پس از اینکه شما آغاز به ویرایش آن کرده‌اید، حذف شده است!",
-       "confirmrecreate": "کاربر [[User:$1|$1]] ([[User talk:$1|بحث]]) این مقاله را پس از اینکه شما آغاز به ویرایش آن نموده‌اید به دلیل زیر حذف کرده است :\n: ''$2''\nلطفاً تأیید کنید که مجدداً می‌خواهید این مقاله را بسازید.",
-       "confirmrecreate-noreason": "کاربر [[User:$1|$1]] ([[User talk:$1|بحث]]) این صفحه را پس از شروع ویرایش‌تان پاک کرده‌است.  لطفاً تأیید کنید که شما واقعاً می‌خواهید آن را دوباره ایجاد کنید.",
+       "confirmrecreate": "کاربر [[User:$1|$1]] ([[User talk:$1|بحث]]) این مقاله را پس از اینکه شما آغاز به ویرایش آن نموده‌اید به دلیل زیر حذف کرده است :\n: ''$2'' \nلطفاً تأیید کنید که مجدداً می‌خواهید این مقاله را بسازید.",
+       "confirmrecreate-noreason": "کاربر [[User:$1|$1]] ([[User talk:$1|بحث]]) این صفحه را پس از شروع ویرایش‌تان {{GENDER:$1|پاک}} کرده‌است.  لطفاً تأیید کنید که شما واقعاً می‌خواهید آن را دوباره ایجاد کنید.",
        "recreate": "باز ایجاد",
        "confirm_purge_button": "تأیید",
        "confirm-purge-top": "پاک‌کردن نسخهٔ حافظهٔ نهانی (Cache) این صفحه را تأیید می‌کنید؟",
        "hebrew-calendar-m11-gen": "آب",
        "hebrew-calendar-m12-gen": "ایلول",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|بحث]])",
+       "timezone-local": "محلی",
        "duplicate-defaultsort": "هشدار: ترتیب پیش‌فرض «$2» ترتیب پیش‌فرض قبلی «$1» را باطل می‌کند.",
        "duplicate-displaytitle": "<strong>هشدار:</strong> نمایش عنوان \" $2 \"باعث ابطال پیش نمایش عنوان\" $1 \" می‌شود.",
        "invalid-indicator-name": "<strong>خطا:</strong>ویژگی های شاخص‌های وضعیت صفحهٔ <code>name</code> نباید خالی باشند.",
        "version-libraries-license": "اجازه‌نامه",
        "version-libraries-description": "توضیحات",
        "version-libraries-authors": "نویسندگان",
-       "redirect": "تغییرمسیر توسط پرونده، کاربر، صفحه یا شناسهٔ نسخه",
+       "redirect": "تغÛ\8cÛ\8cرÙ\85سÛ\8cر ØªÙ\88سط Ù¾Ø±Ù\88Ù\86دÙ\87Ø\8c Ú©Ø§Ø±Ø¨Ø±Ø\8c ØµÙ\81Ø­Ù\87 Û\8cا Ø³Û\8cاÙ\87Ù\87Ù\94 Ø´Ù\86اسÙ\87Ù\94 Ù\86سخÙ\87",
        "redirect-legend": "تغییرمسیر به یک پرونده یا صفحه",
-       "redirect-summary": "این صفحهٔ ویژه به پرونده (نام پرونده داده‌شده)، صفحه (شماره شناسهٔ صفحه یا شماره نسخهٔ داده‌شده) یا صفحهٔ کاربری (شناسهٔ عددی کاربری داده‌شده) تغییرمسیر می‌یابد. طرز استفاده: [[{{#Special:Redirect}}/file/Example.jpg]]، \n[[{{#Special:Redirect}}/page/64308]]، [[{{#Special:Redirect}}/revision/328429]] یا [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "این صفحهٔ ویژه به پرونده (نام پرونده داده‌شده)، صفحه (شماره شناسهٔ صفحه یا شماره نسخهٔ داده‌شده) یا صفحهٔ کاربری (شناسهٔ عددی کاربری داده‌شده) تغییرمسیر می‌یابد. طرز استفاده: [[{{#Special:Redirect}}/file/Example.jpg]]، \n[[{{#Special:Redirect}}/page/64308]]، [[{{#Special:Redirect}}/revision/328429]] یا [[{{#Special:Redirect}}/user/101]] یا\n[[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "برو",
        "redirect-lookup": "جستجو:",
        "redirect-value": "مقدار:",
        "redirect-page": "شناسهٔ صفحه",
        "redirect-revision": "نسخهٔ صفحه",
        "redirect-file": "نام پرونده",
+       "redirect-logid": "سیاهه شناسه",
        "redirect-not-exists": "مقدار پیدا نشد",
        "fileduplicatesearch": "جستجو برای پرونده‌های تکراری",
        "fileduplicatesearch-summary": "جستجو برای پرونده‌های تکراری بر اساس مقدار درهم‌شدهٔ آن‌ها صورت می‌گیرد.",
        "expand_templates_preview": "پیش‌نمایش",
        "expand_templates_preview_fail_html": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
        "expand_templates_preview_fail_html_anon": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
+       "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 بایت}} ($2؛ $3٪)",
+       "mediastatistics-bytespertype": "حجم کل پرونده این بخش: {{PLURAL:$1|$1 بایت|$1 بایت}} ($2; $3%)",
+       "mediastatistics-allbytes": "حجم کل همه پرونده برای همهٔ پرونده‌ها: {{PLURAL:$1|$1 بایت|$1 بایت}} ($2)",
        "mediastatistics-table-mimetype": "نوع مایم",
        "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-error-unknown": "مشکلی با جی‌سن بود. خطا: $1",
        "json-error-depth": "بیشینهٔ عمق پشته رد شده است",
        "mw-widgets-dateinput-no-date": "هیچ داده‌ای انتخاب نشده",
        "mw-widgets-titleinput-description-new-page": "این صفحه هنوز وجود ندارد",
        "mw-widgets-titleinput-description-redirect": "تغییر مسیر به $1",
-       "api-error-blacklisted": "لطفاً یک عنوان توصیفی متفاوت انتخاب کنید."
+       "api-error-blacklisted": "لطفاً یک عنوان توصیفی متفاوت انتخاب کنید.",
+       "sessionmanager-tie": "نمی‌توان چندین نوع درخواست هویت‌سنجی را ترکیب کرد: $1.",
+       "sessionprovider-generic": "$1 فصل",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "فصل‌های کوکی‌محور",
+       "sessionprovider-nocookies": "کوکی‌ها ممکن است غیر فعال شده باشند. اطمینان کسب کنید که کوکی‌ها را فعال کرده‌اید و دوباره آغاز کنید.",
+       "randomrootpage": "صفحهٔ ریشهٔ تصادفی"
 }
index c103b04..e2cc9ea 100644 (file)
        "october-date": "$1. lokakuuta",
        "november-date": "$1. marraskuuta",
        "december-date": "$1. joulukuuta",
+       "period-am": "epp.",
+       "period-pm": "jpp.",
        "pagecategories": "{{PLURAL:$1|Luokka|Luokat}}",
        "category_header": "Sivut, jotka ovat luokassa $1",
        "subcategories": "Alaluokat",
        "virus-scanfailed": "virustarkistus epäonnistui (virhekoodi $1)",
        "virus-unknownscanner": "tuntematon virustutka:",
        "logouttext": "<strong>Olet nyt kirjautunut ulos.</strong>\n\nOta huomioon, että jotkut sivut saattavat näkyä edelleen ikään kuin olisit vielä kirjautuneena sisään siihen saakka kunnes tyhjennät selaimesi välimuistin.",
+       "cannotlogoutnow-title": "Nyt ei voi kirjautua ulos",
+       "cannotlogoutnow-text": "Kirjautuminen ulos ei ole mahdollista käytettäessä $1.",
        "welcomeuser": "Tervetuloa $1!",
        "welcomecreation-msg": "Käyttäjätunnuksesi on luotu.\nVoit nyt muuttaa {{GRAMMAR:genitive|{{SITENAME}}}} [[Special:Preferences|asetuksia]] itsellesi.",
        "yourname": "Käyttäjänimi:",
        "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",
+       "cannotloginnow-text": "Kirjautuminen sisään ei ole mahdollista käytettäessä $1.",
        "yourdomainname": "Verkkonimi:",
        "password-change-forbidden": "Et voi muuttaa salasanoja tässä wikissä.",
        "externaldberror": "Tapahtui virhe ulkoisen autentikointitietokannan käytössä tai sinulla ei ole lupaa päivittää tunnustasi.",
        "resetpass_submit": "Aseta salasana ja kirjaudu sisään",
        "changepassword-success": "Salasanan vaihto onnistui.",
        "changepassword-throttled": "Olet tehnyt liian monta äskettäistä kirjautumisyritystä.\nOdota $1 ennen kuin yrität uudelleen.",
+       "botpasswords": "Botin salasanat",
+       "botpasswords-disabled": "Botin salasanat on poistettu käytöstä.",
        "resetpass_forbidden": "Salasanoja ei voi vaihtaa.",
        "resetpass-no-info": "Et voi nähdä tätä sivua kirjautumatta sisään.",
        "resetpass-submit-loggedin": "Muuta salasana",
        "passwordreset-emailtext-ip": "Joku (todennäköisesti sinä, IP-osoitteesta $1) pyysi salasanasi\nvaihtamista sivustolla {{SITENAME}} ($4). {{PLURAL:$3|Seuraava käyttäjätunnus on|Seuraavat käyttäjätunnukset ovat}}\nyhdistettynä tähän sähköpostiosoitteeseen:\n\n$2\n\n{{PLURAL:$3|Tämä väliaikainen salasana vanhentuu|Nämä väliaikaiset salasanat vanhentuvat}} {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\nKirjaudu sisään nyt ja valitse uusi salasana heti. Jos joku toinen teki tämän pyynnön \ntai jos muistitkin vanhan salasanasi etkä halua enää muuttaa sitä,\nvoit jättää tämän viestin huomiotta ja jatkaa vanhan salasanasi käyttämistä.",
        "passwordreset-emailtext-user": "Käyttäjä $1 pyysi muistutusta tunnuksesi tiedoista sivustolla {{SITENAME}} ($4).\n{{PLURAL:$3|Seuraava käyttäjätunnus on|Seuraavat käyttäjätunnukset ovat}} liitetty tähän sähköpostiosoitteeseen:\n\n$2\n\n{{PLURAL:$3|Tämä väliaikainen salasana vanhentuu|Nämä väliaikaiset salasanat vanhentuvat}} {{PLURAL:$5|yhden päivän|$5 päivän}} kuluttua.\nSinun kannattaa kirjautua sisään ja valita uusi salasana. Jos joku toinen teki tämän\npyynnön, tai muistat sittenkin vanhan salasanasi, etkä halua muuttaa sitä,\nvoit jättää tämän viestin huomiotta ja jatkaa vanhan salasanan käyttöä.",
        "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-emailsentemail": "Mikäli tämä sähköpostiosoite on liitetty tiliisi, salasanan nollaamisviesti lähetetään.",
+       "passwordreset-emailsentusername": "Mikäli tähän käyttäjänimeen liitetty sähköpostiosoite löytyy, salasanan nollaamisviesti 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",
        "changeemail": "Muuta tai poista sähköpostiosoite",
        "userrights": "Käyttöoikeuksien hallinta",
        "userrights-lookup-user": "Hallinnoi käyttäjän ryhmiä",
        "userrights-user-editname": "Käyttäjätunnus",
-       "editusergroup": "Muokkaa käyttäjän ryhmiä",
+       "editusergroup": "Muokkaa {{GENDER:$1|käyttäjän}} ryhmiä",
        "editinguser": "Muutetaan {{GENDER:$1|käyttäjän}} <strong>[[User:$1|$1]]</strong> $2 oikeuksia",
        "userrights-editusergroup": "Muuta käyttäjän ryhmiä",
-       "saveusergroups": "Tallenna nämä käyttäjäryhmät",
+       "saveusergroups": "Tallenna {{GENDER:$1|käyttäjän}} ryhmät",
        "userrights-groupsmember": "Jäsenenä ryhmissä:",
        "userrights-groupsmember-auto": "Automaattisesti jäsenenä ryhmissä:",
        "userrights-groups-help": "Voit muuttaa ryhmiä, joissa tämä käyttäjä on.\n* Merkattu valintaruutu tarkoittaa, että käyttäjä on kyseisessä ryhmässä.\n* Merkkaamaton valintaruutu tarkoittaa, että käyttäjä ei ole kyseisessä ryhmässä.\n* <nowiki>*</nowiki> tarkoittaa, että et pysty poistamaan käyttäjää tästä ryhmästä, kun olet hänet siihen lisännyt tai päinvastoin",
        "right-createpage": "Luoda sivuja (jotka eivät ole keskustelusivuja)",
        "right-createtalk": "Luoda keskustelusivuja",
        "right-createaccount": "Luoda uusia käyttäjätunnuksia",
+       "right-autocreateaccount": "Kirjautua sisään automaattisesti ulkopuolisen käyttäjätunnuksen kautta",
        "right-minoredit": "Merkitä muokkauksensa pieniksi",
        "right-move": "Siirtää sivuja",
        "right-move-subpages": "Siirtää sivuja alasivuineen",
        "right-blockemail": "Estää käyttäjää lähettämästä sähköpostia",
        "right-hideuser": "Estää käyttäjätunnus ja piilottaa se näkyvistä",
        "right-ipblock-exempt": "Ohittaa IP-, automaattiset ja osoitealue-estot",
-       "right-proxyunbannable": "Ohittaa automaattiset välityspalvelinestot",
        "right-unblockself": "Poistaa esto itseltään",
        "right-protect": "Muuttaa suojaustasoja ja muokata tarttuvasti suojattuja sivuja",
        "right-editprotected": "Muokata sivuja, jotka on suojattu tasolle \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Luoda ja poistaa [[Special:Tags|merkkauksia]] tietokannasta",
        "right-applychangetags": "Asettaa [[Special:Tags|merkkauksia]] omien muutosten yhteyteen",
        "right-changetags": "Lisätä ja poistaa satunnaisia [[Special:Tags|merkkauksia]] yksittäisissä sivuversioissa tai lokimerkinnöissä",
+       "grant-generic": "\"$1\" oikeuksien joukko",
+       "grant-group-email": "Lähettää sähköpostia",
+       "grant-group-high-volume": "Suorittaa suuri määrä toimintoja",
+       "grant-group-customization": "Mukauttaminen ja asetukset",
+       "grant-group-administration": "Suorittaa ylläpitoon liittyviä toimintoja",
+       "grant-group-other": "Sekalainen toiminta",
+       "grant-blockusers": "Estää käyttäjiä muokkaamasta ja poistaa estoja",
+       "grant-createaccount": "Luoda käyttäjätunnuksia",
+       "grant-createeditmovepage": "Luoda, muokata ja siirtää sivuja",
+       "grant-delete": "Poistaa sivuja, yksittäisiä versioita ja lokimerkintöjä",
+       "grant-editinterface": "Muokata järjestelmäviesti-nimiavaruutta ja käyttäjien CSS/JavaScript-sivuja",
+       "grant-editmycssjs": "Muokata käyttäjän omia CSS/JavaScript-sivuja",
+       "grant-editmyoptions": "Muokata käyttäjän omia asetuksia",
+       "grant-editmywatchlist": "Muokata omaa tarkkailulistaa",
+       "grant-editpage": "Muokata olemassa olevia sivuja",
+       "grant-editprotected": "Muokata suojattuja sivuja",
+       "grant-highvolume": "Suorittaa paljon muokkauksia",
+       "grant-oversight": "Piilottaa käyttäjiä ja häivyttää yksittäisiä versioita",
+       "grant-patrol": "Partioida sivuihin tehtyjä muutoksia",
+       "grant-protect": "Suojata sivuja tai poistaa suojauksia",
+       "grant-rollback": "Peräyttää sivuun tehdyt muutokset",
+       "grant-sendemail": "Lähettää sähköpostia toisille käyttäjille",
+       "grant-uploadeditmovefile": "Tallentaa, korvata ja siirtää tiedostoja",
+       "grant-uploadfile": "Tallentaa uusia tiedostoja",
+       "grant-basic": "Perustason oikeudet",
+       "grant-viewdeleted": "Näe poistetut tiedostot ja sivut",
+       "grant-viewmywatchlist": "Näe oma tarkkailulistasi",
        "newuserlogpage": "Uudet käyttäjät",
        "newuserlogpagetext": "Tämä on loki luoduista käyttäjätunnuksista.",
        "rightslog": "Käyttöoikeusloki",
        "action-createpage": "luoda sivuja",
        "action-createtalk": "luoda keskustelusivuja",
        "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",
        "action-minoredit": "merkitä tätä muokkausta pieneksi",
        "action-move": "siirtää tätä sivua",
        "wantedcategories": "Halutut luokat",
        "wantedpages": "Halutut sivut",
        "wantedpages-summary": "Luettelo olemattomista sivuista, joihin johtaa eniten linkkejä. Luettelossa ei kuitenkaan ole sellaisia sivuja, joihin johtaa ainoastaan uudelleenohjauksia. Jos haluat nähdä luettelon niistä olemattomista sivuista, joihin on linkki uudelleenohjauksista, katso [[{{#special:BrokenRedirects}}|luettelo virheellisistä ohjauksista]].",
-       "wantedpages-badtitle": "Virheellinen otsikko tuloksissa: $1",
+       "wantedpages-badtitle": "Kelvoton sivun nimi tulosten joukossa: $1",
        "wantedfiles": "Halutut tiedostot",
        "wantedfiletext-cat": "Seuraavia tiedostoja käytetään, mutta niitä ei ole olemassa. Ulkopuolissa mediavarastoissa olevat tiedostot voivat näkyä tällä listalla, vaikka ne ovat olemassa. Tällaiset väärät merkinnät on <del>yliviivattu</del>. Lisäksi sellaiset sivut, joihin on sisällytetty tiedostoja, jotka eivät ole olemassa, on luetteloitu [[:$1|täällä]].",
        "wantedfiletext-cat-noforeign": "Seuraavat tiedostot ovat käytössä vaikka niitä ei ole olemassa. Luettelo sellaisista sivuista, joihin on upotettu olemattomia tiedostoja, on [[:$1]].",
        "wlshowhideanons": "anonyymit käyttäjät",
        "wlshowhidepatr": "tarkastetut muutokset",
        "wlshowhidemine": "omat muokkaukseni",
+       "wlshowhidecategorization": "sivujen luokkien muutokset",
        "watchlist-options": "Tarkkailulistan asetukset",
        "watching": "Lisätään tarkkailulistalle...",
        "unwatching": "Poistetaan tarkkailulistalta...",
        "unblock": "Poista käyttäjän esto",
        "blockip": "Estä {{GENDER:$1|käyttäjä}}",
        "blockip-legend": "Estä käyttäjä",
-       "blockiptext": "Tällä toiminnolla voit estää käyttäjätunnusta tai IP-osoitetta muokkaamasta.<br />\nTällainen muokkausesto pitäisi asettaa vain vandalismin torjumiseksi ja [[{{MediaWiki:Policy-url}}|käytännön]] mukaisesti.\nKirjoita eston syy alla olevaan kenttään.",
+       "blockiptext": "Tällä toiminnolla voit estää käyttäjätunnusta tai IP-osoitetta muokkaamasta.<br />\nTällainen muokkausesto pitäisi asettaa vain vandalismin torjumiseksi ja [[{{MediaWiki:Policy-url}}|käytännön]] mukaisesti.\nKirjoita eston syy alla olevaan kenttään.\nVoit estää IP-osoiteavaruuksia käyttämällä [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-syntaksia; suurin sallittu alue on /$1 protokollalle IPv4 ja /$2 protokollalle IPv6.",
        "ipaddressorusername": "IP-osoite tai käyttäjätunnus:",
        "ipbexpiry": "Vanhentuu:",
        "ipbreason": "Syy:",
        "block-log-flags-hiddenname": "käyttäjänimi piilotettu",
        "range_block_disabled": "Ylläpitäjien mahdollisuus asettaa avaruusestoja on poistettu käytöstä.",
        "ipb_expiry_invalid": "Virheellinen päättymisaika.",
+       "ipb_expiry_old": "Vanhentumisaika on menneisyyttä.",
        "ipb_expiry_temp": "Piilotettujen käyttäjätunnusten estojen tulee olla pysyviä.",
        "ipb_hide_invalid": "Tämän tunnuksen piilottaminen ei onnistu; sillä on enemmän kuin {{PLURAL:$1|yksi muokkaus|$1 muokkausta}}.",
        "ipb_already_blocked": "”$1” on jo estetty.",
        "export-download": "Tallenna tiedostona",
        "export-templates": "Ota mukaan mallineet",
        "export-pagelinks": "Sisällytä linkkien kohteina olevat sivut syvyydelle:",
+       "export-manual": "Lisää sivuja manuaalisesti:",
        "allmessages": "Järjestelmäviestit",
        "allmessagesname": "Nimi",
        "allmessagesdefault": "Viestin tekstin perusmuoto",
        "javascripttest-pagetext-frameworks": "Valitse yksi seuraavista testausalustoista: $1",
        "javascripttest-pagetext-skins": "Valitse testauksessa käytettävä ulkoasu",
        "javascripttest-qunit-intro": "Katso [$1 testausohjeet] mediawiki.orgissa.",
-       "tooltip-pt-userpage": "Oma käyttäjäsivu",
+       "tooltip-pt-userpage": "{{GENDER:|Oma käyttäjäsivusi}}",
        "tooltip-pt-anonuserpage": "IP-osoitteesi käyttäjäsivu",
-       "tooltip-pt-mytalk": "Oma keskustelusivu",
+       "tooltip-pt-mytalk": "{{GENDER:|Oma keskustelusivusi}}",
        "tooltip-pt-anontalk": "Keskustelu tämän IP-osoitteen muokkauksista",
-       "tooltip-pt-preferences": "Omat asetukset",
+       "tooltip-pt-preferences": "{{GENDER:|Omat asetuksesi}}",
        "tooltip-pt-watchlist": "Lista sivuista, joiden muokkauksia tarkkailet",
-       "tooltip-pt-mycontris": "Lista omista muokkauksista",
+       "tooltip-pt-mycontris": "Luettelo {{GENDER:|omista muokkauksistasi}}",
        "tooltip-pt-anoncontribs": "Luettelo tästä IP-osoitteesta tehdyistä muokkauksista",
        "tooltip-pt-login": "Kirjaudu sisään tai luo tunnus",
        "tooltip-pt-logout": "Kirjaudu ulos",
        "tooltip-t-recentchangeslinked": "Viimeisimmät muokkaukset sivuissa, joille viitataan tältä sivulta",
        "tooltip-feed-rss": "RSS-syöte tälle sivulle",
        "tooltip-feed-atom": "Atom-syöte tälle sivulle",
-       "tooltip-t-contributions": "Näytä lista tämän käyttäjän muokkauksista",
-       "tooltip-t-emailuser": "Lähetä sähköpostia tälle käyttäjälle",
+       "tooltip-t-contributions": "Näytä lista {{GENDER:$1|tämän käyttäjän}} muokkauksista",
+       "tooltip-t-emailuser": "Lähetä sähköpostia {{GENDER:$1|tälle käyttäjälle}}",
        "tooltip-t-info": "Enemmän tietoa tästä sivusta",
        "tooltip-t-upload": "Tallenna tiedostoja",
        "tooltip-t-specialpages": "Näytä toimintosivut",
        "pageinfo-category-files": "Tiedostojen määrä",
        "markaspatrolleddiff": "Merkitse tarkastetuksi",
        "markaspatrolledtext": "Merkitse muutos tarkastetuksi",
+       "markaspatrolledtext-file": "Merkitse tämä tiedoston versio tarkastetuksi",
        "markedaspatrolled": "Muutos on tarkastettu",
        "markedaspatrolledtext": "Valittu versio sivusta [[:$1]] on merkitty tarkastetuksi.",
        "rcpatroldisabled": "Tuoreiden muutosten tarkastustoiminto ei ole käytössä",
        "newimages-legend": "Suodatin",
        "newimages-label": "Tiedostonimi (tai osa siitä)",
        "newimages-showbots": "Näytä bottien tekemät tallennukset",
+       "newimages-hidepatrolled": "Piilota tarkastetut tiedostotallennukset",
        "noimages": "Ei uusia tiedostoja.",
        "ilsubmit": "Hae",
        "bydate": "päiväyksen mukaan",
        "scarytranscludefailed-httpstatus": "[Mallineen hakeminen epäonnistui: $1 HTTP $2]",
        "scarytranscludetoolong": "[Verkko-osoite on liian pitkä]",
        "deletedwhileediting": "'''Varoitus''': Tämä sivu on poistettu sen jälkeen, kun aloitit sen muokkaamisen!",
-       "confirmrecreate": "Käyttäjä '''[[User:$1|$1]]''' ([[User talk:$1|keskustelu]]) on poistanut sivun sen jälkeen, kun aloit muokata sitä. Syy oli:\n: ''$2''\nVarmista, että haluat luoda sivun uudelleen.",
-       "confirmrecreate-noreason": "Käyttäjä '''[[User:$1|$1]]''' ([[User talk:$1|keskustelu]]) on poistanut tämän sivun sen jälkeen, kun aloit muokata sitä. \nVarmista, että haluat luoda sivun uudelleen.",
+       "confirmrecreate": "Käyttäjä [[User:$1|$1]] ([[User talk:$1|keskustelu]]) {{GENDER:$1|on poistanut}} tämän sivun sen jälkeen, kun olet alkanut muokata sitä. Syy poistoon on:\n: <em>$2</em>\nVarmista, että haluat todella luoda tämän sivun uudelleen.",
+       "confirmrecreate-noreason": "Käyttäjä [[User:$1|$1]] ([[User talk:$1|keskustelu]]) {{GENDER:$1|on poistanut}} tämän sivun sen jälkeen, kun aloit muokata. Varmista, että haluat tosiaan luoda sivun uudelleen.",
        "recreate": "Luo uudelleen",
        "unit-pixel": " px",
        "confirm_purge_button": "Poista",
        "watchlisttools-edit": "Katso ja muokkaa tarkkailulistaa",
        "watchlisttools-raw": "Muokkaa listaa raakamuodossa",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|keskustelu]])",
+       "timezone-local": "Paikallinen",
        "duplicate-defaultsort": "'''Varoitus:''' Oletuslajitteluavain ”$2” korvaa aiemman oletuslajitteluavaimen ”$1”.",
        "duplicate-displaytitle": "<strong>Varoitus:</strong> Näytettävä otsikko \"$2\" päällekirjoittaa edellisen otsikon \"$1\".",
        "invalid-indicator-name": "<strong>Virhe:</strong> Sivun tilan osoittimien attribuutti <code>name</code> ei saa olla tyhjä.",
        "version-libraries-license": "Lisenssi",
        "version-libraries-description": "Kuvaus",
        "version-libraries-authors": "Tekijät",
-       "redirect": "Ohjaus tiedoston, käyttäjän, sivun tai version tunnisteen mukaan",
+       "redirect": "Ohjaus tiedoston, käyttäjän, sivun, sivuversion tai lokin tunnistenumeron mukaan",
        "redirect-legend": "Ohjaus tiedostoon tai sivulle",
-       "redirect-summary": "Tämä toimintosivu ohjaa tiedostoon (tiedostonimen mukaan), sivulle (version numeron tai sivun tunnisteen mukaan) tai käyttäjäsivulle (käyttäjän numeron mukaan). Käyttö: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] tai [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Tämä toimintosivu ohjaa tiedostoon (tiedostonimen mukaan), sivulle (version tunnistenumeron tai sivun tunnistenumeron mukaan), käyttäjäsivulle (käyttäjän tunnistenumeron mukaan) taikka lokimerkintään (lokin tunnistenumeron mukaan). Käytetään seuraavasti: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]],[[{{#Special:Redirect}}/user/101]] tai [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Siirry",
        "redirect-lookup": "Hae:",
        "redirect-value": "Arvo:",
-       "redirect-user": "Käyttäjän tunnusnumero",
+       "redirect-user": "Käyttäjän tunnistenumero",
        "redirect-page": "Sivun tunnistenumero",
        "redirect-revision": "Sivun versio",
        "redirect-file": "Tiedostonimi",
+       "redirect-logid": "Lokin tunnistenumero",
        "redirect-not-exists": "Arvoa ei löytynyt",
        "fileduplicatesearch": "Kaksoiskappaleiden haku",
        "fileduplicatesearch-summary": "Etsii tiedoston kaksoiskappaleita hajautusarvon perusteella.",
        "expand_templates_preview": "Esikatselu",
        "expand_templates_preview_fail_html": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska istunnon tiedot ovat kadonneet, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, yritä uudestaan.</strong>\nJos esikatselu ei vieläkään toimi, kokeile [[Special:UserLogout|kirjautua ulos]] ja sen jälkeen kirjaudu uudestaan sisään.",
        "expand_templates_preview_fail_html_anon": "<em>Koska sivustolla {{SITENAME}} on käytössä puhdas HTML-koodi ja koska et ole kirjautunut sisään, esikatselu on piilotettu JavaScript-hyökkäyksien torjumiseksi.</em>\n\n<strong>Jos olet oikealla asialla, [[Special:UserLogin|kirjaudu sisään]] ja yritä uudestaan.</strong>",
+       "expand_templates_input_missing": "Sinun on annettava edes jotakin tekstiä syötteeksi.",
        "pagelanguage": "Sivun kielen valinta",
        "pagelang-name": "Sivu",
        "pagelang-language": "Kieli",
        "pagelang-use-default": "Käytä oletuskieltä",
        "pagelang-select-lang": "Valitse kieli",
+       "pagelang-submit": "Lähetä",
        "right-pagelang": "Vaihtaa sivun kieli",
        "action-pagelang": "muuttaa sivun kieliasetuksia",
        "log-name-pagelang": "Kielenvaihtoloki",
        "mediastatistics": "Median tilastotiedot",
        "mediastatistics-summary": "Tietoja tallennettujen tiedostojen tyypeistä. Luettelossa ovat ainoastaan tiedostojen uusimmat versiot eikä lainkaan vanhoja tai poistettuja versioita.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 tavu|$1 tavua}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Tiedostojen yhteenlaskettu koko tässä osiossa: {{PLURAL:$1|$1 tavu|$1 tavua}} ($2; $3%).",
+       "mediastatistics-allbytes": "Kaikkien tiedostojen yhteenlaskettu koko: {{PLURAL:$1|$1 tavu|$1 tavua}} ($2).",
        "mediastatistics-table-mimetype": "MIME-tyyppi",
        "mediastatistics-table-extensions": "Tiedostopäätteet",
        "mediastatistics-table-count": "Tiedostojen lukumäärä",
        "mediastatistics-header-text": "Tekstitiedostot",
        "mediastatistics-header-executable": "Ohjelmatiedostot",
        "mediastatistics-header-archive": "Pakatussa muodossa",
+       "mediastatistics-header-total": "Kaikki tiedostot",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|jäljelle jäänyt pilkku|jäljelle jäänyttä pilkkua}} poistettiin JSON-tekstistä.",
        "json-error-unknown": "Syntyi ongelma JSONin kanssa. Virhe: $1",
        "json-error-depth": "Suurin mahdollinen pinosyvyys (stack depth) on ylitetty",
        "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."
+       "api-error-blacklisted": "Valitse toinen, kuvaava nimi.",
+       "sessionprovider-generic": "$1 istuntoa",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "istuntoja, joissa on evästeet käytössä",
+       "sessionprovider-nocookies": "Evästeet on voitu poistaa käytöstä. Varmista, että sinulla on evästeet käytössä ja yritä sitten uudelleen.",
+       "randomrootpage": "Satunnainen juurisivu"
 }
index f31cbad..efdbe3c 100644 (file)
                        "L",
                        "SRXcraft",
                        "StevenJ81",
-                       "The RedBurn"
+                       "The RedBurn",
+                       "Fredlefred"
                ]
        },
        "tog-underline": "Soulignement des liens :",
        "october-gen": "octobre",
        "november-gen": "novembre",
        "december-gen": "décembre",
-       "jan": "janv",
+       "jan": "jan",
        "feb": "fév",
-       "mar": "mars",
+       "mar": "mar",
        "apr": "avr",
        "may": "mai",
        "jun": "juin",
        "jul": "juil",
        "aug": "août",
-       "sep": "sept",
+       "sep": "sep",
        "oct": "oct",
        "nov": "nov",
        "dec": "déc",
        "october-date": "$1 octobre",
        "november-date": "$1 novembre",
        "december-date": "$1 décembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Catégorie|Catégories}}",
        "category_header": "Pages dans la catégorie « $1 »",
        "subcategories": "Sous-catégories",
        "history_short": "Historique",
        "updatedmarker": "modifié depuis ma dernière visite",
        "printableversion": "Version imprimable",
-       "permalink": "Adresse de cette version",
+       "permalink": "Adresse permanente",
        "print": "Imprimer",
        "view": "Lire",
        "view-foreign": "Voir sur $1",
        "redirectedfrom": "(Redirigé depuis $1)",
        "redirectpagesub": "Page de redirection",
        "redirectto": "Rediriger vers :",
-       "lastmodifiedat": "Dernière modification de cette page le $1 à $2.",
+       "lastmodifiedat": "Dernière modification de cette page le $1, à $2.",
        "viewcount": "Cette page a été consultée {{PLURAL:$1|une fois|$1 fois}}.",
        "protectedpage": "Page protégée",
        "jumpto": "Aller à :",
        "virus-scanfailed": "Échec de la recherche (code $1)",
        "virus-unknownscanner": "antivirus inconnu :",
        "logouttext": "'''Vous êtes à présent déconnecté{{GENDER:||e}}.'''\n\nNotez que certaines pages peuvent être encore affichées comme si vous étiez toujours connecté, jusqu’à ce que vous effaciez le cache de votre navigateur.",
+       "cannotlogoutnow-title": "Impossible de se déconnecter maintenant",
+       "cannotlogoutnow-text": "La déconnexion n’est pas possible en utilisant $1.",
        "welcomeuser": "Bienvenue, $1&nbsp;!",
        "welcomecreation-msg": "Votre compte a été créé.\nN'oubliez pas de modifier [[Special:Preferences|vos préférences pour {{SITENAME}}]].",
        "yourname": "Nom d'utilisateur :",
        "remembermypassword": "Me reconnecter automatiquement lors des prochaines visites avec ce navigateur (au maximum $1&nbsp;{{PLURAL:$1|jour|jours}})",
        "userlogin-remembermypassword": "Garder ma session active",
        "userlogin-signwithsecure": "Utiliser une connexion sécurisée",
+       "cannotloginnow-title": "Impossible de se connecter maintenant",
+       "cannotloginnow-text": "La connexion n’est pas possible en utilisant $1.",
        "yourdomainname": "Votre domaine :",
        "password-change-forbidden": "Vous ne pouvez pas modifier les mots de passe sur ce wiki.",
        "externaldberror": "Une erreur s'est produite avec la base de données d'authentification externe, ou bien vous ne pouvez pas mettre à jour votre compte externe.",
        "resetpass_submit": "Changer le mot de passe et se connecter",
        "changepassword-success": "Votre mot de passe a été changé avec succès !",
        "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-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-createnew": "Créer un nouveau mot de passe de robots",
+       "botpasswords-editexisting": "Modifier un mot de passe de robots existant",
+       "botpasswords-label-appid": "Nom du robot :",
+       "botpasswords-label-create": "Créer",
+       "botpasswords-label-update": "Mettre à jour",
+       "botpasswords-label-cancel": "Annuler",
+       "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-label-restrictions": "Restrictions d’utilisation :",
+       "botpasswords-label-grants-column": "Accordé",
+       "botpasswords-bad-appid": "Le nom de robot « $1 » n’est pas valide.",
+       "botpasswords-insert-failed": "Échec de l’ajout du nom de robot « $1 ». A-t-il déjà été ajouté ?",
+       "botpasswords-update-failed": "Échec à la mise à jour du nom de robot « $1 ». A-t-il déjà été supprimé ?",
+       "botpasswords-created-title": "Mot de passe de robots créé",
+       "botpasswords-created-body": "Le mot de passe de robots « $1 » a bien été créé.",
+       "botpasswords-updated-title": "Mot de passe de robots mis à jour",
+       "botpasswords-updated-body": "Le mot de passe de robots « $1 » a bien été mis à jour.",
+       "botpasswords-deleted-title": "Mot de passe de robots supprimé",
+       "botpasswords-deleted-body": "Le mot de passe de robots « $1 » a été supprimé.",
+       "botpasswords-newpassword": "Le nouveau mot de passe pour se connecter avec <strong>$1</strong> est <strong>$2</strong>. <em>Veuillez l’enregistrer pour y faire référence ultérieurement.</em>",
+       "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 ».",
        "resetpass_forbidden": "Les mots de passe ne peuvent pas être changés",
        "resetpass-no-info": "Vous devez être connecté pour avoir accès à cette page.",
        "resetpass-submit-loggedin": "Changer de mot de passe",
        "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-emailsentemail": "Si c’est une adresse de courriel enregistrée pour votre compte, alors un courriel de réinitialisation de mot de passe sera envoyé.",
-       "passwordreset-emailsentusername": "S’il y a une adresse de courriel enregistrée pour ce compte, alors un courriel de réinitialisation de mot de passe sera envoyé.",
+       "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",
        "changeemail": "Changer ou supprimer l’adresse de courriel",
        "revision-info": "Révision de $1 par {{GENDER:$6|$2}}$7",
        "previousrevision": "← Version précédente",
        "nextrevision": "Version suivante →",
-       "currentrevisionlink": "Voir la version courante",
+       "currentrevisionlink": "Voir la version actuelle",
        "cur": "actu",
        "next": "suivant",
        "last": "diff",
        "userrights": "Gestion des droits des utilisateurs",
        "userrights-lookup-user": "Gestion des groupes d'utilisateurs",
        "userrights-user-editname": "Entrez un nom d'utilisateur :",
-       "editusergroup": "Modification des groupes d'utilisateurs",
+       "editusergroup": "Modification des groupes d’{{GENDER:$1|utilisateurs}}",
        "editinguser": "Modification des droits de l’{{GENDER:$1|utilisateur|utilisatrice}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Modifier les groupes de l'utilisateur",
-       "saveusergroups": "Enregistrer les groupes de l'utilisateur",
+       "saveusergroups": "Enregistrer les groupes de l’{{GENDER:$1|utilisateur|utilisatrice}}",
        "userrights-groupsmember": "Membre de :",
        "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.",
        "right-createpage": "Créer des pages (qui ne sont pas des pages de discussion)",
        "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-move": "Renommer des pages",
        "right-move-subpages": "Renommer des pages avec leurs sous-pages",
        "right-blockemail": "Empêcher un utilisateur d'envoyer des courriels",
        "right-hideuser": "Bloquer un utilisateur en masquant son nom au public",
        "right-ipblock-exempt": "Ne pas être affecté par les IP bloquées, les blocages automatiques et les blocages de plages d'IP",
-       "right-proxyunbannable": "Ne pas être affecté par les blocages automatiques de serveurs mandataires",
        "right-unblockself": "Se débloquer soi-même",
        "right-protect": "Modifier les niveaux de protection et modifier les pages protégées en cascade",
        "right-editprotected": "Modifier les pages protégées avec « {{int:protect-level-sysop}} »",
        "right-managechangetags": "Créer et supprimer des [[Spécial:Balises|balises]] de la base de données",
        "right-applychangetags": "Appliquer [[Special:Tags|les balises]] avec ses propres modifications",
        "right-changetags": "Ajouter et supprimer de façon arbitraire [[Special:Tags|des balises]] sur des révisions individuelles et des entrées de journal",
+       "grant-generic": "ensemble de droits « $1 »",
+       "grant-group-page-interaction": "Interagir avec des pages",
+       "grant-group-file-interaction": "Interagir avec des médias",
+       "grant-group-watchlist-interaction": "Interagir avec votre liste de suivi",
+       "grant-group-email": "Envoyer un courriel",
+       "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-other": "Activités diverses",
+       "grant-blockusers": "Bloquer et débloquer des utilisateurs",
+       "grant-createaccount": "Créer des comptes",
+       "grant-createeditmovepage": "Créer, modifier et déplacer des pages",
+       "grant-delete": "Supprimer les pages, les révisions et les entrées du journal",
+       "grant-editinterface": "Modifier l’espace de noms MédiaWiki et le CSS/JavaScript utilisateur",
+       "grant-editmycssjs": "Modifier votre CSS/JavaScript utilisateur",
+       "grant-editmyoptions": "Modifier vos préférences utilisateur",
+       "grant-editmywatchlist": "Modifier votre liste de suivi",
+       "grant-editpage": "Modifier des pages existantes",
+       "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-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",
+       "grant-uploadeditmovefile": "Télécharger, remplacer et renommer des fichiers",
+       "grant-uploadfile": "Importer de nouveaux fichiers",
+       "grant-basic": "Droits de base",
+       "grant-viewdeleted": "Afficher les fichiers et pages supprimés",
+       "grant-viewmywatchlist": "Afficher votre liste de suivi",
        "newuserlogpage": "Journal des créations de comptes utilisateur",
        "newuserlogpagetext": "Cette page affiche l’historique des créations de comptes utilisateur.",
        "rightslog": "Journal des modifications de droits d’utilisateurs",
        "action-createpage": "créer des pages",
        "action-createtalk": "créer des pages de discussion",
        "action-createaccount": "créer ce compte utilisateur",
+       "action-autocreateaccount": "créer automatiquement ce compte utilisateur externe",
        "action-history": "afficher l’historique de cette page",
        "action-minoredit": "marquer cette modification comme mineure",
        "action-move": "renommer cette page",
        "upload-form-label-select-file": "Sélectionner un fichier",
        "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-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-usage-filename": "Nom du fichier",
        "foreign-structured-upload-form-label-own-work": "Je suis l’auteur de cette œuvre",
        "log-title-wildcard": "Chercher parmi les titres commençant par ce texte",
        "showhideselectedlogentries": "Afficher/masquer les entrées de journal sélectionnées",
        "log-edit-tags": "Modifier les balises des entrées de journal sélectionnées",
+       "checkbox-select": "Sélectionner : $1",
+       "checkbox-all": "Tout",
+       "checkbox-none": "Aucun",
+       "checkbox-invert": "Inverser",
        "allpages": "Toutes les pages",
        "nextpage": "Page suivante ($1)",
        "prevpage": "Page précédente ($1)",
        "listgrouprights-namespaceprotection-header": "Restrictions d'espace de noms",
        "listgrouprights-namespaceprotection-namespace": "Espace de noms",
        "listgrouprights-namespaceprotection-restrictedto": "Droit(s) permettant à l'utilisateur de modifier",
+       "listgrants": "Autorisations",
+       "listgrants-summary": "Voici une liste des droits avec leur accès associé aux droits utilisateur. Les utilisateurs peuvent autoriser les applications à utiliser leur compte, mais avec des droits limités d’après les droits que l’utilisateur a donnés à l’application. Un application agissant au nom d’un utilisateur ne peut toutefois pas, de fait, utiliser des droits que l’utilisateur ne possède pas.\nIl peut y avoir [[{{MediaWiki:Listgrouprights-helppage}}|plus d’information]] sur les droits individuels.",
+       "listgrants-grant": "Accorder",
+       "listgrants-rights": "Droits",
        "trackingcategories": "Catégories de suivi",
        "trackingcategories-summary": "Cette page liste les catégories de suivi qui sont remplies automatiquement par [[MediaWiki]]. Leurs noms peuvent être changés en modifiant les messages systèmes correspondants dans l’espace de noms {{ns:8}}.",
        "trackingcategories-msg": "Catégorie de suivi",
        "wlshowhideanons": "utilisateurs anonymes",
        "wlshowhidepatr": "modifications relues",
        "wlshowhidemine": "mes modifications",
+       "wlshowhidecategorization": "catégorisation de la page",
        "watchlist-options": "Options de la liste de suivi",
        "watching": "Suivi…",
        "unwatching": "Fin du suivi…",
        "unblock": "Débloquer l’utilisateur",
        "blockip": "Bloquer l’{{GENDER:$1|utilisateur|utilisatrice}}",
        "blockip-legend": "Bloquer l’utilisateur",
-       "blockiptext": "Utilisez le formulaire ci-dessous pour bloquer les tentatives de modification faites à partir d’une adresse IP spécifique ou d’un nom d’utilisateur.\nUne telle mesure ne devrait être prise que pour prévenir le vandalisme et en accord avec les [[{{MediaWiki:Policy-url}}|règles internes]].\nDonnez ci-dessous un motif précis (par exemple en citant les pages qui ont été vandalisées).",
+       "blockiptext": "Utilisez le formulaire ci-dessous pour bloquer les tentatives de modification faites à partir d’une adresse IP spécifique ou d’un nom d’utilisateur.\nUne telle mesure ne devrait être prise que pour prévenir le vandalisme et en accord avec les [[{{MediaWiki:Policy-url}}|règles internes]].\nDonnez ci-dessous un motif précis (par exemple en citant les pages qui ont été vandalisées).\nVous pouvez bloquer des plages d’adresses IP en utilisant la syntaxe [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] ; la plus grande plage autorisée est /$1 pour IP v4 et /$2 pour IP v6.",
        "ipaddressorusername": "Adresse IP ou nom d'utilisateur :",
        "ipbexpiry": "Durée avant expiration :",
        "ipbreason": "Motif :",
        "block-log-flags-hiddenname": "nom d’utilisateur masqué",
        "range_block_disabled": "Le droit administrateur de créer des blocages de plages IP est désactivé.",
        "ipb_expiry_invalid": "Durée d'expiration incorrecte.",
+       "ipb_expiry_old": "L’heure d’expiration est passée.",
        "ipb_expiry_temp": "Les blocages de noms d'utilisateurs cachés doivent être permanents.",
        "ipb_hide_invalid": "Impossible de supprimer ce compte ; il semble avoir plus {{PLURAL:$1|d’une modification|de $1 modifications}}.",
        "ipb_already_blocked": "« $1 » est déjà bloqué",
        "lockedbyandtime": "(par $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 '''pas''' 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'''Attention !'''\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-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 '''pas''' 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'''Attention !'''\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. 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-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</stong> 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 !</stong>\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é.",
        "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.",
        "movenosubpage": "Cette page n'a aucune sous-page.",
        "movereason": "Motif :",
        "revertmove": "rétablir",
-       "delete_and_move_text": "== Suppression requise ==\nLa page de destination « [[:$1]] » existe déjà.\nÊtes-vous certain{{GENDER:||e|}} de vouloir la supprimer pour permettre ce renommage ?",
+       "delete_and_move_text": "La page de destination « [[:$1]] » existe déjà.\nÊtes-vous certain{{GENDER:||e|}} de vouloir la supprimer pour permettre ce renommage ?",
        "delete_and_move_confirm": "Oui, supprimer la page de destination",
        "delete_and_move_reason": "Page supprimée pour permettre le renommage depuis « [[$1]] »",
        "selfmove": "Les titres d'origine et de destination sont les mêmes ;\nimpossible de renommer une page sur elle-même.",
        "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 :",
-       "move-over-sharedrepo": "== Le fichier existe ==\n[[:$1]] existe déjà sur un dépôt partagé. Renommer ce fichier rendra le fichier sur le dépôt partage inaccessible.",
+       "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}}]].",
        "export-download": "Enregistrer dans un fichier",
        "export-templates": "Inclure les modèles",
        "export-pagelinks": "Inclure les pages liées à une profondeur de :",
+       "export-manual": "Ajouter des pages manuellement :",
        "allmessages": "Messages système",
        "allmessagesname": "Nom du message",
        "allmessagesdefault": "Message par défaut",
        "javascripttest-pagetext-frameworks": "Veuillez choisir l'une des structures de test suivantes : $1",
        "javascripttest-pagetext-skins": "Choisissez un habillage avec lequel lancer les tests :",
        "javascripttest-qunit-intro": "Voir [$1 la documentation de test] sur mediawiki.org.",
-       "tooltip-pt-userpage": "Votre page utilisateur",
+       "tooltip-pt-userpage": "{{GENDER:|Votre}} page utilisateur",
        "tooltip-pt-anonuserpage": "La page utilisateur de l'IP avec laquelle vous contribuez",
-       "tooltip-pt-mytalk": "Votre page de discussion",
+       "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": "Vos préférences",
+       "tooltip-pt-preferences": "{{GENDER:|Vos}} préférences",
        "tooltip-pt-watchlist": "La liste des pages dont vous suivez les modifications",
-       "tooltip-pt-mycontris": "La liste de vos contributions",
+       "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-pt-logout": "Se déconnecter",
        "tooltip-t-recentchangeslinked": "Liste des modifications récentes des pages liées à 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 cet utilisateur",
-       "tooltip-t-emailuser": "Envoyer un courriel à cet utilisateur",
+       "tooltip-t-contributions": "Voir la liste des contributions de {{GENDER:$1|cet utilisateur|cette utilisatrice}}",
+       "tooltip-t-emailuser": "Envoyer un courriel à {{GENDER:$1|cet utilisateur|cette utilisatrice}}",
        "tooltip-t-info": "Plus d’information sur cette page",
-       "tooltip-t-upload": "Envoyer une image ou fichier média sur le serveur",
+       "tooltip-t-upload": "Téléverser des fichiers",
        "tooltip-t-specialpages": "Liste de toutes les pages spéciales",
        "tooltip-t-print": "Version imprimable de cette page",
-       "tooltip-t-permalink": "Lien permanent vers cette version de la page",
+       "tooltip-t-permalink": "Adresse permanente de cette version de la page",
        "tooltip-ca-nstab-main": "Voir la page de contenu",
        "tooltip-ca-nstab-user": "Voir la page utilisateur",
        "tooltip-ca-nstab-media": "Voir la page du média",
        "pageinfo-category-files": "Nombre de fichiers",
        "markaspatrolleddiff": "Marquer comme relue",
        "markaspatrolledtext": "Marquer cette page comme relue",
+       "markaspatrolledtext-file": "Marquer cette version de fichier comme patrouillée",
        "markedaspatrolled": "Marquée comme relue",
        "markedaspatrolledtext": "La version sélectionnée de [[:$1]] a été marquée comme relue.",
        "rcpatroldisabled": "La fonction de relecture des modifications récentes n'est pas activée.",
        "newimages-legend": "Nom du fichier",
        "newimages-label": "Nom du fichier (ou une partie de celui-ci) :",
        "newimages-showbots": "Afficher les imports par des robots",
+       "newimages-hidepatrolled": "Masquer les téléchargements patrouillés",
        "noimages": "Aucune image à afficher.",
        "ilsubmit": "Rechercher",
        "bydate": "par date",
        "scarytranscludefailed-httpstatus": "[Échec de la récupération du modèle pour  $1 : HTTP  $2 ]",
        "scarytranscludetoolong": "[L'URL est trop longue]",
        "deletedwhileediting": "'''Attention''' : cette page a été supprimée après que vous ayez commencé à la modifier !",
-       "confirmrecreate": "L’utilisateur [[User:$1|$1]] ([[User talk:$1|Discussion]]) a supprimé cette page, alors que vous aviez commencé à la modifier, pour le motif suivant :\n: ''$2''\nVeuillez confirmer que vous désirez réellement recréer cette page.",
-       "confirmrecreate-noreason": "L’utilisateur [[User:$1|$1]] ([[User talk:$1|Discussion]]) a supprimé cette page, alors que vous aviez commencé à la modifier. Veuillez confirmer que vous désirez réellement recréer cette page.",
+       "confirmrecreate": "L’utilisat{{GENDER:$1|eur|rice}} [[User:$1|$1]] ([[User talk:$1|Discussion]]) a supprimé cette page, alors que vous aviez commencé à la modifier, pour le motif suivant :\n: <em>$2</em>\nVeuillez confirmer que vous désirez réellement recréer cette page.",
+       "confirmrecreate-noreason": "L’utilisat{{GENDER:$1|eur|rice}} [[User:$1|$1]] ([[User talk:$1|Discussion]]) a supprimé cette page, alors que vous aviez commencé à la modifier. Veuillez confirmer que vous désirez réellement recréer cette page.",
        "recreate": "Recréer",
        "confirm_purge_button": "Confirmer",
        "confirm-purge-top": "Voulez-vous rafraîchir cette page (purger le cache) ?",
        "hebrew-calendar-m11-gen": "av",
        "hebrew-calendar-m12-gen": "eloul",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussion]])",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "Attention : la clé de tri par défaut « $2 » écrase la précédente clé « $1 ».",
        "duplicate-displaytitle": "<strong>Attention :</strong> Le titre d'affichage « $2 » remplace l'ancien titre d'affichage « $1 ».",
        "invalid-indicator-name": "<strong>Erreur :</strong> L’attribut <code>name</code> des indicateurs d’état de la page ne doit pas être vide.",
        "version-libraries-license": "Licence",
        "version-libraries-description": "Description",
        "version-libraries-authors": "Auteurs",
-       "redirect": "Redirigé par fichier, utilisateur, page ou ID de révision",
+       "redirect": "Rediriger par ID de fichier, utilisateur, page, révision ou journal",
        "redirect-legend": "Rediriger vers une page ou un fichier",
-       "redirect-summary": "Cette page spéciale redirige vers un fichier (nom de fichier fourni), une page (ID de révision ou de page fourni) ou une page d’utilisateur (identifiant numérique de l’utilisateur fourni). Utilisation : [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], ou [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Cette page spéciale redirige vers un fichier (nom de fichier fourni), une page (ID de révision ou de page fourni), une page d’utilisateur (identifiant numérique de l’utilisateur fourni), ou une entrée de journal (ID du journal fourni). Utilisation : [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], ou [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Valider",
        "redirect-lookup": "Recherche :",
        "redirect-value": "Valeur :",
        "redirect-page": "ID de page",
        "redirect-revision": "Révision de la page",
        "redirect-file": "Nom du fichier",
+       "redirect-logid": "ID de journal",
        "redirect-not-exists": "Valeur non trouvée",
        "fileduplicatesearch": "Recherche de doublons",
        "fileduplicatesearch-summary": "Recherche des copies de fichiers identiques d'après leur empreinte de hachage.",
        "tags-apply-not-allowed-one": "La balise « $1 » n’est pas autorisée à être appliquée manuellement.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|La balise suivante n’est pas autorisée à être appliquée|Les balises suivantes ne sont pas autorisées à être appliquées}} manuellement : $1",
        "tags-update-no-permission": "Vous n’avez pas le droit d’ajouter ou de supprimer des balises de modification des révisions individuelles ou des entrées de journal.",
-       "tags-update-blocked": "Vous ne pouvez pas ajouter ou supprimer des balises lorsque vous êtes bloqué{{GENDER:||e}}.",
+       "tags-update-blocked": "Vous ne pouvez pas ajouter ou supprimer des balises de modifications lorsque vous êtes bloqué{{GENDER:||e}}.",
        "tags-update-add-not-allowed-one": "La balise « $1 » ne peut pas être ajoutée manuellement.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|La balise suivante ne peut pas être ajoutée|Les balises suivantes ne peuvent pas être ajoutées}} manuellement : $1",
        "tags-update-remove-not-allowed-one": "La balise « $1 » ne peut pas être enlevée.",
        "expand_templates_preview": "Aperçu du rendu",
        "expand_templates_preview_fail_html": "<em>Comme {{SITENAME}} a HTML brut activé et qu’il y a eu une perte de données de session, l’aperçu est masqué par précaution contre les attaques JavaScript.</em>\n\n<strong>Si c’est une demande d’aperçu légitime, veuillez réessayer.</strong>\nSi cela ne fonctionne toujours pas, essayez de [[Special:UserLogout|vous déconnecter]] et vous reconnecter.",
        "expand_templates_preview_fail_html_anon": "<em>Comme {{SITENAME}} a HTML brut activé et que vous n’êtes pas connecté, l’aperçu est masqué par précaution contre les attaques JavaScript.</em>\n\n<strong>Si c’est une demande d’aperçu légitime, veuillez [[Special:UserLogin|vous connecter]] et réessayer.</strong>",
+       "expand_templates_input_missing": "Vous devez fournir au moins un texte d’entrée.",
        "pagelanguage": "Sélecteur de langue de la page",
        "pagelang-name": "Page",
        "pagelang-language": "Langue",
        "pagelang-use-default": "Utiliser la langue par défaut",
        "pagelang-select-lang": "Sélectionner la langue",
+       "pagelang-submit": "Envoyer",
        "right-pagelang": "Changer la langue de la page",
        "action-pagelang": "changer la langue de la page",
        "log-name-pagelang": "Tracer les changements de langue",
        "mediastatistics": "Statistiques sur les médias",
        "mediastatistics-summary": "Statistiques sur les types de fichier téléchargés. Elles ne prennent en compte que la version la plus récente d’un fichier. Les versions anciennes ou supprimées des fichiers sont exclues.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 octet|$1 octets}} ($2 ; $3%)",
+       "mediastatistics-bytespertype": "Taille totale de fichiers pour cette section : {{PLURAL:$1|$1 octet|$1 octets}} ($2 ; $3%).",
+       "mediastatistics-allbytes": "Taille totale pour tous les fichiers : {{PLURAL:$1|$1 octet|$1 octets}} ($2).",
        "mediastatistics-table-mimetype": "Type MIME",
        "mediastatistics-table-extensions": "Extensions possibles",
        "mediastatistics-table-count": "Nombre de fichiers",
        "mediastatistics-header-text": "Textuel",
        "mediastatistics-header-executable": "Exécutables",
        "mediastatistics-header-archive": "Formats compressés",
+       "mediastatistics-header-total": "Tous les fichiers",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|virgule finale a été supprimée|virgules finales ont été supprimées}} du JSON",
        "json-error-unknown": "Il y a eu un problème avec le JSON. Erreur : $1",
        "json-error-depth": "La taille maximale de la pile a été dépassée",
        "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."
+       "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",
+       "sessionprovider-nocookies": "Les cookies peuvent être désactivés. Assurez-vous que vous avez activé les cookies et recommencez.",
+       "randomrootpage": "Page racine aléatoire"
 }
index 3aca26e..7586ffc 100644 (file)
@@ -33,7 +33,7 @@
        "tog-previewontop": "\"Iarst ans luke\" boowen faan't wönang tu bewerkin",
        "tog-previewonfirst": "Bi't iarst bewerkin \"iarst ans luke\" uunwise",
        "tog-enotifwatchlistpages": "Schüür mi en e-mail, wan sidjen of datein feranert wurd, diar ik uun't uug behual wal",
-       "tog-enotifusertalkpages": "Bi feranrangen üüb min brüker-diskusjuunssidj en e-mail schüür",
+       "tog-enotifusertalkpages": "Bi feranrangen üüb min brüker-diskuschuunssidj en e-mail schüür",
        "tog-enotifminoredits": "Schüür mi uk bi letj feranrangen faan sidjen an datein en e-mail",
        "tog-enotifrevealaddr": "Min e-mail adres uun e-mail noorachten uunwise",
        "tog-shownumberswatching": "Taal faan brükern uunwise, diar det sidj uun't uug haa",
        "morenotlisted": "Detdiar list as ei komplet.",
        "mypage": "Sidj",
        "mytalk": "Diskuschuun",
-       "anontalk": "Diskusjuunssidj faan detdiar IP",
+       "anontalk": "Diskuschuun",
        "navigation": "Nawigatjuun",
        "and": "&#32;an",
        "qbfind": "Finj",
        "nstab-template": "Föörlaag",
        "nstab-help": "Halepsidj",
        "nstab-category": "Kategorii",
+       "mainpage-nstab": "Hoodsidj",
        "nosuchaction": "Son aktjuun jaft at ei",
        "nosuchactiontext": "Son aktjuun jaft at üüb MediaWiki ei.\nFerlicht heest dü det URL ferkiard apskrewen, of dü beest en ferkiard ferwisang fulagt.\nFerlicht as det uk en feeler uun det software faan {{SITENAME}}.",
        "nosuchspecialpage": "Son spezial-sidj jaft at ei.",
        "passwordreset-emailtext-ip": "Hoker mä det IP-Adres $1, woorskiinelk dü salew, wul hal brükerinformatsjuunen för {{SITENAME}} tusjüürd fu ($4). {{PLURAL:$3|Detdiar brükerkonto as|Jodiar brükerkontos san}} mä detdiar E-Mail-adres ferbünjen:\n\n$2\n\n{{PLURAL:$3|Detheer tidjwiis paaswurd lääpt|Joheer tidjwiis paaswurden luup}} efter {{PLURAL:$5|ään dai|$5 daar}} uf. \nDü skulst di uunmelde an en nei paaswurd iinracht. Wan hoker ööders detheer uunfraag steld hää an dü din ual paaswurd käänst, do säärst dü niks widjer onernem. Melde di ianfach widjerhen mä din ual paaswurd uun.",
        "passwordreset-emailtext-user": "Di brüker $1 üüb {{SITENAME}} hää am brükerinformatsjuunen för {{SITENAME}} uunfraaget ($4). {{PLURAL:$3|Detdiar brükerkonto as|Jodiar brükerkontos san}} mä detdiar E-Mail-Adres ferbünjen:\n\n$2\n\n{{PLURAL:$3|Detheer tidjwiis paaswurd lääpt|Joheer tidjwiis paaswurden luup}} efter {{PLURAL:$5|ään dai|$5 daar}} uf. Dü skulst di uunmelde an en nei paaswurd iinracht. Wan hoker ööders detheer uunfraag steld hää of dü din ual paaswurd käänst, säärst dü niks widjer onernem. Melde di ianfach mä din ual paaswurd uun.",
        "passwordreset-emailelement": "Brükernööm: \n$1\n\nTidjwiis paaswurd: \n$2",
-       "passwordreset-emailsent": "Diar as en E-Mail tu di onerwais.",
+       "passwordreset-emailsentemail": "Diar as en E-Mail tu di onerwais.",
        "passwordreset-emailsent-capture": "Detdiar E-Mail, wat oner uunwiset woort, as tu di onerwais.",
        "passwordreset-emailerror-capture": "Detdiar E-Mail, wat oner uunwiset woort, wiar tu di onerwais, oober küd ei tu di {{GENDER:$2|brüker}} ufsjüürd wurd: $1",
        "changeemail": "Feranre det E-Mail-adres",
        "prefs-help-prefershttps": "Detdiar iinstelang täält, wan dü di naist tooch uunmeldest.",
        "prefswarning-warning": "A feranrangen bi din iinstelangen san noch ei seekert wurden.\nWan dü detheer sidj ferläätst, saner üüb \"$1\" tu traken, wurd din iinstelangen ei aktualisiaret.",
        "prefs-tabs-navigation-hint": "Halep: Dü könst a lachter of rochter wiiser-knoop brük, am tesken a ridjerkoorden boowen uun't menüü hen an weder tu springen.",
-       "email-address-validity-valid": "Detdiar E-Mail-adres schocht gud ütj.",
-       "email-address-validity-invalid": "Du en echt E-Mail-adres uun.",
        "userrights": "Brükerrochten bewerke",
        "userrights-lookup-user": "Brükersköölen bewerke",
        "userrights-user-editname": "Brükernööm:",
        "contributions": "{{GENDER:$1|Brüker}} bidracher",
        "contributions-title": "Brükerbidracher för \"$1\"",
        "mycontris": "Bidracher",
+       "anoncontribs": "Bidracher",
        "contribsub2": "För {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "Son brükerkonto \"$1\" jaft at ei.",
        "nocontribs": "Diar wiar nian paasin brükerbidracher",
        "ipbother": "Ööder sperdüür (ingelsk):",
        "ipboptions": "2 stünj:2 hours,1 dai:1 day,3 daar:3 days,1 weg:1 week,2 weg:2 weeks,1 muun:1 month,3 muuner:3 months,6 muuner:6 months,1 juar:1 year,saner aanj:infinite",
        "ipbhidename": "Brükernööm uun feranrangen an listen fersteeg",
-       "ipbwatchuser": "Hual di brüker sin brüker- an diskusjuunssidj uun't uug",
-       "ipb-disableusertalk": "Ferhanre, dat di brüker sin diskusjuunssidj bewerket, so loong hi speret as.",
+       "ipbwatchuser": "Hual di brüker sin brüker- an diskuschuunssidj uun't uug",
+       "ipb-disableusertalk": "Ferhanre, dat di brüker sin diskuschuunssidj bewerket, so loong hi speret as.",
        "ipb-change-block": "Mä jodiar iinstelangen widjer spere",
        "ipb-confirm": "Sper gudkään",
        "badipaddress": "Det IP-adres as ferkiard.",
        "noautoblockblock": "autoblock ei aktiif",
        "createaccountblock": "brükerkontos kön ei iinracht wurd.",
        "emailblock": "e-mail fersjüüren ufsteld",
-       "blocklist-nousertalk": "koon sin aanj diskusjuunssidj ei bewerke",
+       "blocklist-nousertalk": "koon sin aanj diskuschuunssidj ei bewerke",
        "ipblocklist-empty": "Det sperlist as leesag",
        "ipblocklist-no-results": "Detdiar IP-adres/di brükernööm as ei speret.",
        "blocklink": "Spere",
        "block-log-flags-nocreate": "brükerkontos kön ei iinracht wurd.",
        "block-log-flags-noautoblock": "autoblock ei aktiif",
        "block-log-flags-noemail": "e-mail fersjüüren ufsteld",
-       "block-log-flags-nousertalk": "koon sin aanj diskusjuunssidj ei bewerke",
+       "block-log-flags-nousertalk": "koon sin aanj diskuschuunssidj ei bewerke",
        "block-log-flags-angry-autoblock": "ütjwidjet autoblock aktiwiaret",
        "block-log-flags-hiddenname": "brükernööm ferbürgen",
        "range_block_disabled": "Det mögelkhaid, hialer adresrümer tu sperin, as ei aktiif.",
        "movepage-moved-noredirect": "Det maagin faan en widjerfeerang as ferhanert wurden.",
        "articleexists": "En sidj mä didiar nööm jaft at al. Wees so gud an nem en öödern nööm.",
        "cantmove-titleprotected": "Dü könst det sidj ei so fersküüw, auer di nei nööm speret as.",
-       "movetalk": "Uk det diskusjuunssidj fersküüw, wan't gongt",
+       "movetalk": "Uk det diskuschuunssidj fersküüw, wan't gongt",
        "move-subpages": "Onersidjen fersküüw (bit $1)",
-       "move-talk-subpages": "Onersidjen faan't diskusjuunssidj fersküüw (bit $1)",
+       "move-talk-subpages": "Onersidjen faan't diskuschuunssidj fersküüw (bit $1)",
        "movepage-page-exists": "Det sidj „$1“ as al diar an koon ei automaatisk auerskrewen wurd.",
        "movepage-page-moved": "Det sidj $1 as efter $2 fersköwen wurden.",
        "movepage-page-unmoved": "Det sidj $1 küd ei efter $2 fersköwen wurd.",
        "movenosubpage": "Det sidj hää nian onersidjen.",
        "movereason": "Grünj:",
        "revertmove": "turag fersküüw",
-       "delete_and_move": "Strik an fersküüw",
        "delete_and_move_text": "== Striken nuadag  ==\n\nDet sidj „[[:$1]]“ as al diar. Wel dü det strik, am det sidj tu fersküüwen?",
        "delete_and_move_confirm": "Ja, sidj strik",
        "delete_and_move_reason": "Stregen, am steeds för det fersküüwen faan „[[$1]]“ tu maagin.",
index bce3e94..c7f2b00 100644 (file)
        "right-blockemail": "Cead cleachdaiche a bhacadh o chur phost-d",
        "right-hideuser": "Cead ainm-chleachdaiche a bhacadh 's fhalach on t-sealladh phoblach",
        "right-ipblock-exempt": "Cead bacaidhean IP, bacaidhean fèin-obrachail 's bacaidhean rainse a leigeil seachad",
-       "right-proxyunbannable": "Cead bacadh fèin-obrachail phrogsaidhean a leigeil seachad",
        "right-unblockself": "Cead bacadh a thoirt uaithe fhèin",
        "right-protect": "Cead leibheilean an dìona atharrachadh 's duilleagan le dìon o bhith mar eas a dheasachadh",
        "right-editprotected": "Cead gus duilleagan le dìon \"{{int:protect-level-sysop}}\" a dheasachadh",
        "javascripttest-pagetext-frameworks": "Feuch an tagh thu aon dhe na frameworks deuchainn seo: $1",
        "javascripttest-pagetext-skins": "Tagh craiceann airson ruith nan deuchainnean:",
        "javascripttest-qunit-intro": "Faic [$1 docamaideadh nan deuchainnean] air mediawiki.org.",
-       "tooltip-pt-userpage": "An duilleag phearsanta agad",
+       "tooltip-pt-userpage": "An duilleag phearsanta {{GENDER:|agad}}",
        "tooltip-pt-anonuserpage": "Duilleag a' chleachdaiche airson an t-seòlaidh IP leis a bheil thu a' deasachadh",
-       "tooltip-pt-mytalk": "Duilleag do dheasbaireachd",
+       "tooltip-pt-mytalk": "An duilleag deasbaireachd {{GENDER:|agad}}",
        "tooltip-pt-anontalk": "deasbaireachd mu dheasachaidhean on t-seòladh IP seo",
-       "tooltip-pt-preferences": "Do roghainnean",
+       "tooltip-pt-preferences": "Na roghainnean {{GENDER:|agad}}",
        "tooltip-pt-watchlist": "Seo liosta nan duilleagan a tha thu a' cumail sùil orra a thaobh mhùthaidhean a nithear orra",
-       "tooltip-pt-mycontris": "Liosta na h-obrach a rinn thu",
+       "tooltip-pt-mycontris": "Liosta na h-obrach a rinn {{GENDER:|thu}}",
        "tooltip-pt-login": "Mholamaidh dhut logadh a-steach; ge-tà, cha leig thu leas seo a dhèanamh",
        "tooltip-pt-logout": "Log a-mach",
        "tooltip-pt-createaccount": "Ged nach eil e riatanach, tha e nas fhearr ma chruthaicheas tu cunntas agus ma logas tu air.",
        "tooltip-t-recentchangeslinked": "Mùthaidhean a rinneadh o chionn ghoirid air duilleagan a tha ceangal ann thuca on duilleag seo",
        "tooltip-feed-rss": "Inbhir RSS airson na duilleige seo",
        "tooltip-feed-atom": "Inbhir Atom airson na duilleige seo",
-       "tooltip-t-contributions": "Seall liosta na h-obrach a rinn an cleachdaiche seo",
+       "tooltip-t-contributions": "Seall liosta na h-obrach a rinn {{GENDER:$1|an cleachdaiche}} seo",
        "tooltip-t-emailuser": "Cuir post-dealain dhan chleachdaiche seo",
        "tooltip-t-upload": "Luchdaich suas faidhle",
        "tooltip-t-specialpages": "Liosta de gach duilleag shònraichte",
index afb197f..65e334e 100644 (file)
@@ -56,7 +56,7 @@
        "tog-watchlisthidebots": "Agochar as edicións dos bots na lista de vixilancia",
        "tog-watchlisthideminor": "Agochar as edicións pequenas na lista de vixilancia",
        "tog-watchlisthideliu": "Agochar as edicións dos usuarios rexistrados na lista de vixilancia",
-       "tog-watchlistreloadautomatically": "Recargar a lista de vixilancia automáticamente cando se produce un cambio nun filtro (require JavaScript)",
+       "tog-watchlistreloadautomatically": "Recargar a lista de vixilancia automaticamente cando se produce un cambio nun filtro (require JavaScript)",
        "tog-watchlisthideanons": "Agochar as edicións dos usuarios anónimos na lista de vixilancia",
        "tog-watchlisthidepatrolled": "Agochar as edicións patrulladas na lista de vixilancia",
        "tog-watchlisthidecategorization": "Agochar a categorización das páxinas",
        "october-date": "$1 de outubro",
        "november-date": "$1 de novembro",
        "december-date": "$1 de decembro",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoría|Categorías}}",
        "category_header": "Páxinas na categoría \"$1\"",
        "subcategories": "Subcategorías",
        "databaseerror-query": "Pescuda: $1",
        "databaseerror-function": "Función: $1",
        "databaseerror-error": "Erro: $1",
-       "transaction-duration-limit-exceeded": "Para evitar crear un gran atraso na replicación, esta transacción abortouse xa que a duración de escritura ($1) excedeu o límite de $2 segundos.\nSe estás a cambiar moitos obxectos ao mesmo tempo, procura facer operacións múltiples máis pequenas no seu lugar.",
+       "transaction-duration-limit-exceeded": "Para evitar crear un gran atraso na replicación, esta transacción abortouse xa que a duración de escritura ($1) excedeu o límite de $2 {{PLURAL:$2|segundo|segundos}} .\nSe está a cambiar moitos obxectos ao mesmo tempo, procure facer operacións múltiples máis pequenas no seu lugar.",
        "laggedslavemode": "'''Aviso:''' A páxina pode non conter as actualizacións recentes.",
        "readonly": "Base de datos pechada",
        "enterlockreason": "Dea unha razón para o peche, incluíndo unha estimación de até cando se manterá",
-       "readonlytext": "Nestes intres a base de datos está pechada a novas entradas e outras modificacións, probablemente debido a procesos de mantemento, tras os que volverá á normalidade.\n\nO administrador que a pechou deu esta explicación: $1",
+       "readonlytext": "Nestes intres a base de datos está pechada a novas entradas e outras modificacións, probablemente debido a procesos de mantemento, tras os que volverá á normalidade.\n\nO administrador de sistemas que a pechou deu esta explicación: $1",
        "missing-article": "A base de datos non atopou o texto da páxina chamada \"$1\" $2, que debera ter atopado.\n\nNormalmente, isto está causado por seguir unha ligazón cara a unha diferenza vella ou a unha páxina que foi borrada.\n\nSe este non é o caso, poida que atopase un erro no software.\nPor favor, comuníquello a un [[Special:ListUsers/sysop|administrador]] tomando nota do enderezo URL.",
        "missingarticle-rev": "(nº de revisión: $1)",
        "missingarticle-diff": "(dif: $1, $2)",
        "mypreferencesprotected": "Non ten os permisos necesarios para editar as súas preferencias.",
        "ns-specialprotected": "Non se poden editar as páxinas no espazo de nomes \"{{ns:special}}\".",
        "titleprotected": "Este título foi protexido da creación por [[User:$1|$1]].\nO motivo achegado é ''$2''.",
-       "filereadonlyerror": "Non se puido modificar o ficheiro \"$1\" porque o repositorio \"$2\" está en modo de só lectura.\n\nO administrador que bloqueou o repositorio achegou este motivo: \"$3\".",
+       "filereadonlyerror": "Non se puido modificar o ficheiro \"$1\" porque o repositorio \"$2\" está en modo de só lectura.\n\nO administrador de sistemas que bloqueou o repositorio achegou este motivo: \"$3\".",
        "invalidtitle-knownnamespace": "Título inválido co espazo de nomes \"$2\" e o texto \"$3\"",
        "invalidtitle-unknownnamespace": "Título inválido cun número de espazo de nomes, $1, descoñecido e o texto \"$2\"",
        "exception-nologin": "Non accedeu ao sistema",
        "virus-scanfailed": "fallou o escaneado (código $1)",
        "virus-unknownscanner": "antivirus descoñecido:",
        "logouttext": "'''Agora está fóra do sistema.'''\n\nTeña en conta que algunhas páxinas poden continuar aparecendo como se aínda estivese dentro do sistema, ata que limpe a caché do seu navegador.",
+       "cannotlogoutnow-title": "Non se pode saír da sesión agora mesmo",
+       "cannotlogoutnow-text": "Non é posible saír da sesión cando se usa $1.",
        "welcomeuser": "Reciba a nosa benvida, $1!",
        "welcomecreation-msg": "A súa conta foi creada correctamente.\nNon esqueza personalizar as súas [[Special:Preferences|preferencias de {{SITENAME}}]].",
        "yourname": "Nome de usuario:",
        "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",
+       "cannotloginnow-text": "Non é posible iniciar a sesión cando se usa $1.",
        "yourdomainname": "O seu dominio:",
        "password-change-forbidden": "Non pode mudar os contrasinais neste wiki.",
        "externaldberror": "Ou ben se produciu un erro da base de datos na autenticación externa ou ben non se lle permite actualizar a súa conta externa.",
        "resetpass_submit": "Establecer o contrasinal e acceder ao sistema",
        "changepassword-success": "O seu contrasinal modificouse correctamente!",
        "changepassword-throttled": "Fixo demasiados intentos de acceder ao sistema.\nPor favor, agarde $1 antes de probar outra vez.",
+       "botpasswords": "Contrasinais de Bot",
+       "botpasswords-summary": "Os <em>contrasinais de Bot</em> permiten acceder a unha conta de usuario por medio da API sen usar as crecenciais de acceso da conta principal. Os dereitos de usuario dispoñibles cando se accede ao sistema cun contrasinal de bot poden estar restrinxidos.",
+       "botpasswords-disabled": "Os contrasinais de bot non están habilitados.",
+       "botpasswords-no-central-id": "Para usar contrasinais de bot debes acceder ao sistema cunha conta centralizada.",
+       "botpasswords-existing": "Contrasinais de bot existentes",
+       "botpasswords-createnew": "Crear un novo contrasinal de bot",
+       "botpasswords-editexisting": "Editar un contrasinal de bot xa existente",
+       "botpasswords-label-appid": "Nome do bot:",
+       "botpasswords-label-create": "Crear",
+       "botpasswords-label-update": "Actualizar",
+       "botpasswords-label-cancel": "Cancelar",
+       "botpasswords-label-delete": "Borrar",
+       "botpasswords-label-resetpassword": "Restablecer o contrasinal",
+       "botpasswords-label-grants": "Permisos aplicables:",
+       "botpasswords-help-grants": "Cada permiso da acceso aos permisos de usuario listados que a conta xa teña. Vexa a [[Special:ListGrants|táboa de permisos]] para máis información.",
+       "botpasswords-label-restrictions": "Restriccións de uso:",
+       "botpasswords-label-grants-column": "Concedido",
+       "botpasswords-bad-appid": "O nome de bot \"$1\" non é válido.",
+       "botpasswords-insert-failed": "Erro ao engadir o nome de bot \"$1\". Revise se xa foi engadido previamente.",
+       "botpasswords-update-failed": "Erro ao actualizar o nome de bot \"$1\". Revise se foi borrado.",
+       "botpasswords-created-title": "Contrasinal de bot creado",
+       "botpasswords-created-body": "O contrasinal de bot \"$1\" creouse con éxito.",
+       "botpasswords-updated-title": "Contrasinal de bot actualizado",
+       "botpasswords-updated-body": "O contrasinal de bot \"$1\" actualizouse con éxito.",
+       "botpasswords-deleted-title": "Contrasinal de bot borrado",
+       "botpasswords-deleted-body": "O contrasinal de bot \"$1\" foi borrado.",
+       "botpasswords-newpassword": "O novo contrasinal para acceder con strong>$1</strong> é <strong>$2</strong>. <em>Por favor, rexistra isto para referencia futura.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider non está dispoñible.",
+       "botpasswords-restriction-failed": "Restricións de contrasinal de bots evitaron esta conexión.",
+       "botpasswords-invalid-name": "O nome de usuario especificado non contén o separador de contrasinal de bot (\"$1\").",
+       "botpasswords-not-exist": "O usuario \"$1\" non ten un contrasinal de bot de nome \"$2\".",
        "resetpass_forbidden": "Non se poden mudar os contrasinais",
        "resetpass-no-info": "Debe rexistrarse para acceder directamente a esta páxina.",
        "resetpass-submit-loggedin": "Cambiar o contrasinal",
        "passwordreset-emailtext-ip": "Alguén (probablemente vostede, desde o enderezo IP $1) solicitou o restablecemento do seu\ncontrasinal de {{SITENAME}} ($4). {{PLURAL:$3|A seguinte conta de usuario está asociada|As seguintes contas de usuarios están asociadas}}\na este enderezo de correo electrónico:\n\n$2\n\n{{PLURAL:$3|Este contrasinal temporal caducará|Estes contrasinais temporais caducarán}} {{PLURAL:$5|nun día|en $5 días}}.\nDebería acceder ao sistema e elixir un novo contrasinal agora. Se outra persoa fixo esta\nsolicitude ou se lembrou o seu contrasinal orixinal e xa non o quere cambiar,\nignore esta mensaxe e continúe empregando o seu contrasinal vello.",
        "passwordreset-emailtext-user": "O usuario $1 solicitou o restablecemento do contrasinal de {{SITENAME}}\n($4). {{PLURAL:$3|A seguinte conta de usuario está asociada|As seguintes contas de usuarios están asociadas}}\na este enderezo de correo electrónico:\n\n$2\n\n{{PLURAL:$3|Este contrasinal temporal caducará|Estes contrasinais temporais caducarán}} {{PLURAL:$5|nun día|en $5 días}}.\nDebería acceder ao sistema e elixir un novo contrasinal agora. Se outra persoa fixo esta\nsolicitude ou se lembrou o seu contrasinal orixinal e xa non o quere cambiar,\nignore esta mensaxe e continúe empregando o seu contrasinal vello.",
        "passwordreset-emailelement": "Nome de usuario: \n$1\n\nContrasinal temporal: \n$2",
-       "passwordreset-emailsentemail": "Se esta é unha dirección de correo electrónico rexistrada para a túa conta, entón enviarase un correo electrónico para o restablecemento do teu contrasinal.",
+       "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",
        "changeemail": "Cambiar ou eliminar o enderezo de correo electrónico",
        "resettokens-watchlist-token": "Pase para a fonte de novas (Atom/RSS) web dos [[Special:Watchlist|cambios feitos nas páxinas da súa lista de vixilancia]]",
        "resettokens-done": "Restablecéronse os pases.",
        "resettokens-resetbutton": "Restablecer os pases seleccionados",
-       "bold_sample": "Texto en negra",
-       "bold_tip": "Texto en negra",
+       "bold_sample": "Texto en letra grosa",
+       "bold_tip": "Texto en letra grosa",
        "italic_sample": "Texto en cursiva",
        "italic_tip": "Texto en cursiva",
        "link_sample": "Título da ligazón",
        "link_tip": "Ligazón interna",
-       "extlink_sample": "http://www.example.com título da ligazón",
+       "extlink_sample": "http://www.exemplo.com título da ligazón",
        "extlink_tip": "Ligazón externa (lembre o prefixo http://)",
        "headline_sample": "Texto de cabeceira",
        "headline_tip": "Cabeceira de nivel 2",
        "nowiki_sample": "Insira aquí un texto sen formato",
        "nowiki_tip": "Ignorar o formato wiki",
        "image_sample": "Exemplo.jpg",
-       "image_tip": "Ficheiro embelecido",
+       "image_tip": "Ficheiro incorporado",
        "media_sample": "Exemplo.ogg",
        "media_tip": "Ligazón a un ficheiro",
        "sig_tip": "A súa sinatura con data e hora",
        "copyrightwarning2": "Por favor, decátese de que todas as súas contribucións a {{SITENAME}} poden ser editadas, alteradas ou eliminadas por outras persoas. Se non quere que os seus escritos sexan editados sen piedade, non os publique aquí.<br />\nDo mesmo xeito, comprométese a que o que vostede escriba sexa da súa autoría ou copiado dunha fonte de dominio público ou recurso público semellante (vexa $1 para detalles).\n'''NON ENVÍE SEN PERMISO TRABALLOS CON DEREITOS DE COPIA!'''",
        "editpage-cannot-use-custom-model": "O modelo de contido desta páxina non se pode modificar.",
        "longpageerror": "'''Erro: O texto que pretende gardar ocupa {{PLURAL:$1|$1 kilobyte|$1 kilobytes}}, e existe un límite dun máximo de {{PLURAL:$2|$2 kilobyte|$2 kilobytes}}.'''\nPolo tanto, non se pode gardar.",
-       "readonlywarning": "'''Atención: A base de datos foi pechada para facer mantemento, polo que non vai poder gardar as súas edicións polo de agora.'''\nSe cadra, pode cortar e pegar o texto nun ficheiro de texto e gardalo para despois.\n\nO administrador que a pechou deu esta explicación: $1",
+       "readonlywarning": "<strong>Atención: A base de datos foi pechada para facer mantemento, polo que non vai poder gardar as súas edicións polo de agora.</strong>\nSe cadra, pode cortar e pegar o texto nun ficheiro de texto e gardalo para despois.\n\nO administrador de sistemas que a pechou deu esta explicación: $1",
        "protectedpagewarning": "'''Aviso: Esta páxina foi protexida de xeito que só os usuarios con privilexios de administrador a poidan editar.'''\nVelaquí está a última entrada no rexistro, por se quere consultala:",
        "semiprotectedpagewarning": "'''Nota:''' Esta páxina foi protexida de xeito que só os usuarios rexistrados a poidan editar.\nVelaquí está a última entrada no rexistro, por se quere consultala:",
        "cascadeprotectedwarning": "<strong>Aviso:</strong> Esta páxina foi protexida de xeito que só a poden editar os usuarios con privilexios de administrador debido a que está transcluída {{PLURAL:$1|na seguinte páxina protexida|nas seguintes páxinas protexidas}} coa opción \"protección en serie\" activada:",
        "permissionserrors": "Erro de permisos",
        "permissionserrorstext": "Non ten os permisos necesarios para facelo {{PLURAL:$1|pola seguinte razón|polas seguintes razóns}}:",
        "permissionserrorstext-withaction": "Non ten os permisos necesarios para $2, {{PLURAL:$1|pola seguinte razón|polas seguintes razóns}}:",
-       "contentmodelediterror": "Non pode editar esta revisión porque o seu modelo de contido é <code>$1</code>, e o modelo de contido actual da páxina é <code>$2</code>.",
+       "contentmodelediterror": "Non pode editar esta revisión porque o seu modelo de contido é <code>$1</code>, o cal difire do modelo de contido actual da páxina <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Atención: Vai volver crear unha páxina que xa foi eliminada anteriormente.'''\n\nDebería considerar se é apropiado continuar a editar esta páxina.\nVelaquí están o rexistro de borrados e mais o de traslados desta páxina, por se quere consultalos:",
        "moveddeleted-notice": "Esta páxina foi borrada.\nA continuación pódese ver o rexistro de borrados e traslados desta páxina, por se quere consultalos.",
        "moveddeleted-notice-recent": "Sentímolo, esta página foi borrada recentemente (dentro das últimas 24 horas).\nO rexistro de borrado e traslado da páxina amósanse abaixo como referencia.",
        "revdelete-legend": "Aplicar restricións de visibilidade",
        "revdelete-hide-text": "Texto da revisión",
        "revdelete-hide-image": "Agochar o contido do ficheiro",
-       "revdelete-hide-name": "Agochar o destino e os parámetros",
+       "revdelete-hide-name": "Agochar o contido da edición",
        "revdelete-hide-comment": "Resumo de edición",
        "revdelete-hide-user": "Nome de usuario ou enderezo IP do editor",
        "revdelete-hide-restricted": "Eliminar os datos da vista dos administradores así coma da doutros",
        "textmatches": "O texto da páxina coincide",
        "notextmatches": "Non se atopou o texto en ningunha páxina",
        "prevn": "{{PLURAL:$1|$1}} previas",
-       "nextn": "{{PLURAL:$1|$1}} seguintes",
+       "nextn": "{{PLURAL:$1|seguinte|$1 seguintes}}",
        "prev-page": "páxina anterior",
        "next-page": "páxina seguinte",
        "prevn-title": "{{PLURAL:$1|O resultado anterior|Os anteriores $1 resultados}}",
        "userrights": "Xestión dos dereitos de usuario",
        "userrights-lookup-user": "Administrar os grupos do usuario",
        "userrights-user-editname": "Escriba un nome de usuario:",
-       "editusergroup": "Editar os grupos do usuario",
+       "editusergroup": "Editar os grupos {{GENDER:$1|do usuario|da usuaria}}",
        "editinguser": "Mudando os dereitos {{GENDER:$1|do usuario|da usuaria}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Editar os grupos do usuario",
-       "saveusergroups": "Gardar os grupos do usuario",
+       "saveusergroups": "Gardar os grupos {{GENDER:$1|do usuario|da usuaria}}",
        "userrights-groupsmember": "Membro de:",
        "userrights-groupsmember-auto": "Membro implícito de:",
        "userrights-groups-help": "Pode cambiar os grupos aos que {{GENDER:$1|o usuario|a usuaria}} pertence:\n* Se a caixa ten un sinal (✓) significa que {{GENDER:$1|o usuario|a usuaria}} pertence a ese grupo.\n* Se, pola contra, non o ten, significa que non pertence.\n* Un asterisco (*) indica que non pode eliminar o grupo unha vez que o engadiu, e viceversa.",
        "right-createpage": "Crear páxinas (que non son de conversa)",
        "right-createtalk": "Crear páxinas de conversa",
        "right-createaccount": "Crear novas contas de usuario",
+       "right-autocreateaccount": "Acceder ao sistema automaticamente cunha conta de usuario externa",
        "right-minoredit": "Marcar as edicións como pequenas",
        "right-move": "Mover páxinas",
        "right-move-subpages": "Mover páxinas coas súas subpáxinas",
        "right-blockemail": "Bloquear un usuario fronte ao envío dun correo electrónico",
        "right-hideuser": "Bloquear un usuario, agochándollo ao público",
        "right-ipblock-exempt": "Evitar bloqueos de IPs, autobloqueos e bloqueos de rango",
-       "right-proxyunbannable": "Evitar os bloqueos autamáticos a proxies",
        "right-unblockself": "Desbloquearse a si mesmo",
        "right-protect": "Cambiar os niveis de protección e editar páxinas protexidas coa opción \"protección en serie\"",
        "right-editprotected": "Editar páxinas protexidas con \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Crear e borrar [[Special:Tags|tags]] da base de datos",
        "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] xunto cos cambios propios",
        "right-changetags": "Engadir e quitar [[Special:Tags|etiquetas]] arbitrarias a revisións individuais e entradas do rexistro",
+       "grant-generic": "conxunto de dereitos \"$1\"",
+       "grant-group-page-interaction": "Interactuar con páxinas",
+       "grant-group-file-interaction": "Interactuar con ficheiros multimedia",
+       "grant-group-watchlist-interaction": "Interactuar coa súa lista de vixilancia",
+       "grant-group-email": "Enviar correos electrónicos",
+       "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-other": "Outras actividades",
+       "grant-blockusers": "Bloquear e desbloquear usuarios",
+       "grant-createaccount": "Crear contas",
+       "grant-createeditmovepage": "Crear, editar e mover páxinas",
+       "grant-delete": "Borrar páxinas, revisións e entradas de rexistro",
+       "grant-editinterface": "Editar o espazo de nomes MediaWiki e o CSS/JavaScript de usuario",
+       "grant-editmycssjs": "Editar o seu CSS/JavaScript de usuario",
+       "grant-editmyoptions": "Editar as súas preferencias de usuario",
+       "grant-editmywatchlist": "Editar a súa lista de vixilancia",
+       "grant-editpage": "Editar páxinas existentes",
+       "grant-editprotected": "Editar páxinas protexidas",
+       "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-protect": "Protexer e desprotexer páxinas",
+       "grant-rollback": "Reverter os cambios feitos nas páxinas",
+       "grant-sendemail": "Enviar correos electrónicos a outros usuarios",
+       "grant-uploadeditmovefile": "Cargar, substituír e mover ficheiros",
+       "grant-uploadfile": "Cargar ficheiros novos",
+       "grant-basic": "Dereitos básicos",
+       "grant-viewdeleted": "Ver ficheiros e páxinas eliminadas",
+       "grant-viewmywatchlist": "Ver a súa lista de vixilancia",
        "newuserlogpage": "Rexistro de creación de usuarios",
        "newuserlogpagetext": "Este é un rexistro de creación de contas de usuario.",
        "rightslog": "Rexistro de dereitos de usuario",
        "action-createpage": "crear páxinas",
        "action-createtalk": "crear páxinas de conversa",
        "action-createaccount": "crear esta conta de usuario",
+       "action-autocreateaccount": "crear automaticamente esta conta de usuario externa",
        "action-history": "ver o historial desta páxina",
        "action-minoredit": "marcar esta edición como pequena",
        "action-move": "mover esta páxina",
        "recentchanges-legend-heading": "'''Lenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (véxase tamén a [[Special:NewPages|lista de páxinas novas]])",
        "recentchanges-legend-plusminus": "(''±123'')",
+       "recentchanges-submit": "Mostrar",
        "rcnotefrom": "A continuación {{PLURAL:$5|móstrase o cambio feito|móstranse os cambios feitos}} desde o <strong>$3</strong> ás <strong>$4</strong> (móstranse <strong>$1</strong> como máximo).",
        "rclistfrom": "Mostrar os cambios novos desde o $3 ás $2",
        "rcshowhideminor": "$1 as edicións pequenas",
        "upload-form-label-select-file": "Seleccionar un ficheiro",
        "upload-form-label-infoform-title": "Detalles",
        "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-name-tooltip": "Un título único descritivo para o ficheiro, que servirá como un nome de ficheiro. Pode usar unha linguaxe clara con espazos. Non inclúa a extensión do ficheiro.",
        "upload-form-label-infoform-description": "Descrición",
+       "upload-form-label-infoform-description-tooltip": "Describa brevemente todo o destacable acerca do traballo.\nPara unha foto, mencione as cousas principais que se representan, a ocasión ou o lugar.",
        "upload-form-label-usage-title": "Uso",
        "upload-form-label-usage-filename": "Nome do ficheiro",
        "foreign-structured-upload-form-label-own-work": "Isto é o meu propio traballo",
        "foreign-structured-upload-form-label-own-work-message-shared": "Certifico que son o propietario dos dereitos de autor deste ficheiro, e que concordo a liberar irrevocablemente este ficheiro a Wikimedia Commons baixo a licenza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], e que concordo cos [https://wikimediafoundation.org/wiki/Terms_of_Use Termos de uso].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Se non posúe os dereitos de autor deste ficheiro, ou quere liberalo baixo unha licenza diferente, considere usar o [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subas de Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Tamén pode interesarlle usar [[Special:Upload|a páxina de carga en {{SITENAME}}]], se o sitio permite a suba deste ficheiro nas súas políticas.",
+       "foreign-structured-upload-form-2-label-intro": "Grazas por doar unha imaxe para ser usada en {{SITENAME}}. Só debería continuar se se cumpren varias condicións:",
+       "foreign-structured-upload-form-2-label-ownwork": "Debe ser completamente <strong>creada por vostede</strong>, non só tomada de Internet",
+       "foreign-structured-upload-form-2-label-noderiv": "Non ten que conter <strong>traballo creado por outra persoa</strong>, nin estar  inspirado por outras obras",
+       "foreign-structured-upload-form-2-label-useful": "Debe ser <strong>educativo e útil</strong> para ensinar a outros",
+       "foreign-structured-upload-form-2-label-ccbysa": "Debe aceptar a <strong>publicación de forma irrevogable</strong> en Internet baixo a licenza [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]",
+       "foreign-structured-upload-form-2-label-alternative": "Se non todos os criterios de arriba son certos, aínda pode subir este ficheiro usando o [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subas de Commons], mentres estea dispoñible baixo unha licenza libre.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Ó subir o ficheiro, vostede da fe de que é o dono dos dereitos de autor neste ficheiro, e acepta irrevogablemente liberar este ficheiro a Wikimedia Commons baixo a licenza Creative Commons Attribution-ShareAlike 4.0, e acepta os [https://wikimediafoundation.org/wiki/Terms_of_Use Termos de Uso].",
+       "foreign-structured-upload-form-3-label-question-website": "Descargou esta imaxe dun sitio web, ou obtívoa dunha busca de imaxes?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Creou esta imaxe (tomou a foto, fixo o debuxo, etc) vostede mesmo?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Contén, ou está inspirada por, traballo propiedade de calquera outra persoa, como un logotipo?",
+       "foreign-structured-upload-form-3-label-yes": "Si",
+       "foreign-structured-upload-form-3-label-no": "Non",
+       "foreign-structured-upload-form-3-label-alternative": "Desafortunadamente, neste caso, esta ferramenta non é compatible para subir este ficheiro. Aínda pode ser capaz de subir este ficheiro usando o [https://commons.wikimedia.org/wiki/Special:UploadWizard Asistente de subas de Commons], mentres estea dispoñible baixo unha licenza libre.",
+       "foreign-structured-upload-form-4-label-good": "Ó usar esta ferramenta, pode subir gráficos educativos que crease e fotografías que fixese, que non conteñan traballo de ninguén máis.",
+       "foreign-structured-upload-form-4-label-bad": "Non pode subir imaxes atopadas nun motor de buscas ou descargadas doutros sitios web.",
        "backend-fail-stream": "Non se puido transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Non se puido facer unha copia de seguridade do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro \"$1\" non existe.",
        "mostrevisions": "Páxinas con máis revisións",
        "prefixindex": "Todas as páxinas con prefixo",
        "prefixindex-namespace": "Todas as páxinas con prefixo (espazo de nomes $1)",
+       "prefixindex-submit": "Mostrar",
        "prefixindex-strip": "Quitar o prefixo na lista",
        "shortpages": "Páxinas curtas",
        "longpages": "Páxinas longas",
        "protectedpages-performer": "Protector",
        "protectedpages-params": "Parámetros da protección",
        "protectedpages-reason": "Motivo",
+       "protectedpages-submit": "Mostrar páxinas",
        "protectedpages-unknown-timestamp": "Descoñecido",
        "protectedpages-unknown-performer": "Usuario descoñecido",
        "protectedtitles": "Títulos protexidos",
        "protectedtitles-summary": "Esta páxina lista os títulos que están protexidos actualmente fronte á creación. Para obter unha lista de páxinas existentes protexidas, consulte [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Actualmente non hai ningún título protexido con eses parámetros.",
+       "protectedtitles-submit": "Mostrar títulos",
        "listusers": "Lista de usuarios",
        "listusers-editsonly": "Mostrar só os usuarios con edicións",
        "listusers-creationsort": "Ordenar por data de creación",
        "usereditcount": "$1 {{PLURAL:$1|edición|edicións}}",
        "usercreated": "{{GENDER:$3|Creado|Creada}} o $1 ás $2",
        "newpages": "Páxinas novas",
+       "newpages-submit": "Mostrar",
        "newpages-username": "Nome de usuario:",
        "ancientpages": "Páxinas máis antigas",
        "move": "Mover",
        "movethispage": "Mover esta páxina",
        "unusedimagestext": "Os seguintes ficheiros existen pero aínda non se incluíron en ningunha páxina.\nPor favor, teña en conta que outras páxinas web poden ligar cara a un ficheiro mediante un enderezo URL directo e por iso poden aparecer listados aquí, mesmo estando en uso.",
-       "unusedcategoriestext": "Existen as seguintes categorías, aínda que ningún artigo ou categoría as emprega.",
+       "unusedcategoriestext": "As seguintes categorías están creadas, aínda que ningún artigo ou categoría fai uso delas.",
        "notargettitle": "Sen obxectivo",
        "notargettext": "Non especificou a páxina ou o usuario no cal levar a cabo esta función.",
        "nopagetitle": "Non existe esa páxina",
        "specialloguserlabel": "Executante:",
        "speciallogtitlelabel": "Obxectivo (título ou {{ns:user}}:nome de usuario):",
        "log": "Rexistros",
+       "logeventslist-submit": "Mostrar",
        "all-logs-page": "Todos os rexistros públicos",
        "alllogstext": "Vista combinada de todos os rexistros dipoñibles en {{SITENAME}}.\nPode precisar máis a vista seleccionando o tipo de rexistro, o nome do usuario ou o título da páxina afectada.",
        "logempty": "Non se atopou ningún elemento relacionado no rexistro.",
        "log-title-wildcard": "Procurar os títulos que comecen con este texto",
        "showhideselectedlogentries": "Mostrar/agochar as entradas do rexistro seleccionadas",
        "log-edit-tags": "Editar as etiquetas das entradas do registro seleccionadas",
+       "checkbox-select": "Seleccionar: $1",
+       "checkbox-all": "Todos",
+       "checkbox-none": "Ningún",
+       "checkbox-invert": "Invertir",
        "allpages": "Todas as páxinas",
        "nextpage": "Páxina seguinte ($1)",
        "prevpage": "Páxina anterior ($1)",
        "cachedspecial-viewing-cached-ts": "Está a ver unha versión da páxina gardada na caché, que pode non estar completamente actualizada.",
        "cachedspecial-refresh-now": "Ir á versión máis recente.",
        "categories": "Categorías",
+       "categories-submit": "Mostrar",
        "categoriespagetext": "{{PLURAL:$1|A seguinte categoría contén|As seguintes categorías conteñen}} páxinas ou contidos multimedia.\nAquí non se mostran as [[Special:UnusedCategories|categorías sen uso]].\nOlle tamén as [[Special:WantedCategories|categorías requiridas]].",
        "categoriesfrom": "Mostrar as categorías que comecen por:",
        "special-categories-sort-count": "ordenar por número",
        "activeusers-hidebots": "Agochar os bots",
        "activeusers-hidesysops": "Agochar os administradores",
        "activeusers-noresult": "Non se atopou ningún usuario.",
+       "activeusers-submit": "Mostrar usuarios activos",
        "listgrouprights": "Dereitos dun usuario segundo o seu grupo",
        "listgrouprights-summary": "A seguinte lista mostra os grupos de usuario definidos neste wiki, cos seus dereitos de acceso asociados.\nSe quere máis información acerca dos dereitos individuais, pode atopala [[{{MediaWiki:Listgrouprights-helppage}}|aquí]].",
        "listgrouprights-key": "Lenda:\n* <span class=\"listgrouprights-granted\">Dereito concedido</span>\n* <span class=\"listgrouprights-revoked\">Dereito revogado</span>",
        "listgrouprights-namespaceprotection-header": "Restricións dos espazos de nomes",
        "listgrouprights-namespaceprotection-namespace": "Espazo de nomes",
        "listgrouprights-namespaceprotection-restrictedto": "Dereito(s) que permite(n) ao usuario editar",
+       "listgrants": "Permisos",
+       "listgrants-summary": "Esta é unha lista de permisos cos seus accesos asoaciados aos permisos de usuario. Os usuarios poden autorizar aplicacións para usar a súa conta, pero con permisos limitados baseados nos permisos que o usuario dá á aplicación. Porén, unha aplicación que actúa no lugar do usuario non pode empregar permisos que o propio usuario non posúe.\nPode ver máis información sobre dereitos individuais [[{{MediaWiki:Listgrouprights-helppage}}|aquí]].",
+       "listgrants-grant": "Outorgar",
+       "listgrants-rights": "Dereitos",
        "trackingcategories": "Categorías de seguimento",
        "trackingcategories-summary": "Esta páxina lista as categorías de seguimento que o software de MediaWiki enche automaticamente. Pódense alterar os seus nomes modificando as correspondentes mensaxes do sistema no espazo de nomes \"{{ns:8}}\".",
        "trackingcategories-msg": "Categoría de seguimento",
        "wlshowlast": "Mostrar as últimas $1 horas e os últimos $2 días",
        "watchlistall2": "todo",
        "watchlist-hide": "Agochar",
+       "watchlist-submit": "Mostrar",
        "wlshowtime": "Periodo de tempo a amosar:",
        "wlshowhideminor": "edicións menores",
        "wlshowhidebots": "bots",
        "wlshowhideanons": "usuarios anónimos",
        "wlshowhidepatr": "edicións vixiadas",
        "wlshowhidemine": "as miñas edicións",
+       "wlshowhidecategorization": "categorización da páxina",
        "watchlist-options": "Opcións de vixilancia",
        "watching": "Vixiando...",
        "unwatching": "Deixando de vixiar...",
        "delete-confirm": "Borrar \"$1\"",
        "delete-legend": "Borrar",
        "historywarning": "<strong>Atención:</strong> A páxina que está a piques de borrar ten un historial con $1 {{PLURAL:$1|revisión|revisións}}:",
+       "historyaction-submit": "Mostrar",
        "confirmdeletetext": "Está a piques de borrar de xeito permanente unha páxina ou imaxe con todo o seu historial na base de datos.\nPor favor, confirme que é realmente a súa intención, que comprende as consecuencias e que está obrando de acordo coas regras [[{{MediaWiki:Policy-url}}|da política e normas]].",
        "actioncomplete": "Completouse a acción",
        "actionfailed": "Fallou a acción",
        "whatlinkshere-hidelinks": "$1 as ligazóns",
        "whatlinkshere-hideimages": "$1 as ligazóns ao ficheiro",
        "whatlinkshere-filters": "Filtros",
+       "whatlinkshere-submit": "Ir",
        "autoblockid": "Bloqueo automático nº$1",
        "block": "Bloquear un usuario",
        "unblock": "Desbloquear un usuario",
        "blockip": "Bloquear {{GENDER:$1|o usuario|a usuaria}}",
        "blockip-legend": "Bloquear un usuario",
-       "blockiptext": "Use o seguinte formulario para bloquear o acceso de escritura desde un enderezo IP ou para bloquear un usuario específico.\nIsto debería facerse só para previr vandalismo, e de acordo coa [[{{MediaWiki:Policy-url}}|política e normas]] vixentes.\nExplique a razón específica do bloqueo (por exemplo, citando as páxinas concretas que sufriron vandalismo).",
+       "blockiptext": "Use o seguinte formulario para bloquear o acceso de escritura desde un enderezo IP ou para bloquear un usuario específico.\nIsto debería facerse só para previr vandalismo, e de acordo coa [[{{MediaWiki:Policy-url}}|política e normas]] vixentes.\nExplique a razón específica do bloqueo abaixo (por exemplo, citando as páxinas concretas que sufriron vandalismo).\nPode bloquear intervalos IP coa sintaxe [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; o intervalo máis grande permitido é /$1 para IPv4 e /$2 para IPv6.",
        "ipaddressorusername": "Enderezo IP ou nome de usuario:",
        "ipbexpiry": "Duración:",
        "ipbreason": "Motivo:",
        "block-log-flags-hiddenname": "nome de usuario agochado",
        "range_block_disabled": "A funcionalidade de administrador de crear rangos de bloqueos está deshabilitada.",
        "ipb_expiry_invalid": "O tempo de duración non é válido.",
+       "ipb_expiry_old": "O tempo de expiración é no pasado.",
        "ipb_expiry_temp": "Os bloqueos a nomes de usuario agochados deberían ser permanentes.",
        "ipb_hide_invalid": "Non se pode suprimir esta conta; ten máis {{PLURAL:$1|dunha edición|de $1 edicións}}.",
        "ipb_already_blocked": "\"$1\" xa está bloqueado",
        "move-page-legend": "Mover unha páxina",
        "movepagetext": "Ao usar o formulario inferior vai cambiar o nome da páxina, movendo todo o seu historial ao novo nome.\nO título vello vaise converter nunha páxina de redirección ao novo título.\nPode actualizar automaticamente as redireccións que van dar ao título orixinal.\nSe escolle non facelo, asegúrese de verificar que non hai redireccións [[Special:DoubleRedirects|dobres]] ou [[Special:BrokenRedirects|crebadas]].\nVostede é responsable de asegurarse de que as ligazóns continúan a apuntar cara a onde se supón que deberían.\n\nTeña en conta que a páxina '''non''' será trasladada se xa existe unha páxina co novo título, a menos que esta última sexa unha redirección e non teña historial de edicións.\nIsto significa que pode volver renomear unha páxina ao seu nome antigo se comete un erro, e que non pode sobrescribir unha páxina que xa existe.\n\n'''Atención!'''\nEste cambio nunha páxina popular pode ser drástico e inesperado;\npor favor, asegúrese de que entende as consecuencias disto antes de proseguir.",
        "movepagetext-noredirectfixer": "Ao usar o formulario inferior vai cambiar o nome da páxina, movendo todo o seu historial ao novo nome.\nO título vello vaise converter nunha páxina de redirección ao novo título.\nAsegúrese de verificar que non hai redireccións [[Special:DoubleRedirects|dobres]] ou [[Special:BrokenRedirects|crebadas]].\nVostede é responsable de asegurarse de que as ligazóns continúan a apuntar cara a onde se supón que deberían.\n\nTeña en conta que a páxina '''non''' será trasladada se xa existe unha páxina co novo título, a menos que esta última sexa unha redirección e non teña historial de edicións.\nIsto significa que pode volver renomear unha páxina ao seu nome antigo se comete un erro, e que non pode sobrescribir unha páxina que xa existe.\n\n'''Atención!'''\nEste cambio nunha páxina popular pode ser drástico e inesperado;\npor favor, asegúrese de que entende as consecuencias disto antes de proseguir.",
-       "movepagetalktext": "Se marca esta caixa, a páxina de conversa asociada trasladarase automáticamente ó título novo a menos que xa exista unha páxina de conversa non baleira alí.\n\nNeste caso, deberá trasladar ou fusionar manualmente a páxina se así o quere.",
+       "movepagetalktext": "Se marca esta caixa, a páxina de conversa asociada moverase automaticamente ó título novo a menos que xa exista unha páxina de conversa non baleira alí.\n\nNeste caso, deberá trasladar ou fusionar manualmente a páxina se así o desexa.",
        "moveuserpage-warning": "'''Aviso:''' Está a piques de mover unha páxina de usuario. Por favor, teña en conta que só se trasladará a páxina e que o usuario '''non''' será renomeado.",
        "movecategorypage-warning": "'''Aviso:''' Está a piques de mover unha páxina de categoría. Por favor, teña en conta que só se trasladará a páxina e que as páxinas presentes na categoría vella '''non''' serán recategorizadas na categoría nova.",
        "movenologintext": "Debe ser un usuario rexistrado e [[Special:UserLogin|acceder ao sistema]] para mover unha páxina.",
        "export-download": "Gardar como un ficheiro",
        "export-templates": "Incluír os modelos",
        "export-pagelinks": "Engadir as páxinas ligadas a unha profundidade de:",
+       "export-manual": "Engadir páxinas manualmente:",
        "allmessages": "Mensaxes do sistema",
        "allmessagesname": "Nome",
        "allmessagesdefault": "Texto predeterminado",
        "tooltip-pt-anonuserpage": "A páxina de usuario do enderezo IP desde o que está a editar",
        "tooltip-pt-mytalk": "A súa páxina de conversa",
        "tooltip-pt-anontalk": "Conversa acerca de edicións feitas desde este enderezo IP",
-       "tooltip-pt-preferences": "As miñas preferencias",
+       "tooltip-pt-preferences": "As as preferencias",
        "tooltip-pt-watchlist": "A lista de páxinas cuxas modificacións está a seguir",
        "tooltip-pt-mycontris": "Lista das súas contribucións",
        "tooltip-pt-anoncontribs": "Unha lista de modificacións feitas desde esta dirección IP",
        "tooltip-t-recentchangeslinked": "Cambios recentes nas páxinas ligadas desde esta",
        "tooltip-feed-rss": "Fonte de novas RSS desta páxina",
        "tooltip-feed-atom": "Fonte de novas Atom desta páxina",
-       "tooltip-t-contributions": "Ver a lista de contribucións {{GENDER:{{BASEPAGENAME}}|deste usuario|desta usuaria}}",
-       "tooltip-t-emailuser": "Enviarlle unha mensaxe a {{GENDER:{{BASEPAGENAME}}|este usuario|esta usuaria}} por correo electrónico",
+       "tooltip-t-contributions": "Lista de contribucións {{GENDER:$1|deste usuario|desta usuaria}}",
+       "tooltip-t-emailuser": "Enviarlle unha mensaxe de correo electrónico a {{GENDER:$1|este usuario|esta usuaria}}",
        "tooltip-t-info": "Máis información sobre esta páxina",
        "tooltip-t-upload": "Subir ficheiros",
        "tooltip-t-specialpages": "Lista de todas as páxinas especiais",
        "pageinfo-category-files": "Número de ficheiros",
        "markaspatrolleddiff": "Marcar como revisada",
        "markaspatrolledtext": "Marcar esta páxina como revisada",
+       "markaspatrolledtext-file": "Marcar esta versión de ficheiro como verificada",
        "markedaspatrolled": "Marcar como revisado",
        "markedaspatrolledtext": "A revisión seleccionada de \"[[:$1]]\" foi marcada como revisada.",
        "rcpatroldisabled": "A patrulla dos cambios recentes está desactivada",
        "newimages-legend": "Filtro",
        "newimages-label": "Nome do ficheiro (ou parte del):",
        "newimages-showbots": "Mostrar as cargas feitas por bots",
+       "newimages-hidepatrolled": "Ocultar as subas verificadas",
        "noimages": "Non hai imaxes para ver.",
        "ilsubmit": "Procurar",
        "bydate": "por data",
        "exif-compression-6": "JPEG (vello)",
        "exif-copyrighted-true": "Con dereitos de autoría",
        "exif-copyrighted-false": "Dereitos de autoría non definidos",
+       "exif-photometricinterpretation-1": "Negro e branco (negro é 0)",
        "exif-unknowndate": "Data descoñecida",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Volteada horizontalmente",
        "scarytranscludefailed-httpstatus": "[Fallou a busca do modelo \"$1\": HTTP $2]",
        "scarytranscludetoolong": "[O enderezo URL é demasiado longo]",
        "deletedwhileediting": "'''Aviso:''' Esta páxina foi borrada despois de que comezase a editala!",
-       "confirmrecreate": "O usuario [[User:$1|$1]] ([[User talk:$1|conversa]]) borrou esta páxina despois de que vostede comezara a editala, dando o seguinte motivo:\n: ''$2''\nPor favor, confirme que realmente quere recrear esta páxina.",
-       "confirmrecreate-noreason": "O usuario [[User:$1|$1]] ([[User talk:$1|conversa]]) borrou esta páxina despois de que vostede comezara a editala. Por favor, confirme que realmente quere recrear esta páxina.",
+       "confirmrecreate": "O usuario [[User:$1|$1]] ([[User talk:$1|conversa]]) {{GENDER:$1|borrou}} esta páxina despois de que vostede comezara a editala, dando o seguinte motivo:\n: <em>$2</em>\nPor favor, confirme que realmente quere recrear esta páxina.",
+       "confirmrecreate-noreason": "O usuario [[User:$1|$1]] ([[User talk:$1|conversa]]) {{GENDER:$1|borrou}} esta páxina despois de que vostede comezara a editala. Por favor, confirme que realmente quere recrear esta páxina.",
        "recreate": "Recrear",
        "confirm_purge_button": "Aceptar",
        "confirm-purge-top": "Quere limpar a memoria caché desta páxina?",
        "watchlisttools-edit": "Ver e editar a lista de vixilancia",
        "watchlisttools-raw": "Editar a lista de vixilancia simple",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|conversa]])",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "<strong>Aviso:</strong> A clave de ordenación por defecto \"$2\" anula a clave de ordenación anterior por defecto \"$1\".",
        "duplicate-displaytitle": "'''Aviso:''' O título mostrado \"$2\" anula o título anterior \"$1\".",
        "invalid-indicator-name": "<strong>Erro:</strong> O atributo <code>name</code> dos indicadores do estado da páxina non pode estar baleiro.",
        "version-libraries-license": "Licenza",
        "version-libraries-description": "Descrición",
        "version-libraries-authors": "Autores",
-       "redirect": "Redirixir por nome de ficheiro, ID de usuario, ID de páxina ou ID de revisión",
+       "redirect": "Redirixir por nome de ficheiro, usuario, páxina, modificación ou identificador de rexistro",
        "redirect-legend": "Redirixir a un ficheiro ou unha páxina",
-       "redirect-summary": "Esta páxina especial redirixe cara a un ficheiro (dado o nome), unha páxina (dado o ID da páxina ou o dunha revisión) ou unha páxina de usuario (dado o ID dun usuario). Utilización: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] ou [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Esta páxina especial redirixe cara a un ficheiro (dado o nome), unha páxina (dado o ID da páxina ou o dunha revisión) ou unha páxina de usuario (dado o ID dun usuario), ou unha entrada do rexistro (dada polo ID do rexistro). Utilización: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] ou [[{{#Special:Redirect}}/user/101]], ou [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Continuar",
        "redirect-lookup": "Procurar:",
        "redirect-value": "Valor:",
        "redirect-page": "ID de páxina",
        "redirect-revision": "Revisión de páxina",
        "redirect-file": "Nome de ficheiro",
+       "redirect-logid": "Identificador de rexistro",
        "redirect-not-exists": "Non se atopou o valor",
        "fileduplicatesearch": "Procurar ficheiros duplicados",
        "fileduplicatesearch-summary": "Procurar ficheiros duplicados a partir do valor de <i>hash</i> (un mecanismo de comprobación).",
        "tags-deactivate": "desactivar",
        "tags-hitcount": "$1 {{PLURAL:$1|modificación|modificacións}}",
        "tags-manage-no-permission": "Non ten os permisos necesarios para modificar etiquetas.",
+       "tags-manage-blocked": "Non pode acceder á interface de modificación de etiquetas mentres estea bloqueado.",
        "tags-create-heading": "Crear unha nova etiqueta",
        "tags-create-explanation": "Por defecto, as etiquetas creadas recentemente poderán ser empregadas polos usuarios e os bots.",
        "tags-create-tag-name": "Nome da etiqueta:",
        "tags-deactivate-not-allowed": "Non é posible reactivar a páxina \"$1\".",
        "tags-deactivate-submit": "Desactivar",
        "tags-apply-no-permission": "Non ten permisos para aplicar etiquetas de cambios xunto cos seus tus cambios.",
+       "tags-apply-blocked": "Non pode aplicar os cambios das etiquetas cos seus cambios mentras estea bloqueado.",
        "tags-apply-not-allowed-one": "A etiqueta \"$1\" non se puede aplicar manualmente.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta non se pode|As seguintes etiquetas non se poden}} aplicar manualmente: $1",
        "tags-update-no-permission": "Non ten permisos para engadir ou quitar etiquetas de cambio das revisións individuais ou das entradas do rexistro.",
+       "tags-update-blocked": "Non pode engadir nin quitar de modificación etiquetas mentras estea bloqueado.",
        "tags-update-add-not-allowed-one": "A etiqueta \"$1\" non se pode engadir manualmente.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta non se pode|As seguintes etiquetas non se poden}} engadir manualmente: $1",
        "tags-update-remove-not-allowed-one": "A etiqueta \"$1\" non se pode eliminar.",
        "expand_templates_preview": "Vista previa",
        "expand_templates_preview_fail_html": "<em>Dado que o código HTML puro está activado en {{SITENAME}} e produciuse unha perda dos datos da sesión, a vista previa está oculta como precaución contra ataques mediante código JavaScript.</em>\n\n<strong>Se este é un intento lexítimo de acceso á vista previa, inténteo de novo.</strong>\nSe segue sen funcionar, probe a [[Special:UserLogout|saír]] e volver a entrar coa súa conta.",
        "expand_templates_preview_fail_html_anon": "<em>Dado que o código HTML puro está activado en {{SITENAME}} e produciuse unha perda dos datos da sesión, a vista previa está oculta como precaución contra ataques mediante código JavaScript.</em>\n\n<strong>Se este é un intento lexítimo de acceso á vista previa, probe a [[Special:UserLogout|saír]] e volver a entrar coa súa conta.</strong>",
+       "expand_templates_input_missing": "Necesita proporcionar polo menos algún texto de entrada.",
        "pagelanguage": "Selector de lingua da páxina",
        "pagelang-name": "Páxina",
        "pagelang-language": "Lingua",
        "pagelang-use-default": "Utilizar a lingua por defecto",
        "pagelang-select-lang": "Seleccionar a lingua",
+       "pagelang-submit": "Enviar",
        "right-pagelang": "Cambiar a lingua da páxina",
        "action-pagelang": "cambiar a lingua da páxina",
        "log-name-pagelang": "Rexistro de cambios de lingua",
        "mediastatistics": "Estatísticas do contido multimedia",
        "mediastatistics-summary": "Estatísticas sobre os tipos de ficheiros enviados. Isto inclúe unicamente a última versión de cada ficheiro. As versións vellas ou borradas dos ficheiros quedan excluídas.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Tamaño total de ficheiro para esta sección: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Tamaño total de ficheiro para todos os ficheiros: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "Tipo MIME",
        "mediastatistics-table-extensions": "Extensións posibles",
        "mediastatistics-table-count": "Número de ficheiros",
        "mediastatistics-header-text": "Texto",
        "mediastatistics-header-executable": "Executables",
        "mediastatistics-header-archive": "Formatos comprimidos",
+       "mediastatistics-header-total": "Todos os ficheiros",
        "json-warn-trailing-comma": "{{PLURAL:$1|Eliminouse $1 coma final|Elimináronse $1 comas finais}} do JSON.",
        "json-error-unknown": "Houbo un problema co JSON. Erro: $1",
        "json-error-depth": "Superouse o número máximo de ficheiros apartados.",
        "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."
+       "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",
+       "sessionprovider-nocookies": "As cookies poden estar desactivadas. Asegúrese de que ten activas as cookies e comece de novo.",
+       "randomrootpage": "Páxina raíz ao chou"
 }
index 41f1ec9..e4f19df 100644 (file)
        "resettokens-no-tokens": "S git kei Token zum Zrucksetze.",
        "resettokens-tokens": "Token:",
        "resettokens-token-label": "$1 (aktuälle Wärt: $2)",
-       "resettokens-watchlist-token": "Token fir dr Webfeed (Atom/RSS) vu dr [[Special:Watchlist|Änderigen an Syten uf Dyre Beobachtuislischt]]",
+       "resettokens-watchlist-token": "Token fir dr Webfeed (Atom/RSS) vu dr [[Special:Watchlist|Änderigen an Syten uf Dyre Beobachtigslischt]]",
        "resettokens-done": "Token zruckgsetzt.",
        "resettokens-resetbutton": "Uusgwehlti Token zrucksetze",
        "bold_sample": "fetti Schrift",
        "prefs-help-prefershttps": "Die Ystellig würkt sech uus, we du di ds nächste Mal amäldisch.",
        "prefswarning-warning": "Du hesch Ystellige g’änderet wo no nid gspycheret sy.\nWe du die Syte verlasch, ohni uf «$1» z drücke, de gö die Änderige verlore.",
        "prefs-tabs-navigation-hint": "Tipp: Mit der linggen u der rächte Pfyltaste chasch zwüsche de Tabs wächsle.",
-       "email-address-validity-valid": "Siht giltig uus",
-       "email-address-validity-invalid": "S brucht e giltigi Adräss!",
        "userrights": "Benutzerrächtsverwaltig",
        "userrights-lookup-user": "Verwalt d Gruppezuegherigkeit",
        "userrights-user-editname": "Benutzername:",
        "right-blockemail": "Benutzer am Verschicke vu E-Mail hindere",
        "right-hideuser": "E Benutzername sperre un verberge",
        "right-ipblock-exempt": "Uusnahm vu IP-Sperrine, Autoblock und Rangesperre",
-       "right-proxyunbannable": "Uusnahm vu automatische Proxysperrine",
        "right-unblockself": "Sich sälber entsperre",
        "right-protect": "Syteschutzstatus änderen un kaskadegschitzti Syte bearbeite",
        "right-editprotected": "Mit «{{int:protect-level-sysop}}» gschützti Syten ändere",
        "mw-widgets-dateinput-placeholder-month": "JJJJ-MM",
        "mw-widgets-titleinput-description-new-page": "d Syte git’s no nid",
        "mw-widgets-titleinput-description-redirect": "Wyterleitig uf $1",
-       "api-error-blacklisted": "Bitte due en andre, ussagechräftigere Titel usswääle."
+       "api-error-blacklisted": "Bitte due en andre, ussagechräftigere Titel usswääle.",
+       "randomrootpage": "Zuefelligi Stammsyte"
 }
index c86a511..5112bf8 100644 (file)
        "viewtalkpage": "ચર્ચા જુઓ",
        "otherlanguages": "અન્ય ભાષાઓમાં",
        "redirectedfrom": "($1 થી અહીં વાળેલું)",
-       "redirectpagesub": "પાનà«\81àª\82 àª\85નà«\8dયતà«\8dર àªµàª¾àª³à«\8b",
+       "redirectpagesub": "દિશાનિરà«\8dદà«\87શ àª\95રà«\87લ àªªàª¾àª¨à«\81àª\82",
        "redirectto": "દિશાનિર્દેશિત",
        "lastmodifiedat": "આ પાનામાં છેલ્લો ફેરફાર $1ના રોજ $2 વાગ્યે થયો.",
        "viewcount": "આ પાનું {{PLURAL:$1|એક|$1}} વખત જોવામાં આવ્યું છે.",
        "viewyourtext": "આપ આ પાનાનાં '''આપનાં સંપાદનો'''નો મૂળ સ્રોત નિહાળી શકો છો અને તેની નકલ (copy) પણ કરી શકો છો:",
        "protectedinterface": "આ પાનું સોફ્ટવેર માટે ઇન્ટરફેઇસ ટેક્સટ આપે છે, અને તેને દુરુપયોગ રોકવા માટે સ્થગિત કર્યું છે.\nબધાંજ વિકિ માટે ભાષાંતર ઉમેરવા કે બદલવા માટે, કૃપા કરી [//translatewiki.net/ translatewiki.net], મિડિયાવિકિ સ્થાનિયકરણ પ્રકલ્પ, વાપરો.",
        "editinginterface": "<strong>ચેતવણી:</strong> તમે જે પાનામાં ફેરફાર કરી રહ્યા છો તે પાનું સોફ્ટવેર માટે ઇન્ટરફેસ ટેક્સટ પૂરી પાડે છે.\nઅહીંનો બદલાવ આ વિકિ પર ઉપસ્થિત અન્ય સભ્યોના ઇન્ટરફેસનાં દેખાવ ઉપર અસરકર્તા બનશે.",
-       "cascadeprotected": "આ પાના પર ફેરફાર પ્રતિબંધિત છે કેમ કે આ પાનું  {{PLURAL:$1|એવું પાનું|એવા પાના}} માં શામિલ છે જેમાં અનુવર્તી (પગથિયામય)સંરક્ષણ સક્રીય છે :\n$2",
+       "cascadeprotected": "આ પાના પર ફેરફાર પ્રતિબંધિત છે, કારણ કે આ પાનું {{PLURAL:$1|એવા પાનાં}}નું સભ્ય છે કે જેમાં અનુવર્તી(પગથિયામય) સંરક્ષણ લાગુ કરવામાં આવ્યું છે :\n$2",
        "namespaceprotected": "તમને '''$1''' નામાવકાશનાં પાનાંમાં ફેરફાર કરવાની પરવાનગી નથી.",
        "customcssprotected": "તમને આ પાનું બદલવાની પરવાનગી નથી કારણકે આ પાનામાં બીજા સભ્યની પસંદગીના સેટીંગ્સ છે.",
        "customjsprotected": "તમને આ JavaScript પાનું બદલવાની પરવાનગી નથી કારણકે આ પાનામાં બીજા સભ્યની પસંદગીના સેટીંગ્સ છે.",
        "nav-login-createaccount": "પ્રવેશ કરો / નવું ખાતું ખોલો",
        "userlogin": "પ્રવેશ કરો / નવું ખાતું ખોલો",
        "userloginnocreate": "પ્રવેશ",
-       "logout": "બહાર àª¨à«\80àª\95ળà«\8b",
+       "logout": "પà«\8dરસà«\8dથાન",
        "userlogout": "પ્રસ્થાન/લૉગ આઉટ",
        "notloggedin": "પ્રવેશ કરેલ નથી",
        "userlogin-noaccount": "શું તમારૂં ખાતું નથી?",
        "pt-login": "પ્રવેશ",
        "pt-login-button": "પ્રવેશો",
        "pt-createaccount": "ખાતું બનાવો",
-       "pt-userlogout": "બહાર àª¨à«\80àª\95ળà«\8b",
+       "pt-userlogout": "પà«\8dરસà«\8dથાન",
        "php-mail-error-unknown": "PHPની મેલ() કામગીરીમાં અજ્ઞાત ત્રુટિ",
        "user-mail-no-addy": "ઈ મેલ એડ્રસ વગર ઈ મેલ મોકલવા પ્રયત્ન કરેલ.",
        "user-mail-no-body": "કોરો કે નાનકડો ઈમેઇલ મોકલવાનો પ્રયાસ કરાયો.",
        "diff-multi-sameuser": "(સમાન સભ્ય દ્વારા {{PLURAL:$1|કરાયેલ એક પુનરાવર્તન| કરાયેલા $1 પુનરાવર્તનો}} દર્શાવેલ નથી)",
        "diff-multi-manyusers": "{{PLURAL:$2|એક સભ્યએ કરેલું|$2 સભ્યોએ કરેલા}} ({{PLURAL:$1|વચગાળાનું એક પુનરાવર્તન દર્શાવ્યં|વચગાળાનાં $1 પુનરાવર્તનો દર્શાવ્યાં}} નથી.)",
        "searchresults": "પરિણામોમાં શોધો",
-       "searchresults-title": "પરિણામોમાં \"$1\" શોધો",
+       "searchresults-title": "\"$1\" માટેના પરિણામો",
        "titlematches": "પાનાનું શીર્ષક મળતું આવે છે",
        "textmatches": "પાનાના શબ્દો મળતાં આવે છે",
        "notextmatches": "આ શબ્દ કોઈ પાનામાં મળ્યો નથી",
        "right-blockemail": "સભ્યના ઇ-મેલ મોકલવા પર પ્રતિબંધ મૂકો",
        "right-hideuser": "સભ્યનામ પર પ્રતિબંધ મૂકો જેથી તે લોકોને ન દેખાય.",
        "right-ipblock-exempt": "IP દ્વારા, સ્વયં ચાલિત રીતે અને સમૂહ રોકને અવગનીને આગળ વધો",
-       "right-proxyunbannable": "અવેજીના અવયંચાલિત ખંડોને ટાળો",
        "right-unblockself": "તમને જાતે જ અપ્રતિબંધિત કરો",
        "right-protect": "સંરક્ષણ સ્તર બદલો અને ધોધાકાર-સંરક્ષિત પાનાઓમાં ફેરફાર કરો.",
        "right-editprotected": "પાનામાં \"{{int:protect-level-sysop}}\" તરીકે ફેરફાર કરો",
        "cantrollback": "આ ફેરફારો ઉલટાવી નહી શકાય\nછેલ્લો ફેરફાર આ પાના ના રચયિતા દ્વારા જ થયો હતો",
        "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''\".",
-       "revertpage": "[[Special:Contributions/$2|$2]] ([[User talk:$2|talk]])દ્વારા ફેરફરોને  [[User:$1|$1]] દ્વારા કરેલા છેલ્લા સુધારા સુધી ઉલટાવાયા.",
+       "revertpage": "[[Special:Contributions/$2|$2]] ([[User talk:$2|talk]]) દ્વારા કરેલ ફેરફારોને  [[User:$1|$1]] દ્વારા કરેલા છેલ્લા સુધારા સુધી ઉલટાવાયા.",
        "revertpage-nouser": "ગુપ્ત સભ્ય વડે કરાયેલ ફેરફારને {{GENDER:$1|[[User:$1|$1]]}} વડે કરેલ છેલ્લા પુનરાવર્તન પર પાછા લઇ જવાયું.",
        "rollback-success": "$1 દ્વારા થયેલા ફેરફારો ઉલટાવાયા\nતેને $2 દ્વારા થયેલ સંપાદન સુધી લઇ જવાયું",
        "sessionfailure-title": "સત્ર નિષ્ફળ",
        "restriction-level-sysop": "સંપૂર્ણ સંરક્ષિત",
        "restriction-level-autoconfirmed": "અર્ધ સંરક્ષિત",
        "restriction-level-all": "કોઈ પણ સ્તર",
-       "undelete": "ભà«\82àª\82સાડà«\87લા àªªàª¾àª¨àª¾ àª¬àª¤àª¾àªµà«\8b",
+       "undelete": "ભૂંસેલા પાના બતાવો",
        "undeletepage": "હટાવેલ પાના જુઓ અને પુનર્જીવિત કરો",
        "undeletepagetitle": "'''નીચે [[:$1|$1]] ના ભૂંસાડેલ સંપાદનો છે.'''.",
        "viewdeletedpage": "ભૂંસેલા પાના બતાવો",
        "contributions": "{{GENDER:$1|સભ્ય}}નું યોગદાન",
        "contributions-title": "સભ્ય $1નું યોગદાન",
        "mycontris": "યોગદાન",
+       "anoncontribs": "યોગદાનો",
        "contribsub2": "($2) માટે {{GENDER:$3|$1}}",
        "nocontribs": "આ પરિમાણને મળતી પરિણામ નથી મળ્યાં",
        "uctop": "(વર્તમાન)",
        "javascripttest-pagetext-frameworks": "નીચેનામાંથી કોઈ એક ચકાસણી ફ્રેમવર્ક પસંદ કરો : $1",
        "javascripttest-pagetext-skins": "ચકાસણી કરવા માટેની સ્કીન પસંદ કરો",
        "javascripttest-qunit-intro": "mediawiki.org પર  [$1 testing documentation] તપાસ માહિતી જુઓ",
-       "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-mycontris": "{{GENDER:|તમારા}} યોગદાનોની યાદી",
        "tooltip-pt-login": "આપને પ્રવેશ કરવા ભલામણ કરવામાં આવે છે, જોકે તે આવશ્યક નથી",
-       "tooltip-pt-logout": "બહાર àª¨à«\80àª\95ળà«\8b",
+       "tooltip-pt-logout": "પà«\8dરસà«\8dથાન",
        "tooltip-pt-createaccount": "તમને ખાતું બનાવીને પ્રવેશ કરવા માટે આમંત્રણ છે; તેમ કરવું જોકે, આવશ્યક નથી",
        "tooltip-ca-talk": "અનુક્રમણિકાનાં પાના વિષે ચર્ચા",
        "tooltip-ca-edit": "આ પાનામાં ફેરફાર કરો",
        "tooltip-t-recentchangeslinked": "આ પાના પરની કડીઓ વાળા લેખોમાં તાજેતરમાં થયેલા ફેરફારો",
        "tooltip-feed-rss": "આ પાના માટે આર.એસ.એસ. ફીડ",
        "tooltip-feed-atom": "આ પાના માટે એટમ ફીડ",
-       "tooltip-t-contributions": "આ સભ્યનાં યોગદાનોની યાદી",
+       "tooltip-t-contributions": "{{GENDER:$1|આ સભ્ય}} વડે કરાયેલા યોગદાનોની યાદી",
        "tooltip-t-emailuser": "આ સભ્યને ઇ-મેલ મોકલો",
        "tooltip-t-info": "આ પાનાં વિષે વધુ માહિતી",
        "tooltip-t-upload": "ફાઇલ ચડાવો",
index c00a8cc..8370eb8 100644 (file)
@@ -31,7 +31,8 @@
                        "Ldorfman",
                        "LaG roiL",
                        "Eraab",
-                       "Geagea"
+                       "Geagea",
+                       "פוילישער"
                ]
        },
        "tog-underline": "סימון קישורים בקו תחתי:",
        "october-date": "$1 באוקטובר",
        "november-date": "$1 בנובמבר",
        "december-date": "$1 בדצמבר",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|קטגוריה|קטגוריות}}",
        "category_header": "דפים בקטגוריה \"$1\"",
        "subcategories": "קטגוריות משנה",
        "virus-scanfailed": "הסריקה נכשלה (קוד: $1)",
        "virus-unknownscanner": "אנטי־וירוס בלתי ידוע:",
        "logouttext": "'''יצאתם זה עתה מהחשבון.'''\n\nשימו לב כי ייתכן שדפים אחדים ימשיכו להיות מוצגים כאילו אתם עדיין מחוברים לחשבון עד שתנקו את המטמון של הדפדפן שלכם.",
+       "cannotlogoutnow-title": "לא ניתן לצאת מהחשבון עכשיו",
+       "cannotlogoutnow-text": "היציאה אינה אפשרית בעת שימוש ב{{GRAMMAR:תחילית|$1}}.",
        "welcomeuser": "ברוך בואך, $1!",
        "welcomecreation-msg": "חשבונך נוצר.\nבאפשרותך להתאים את [[Special:Preferences|ההעדפות]] שלך ב{{grammar:תחילית|{{SITENAME}}}}.",
        "yourname": "שם משתמש:",
        "remembermypassword": "שמירת הכניסה שלי בדפדפן הזה ({{PLURAL:$1|ליום אחד|ליומיים|ל־$1 ימים}} לכל היותר)",
        "userlogin-remembermypassword": "לזכור שנכנסתי",
        "userlogin-signwithsecure": "שימוש בחיבור מאובטח",
+       "cannotloginnow-title": "לא ניתן להיכנס עכשיו",
+       "cannotloginnow-text": "הכניסה אינה אפשרית בעת שימוש ב{{GRAMMAR:תחילית|$1}}.",
        "yourdomainname": "המתחם שלך:",
        "password-change-forbidden": "אין באפשרותך לשנות סיסמאות באתר זה.",
        "externaldberror": "אירעה שגיאת אימות בבסיס הנתונים, או שאינך מורשה לעדכן את החשבון החיצוני שלך.",
        "resetpass_submit": "הגדרת הסיסמה וכניסה לחשבון",
        "changepassword-success": "סיסמתך שונתה בהצלחה!",
        "changepassword-throttled": "ביצעתם לאחרונה ניסיונות רבים מדי להיכנס לחשבון זה.\nאנא המתינו $1 לפני שתנסו שוב.",
+       "botpasswords": "ססמאות בוט",
+       "botpasswords-summary": "<em>ססמאות בוט</em> מאפשרות כנסה לחשבון משתמש דרך API ללא שימוש בנתוני ההזדהות הראשיים של החשבון. הרשאות המשתמש שזמינות למשתמש שנכנס עם ססמת בוט יכולות להיות מוגבלות.",
+       "botpasswords-disabled": "ססמאות בוט כבויות.",
+       "botpasswords-no-central-id": "כדי להשתמש בססמאות בוט, יש להיכנס לחשבון מרכזי.",
+       "botpasswords-existing": "ססמאות בוט קיימות",
+       "botpasswords-createnew": "יצירת ססמת בוט חדשה",
+       "botpasswords-editexisting": "עריכת ססמת בוט קיימת",
+       "botpasswords-label-appid": "שם הבוט:",
+       "botpasswords-label-create": "יצירה",
+       "botpasswords-label-update": "עדכון",
+       "botpasswords-label-cancel": "ביטול",
+       "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": "ססמת בוט נוצרה",
+       "botpasswords-created-body": "ססמת הבוט \"$1\" נוצרה בהצלחה.",
+       "botpasswords-updated-title": "ססמת הבוט עודכנה",
+       "botpasswords-updated-body": "ססמת הבוט \"$1\" עודכנה בהצלחה.",
+       "botpasswords-deleted-title": "ססמת הבוט נמחקה",
+       "botpasswords-deleted-body": "ססמת הבוט \"$1\" נמחקה.",
+       "botpasswords-newpassword": "הססמה החדשה לכניסה <strong>$1</strong> היא <strong>$2</strong>. נא לרשום את זה לעיון עתידי.",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider אינו זמין.",
+       "botpasswords-restriction-failed": "הגבלות של ססמת בוט מונעות את הכניסה הזאת.",
+       "botpasswords-invalid-name": "שם המשתמש שניתן אינו מכיל את פריד ססמאות הבוט (\"$1\").",
+       "botpasswords-not-exist": "למשתמש \"$1\" אין ססמת בוט בשם \"$2\".",
        "resetpass_forbidden": "לא ניתן לשנות סיסמאות.",
        "resetpass-no-info": "נדרשת כניסה לחשבון כדי לגשת לדף זה באופן ישיר.",
        "resetpass-submit-loggedin": "שינוי סיסמה",
        "passwordreset-emailtext-ip": "מישהו (ככל הנראה אתם, מכתובת ה־IP מספר $1) ביקש איפוס של\nהסיסמה שלכם ב{{grammar:תחילית|{{SITENAME}}}} ($4). {{PLURAL:$3|חשבון המשתמש הבא שייך|חשבונות המשתמש הבאים שייכים}}\nלכתובת הדואר האלקטרוני הזאת:\n\n$2\n\n{{PLURAL:$3|סיסמה זמנית זו תפקע|סיסמאות זמניות אלה יפקעו}} תוך {{PLURAL:$5|יום|יומיים|$5 ימים}}.\nעליכם להיכנס ולבחור סיסמה חדשה עכשיו. אם מישהו אחר ביצע בקשה זו, או שנזכרתם בסיסמתכם\nהמקורית ואינכם רוצים עוד לשנות אותה, באפשרותכם להתעלם מהודעה זו ולהמשיך להשתמש בסיסמה\nהישנה.",
        "passwordreset-emailtext-user": "{{GENDER:$1|המשתמש|המשתמשת}} $1 ב{{GRAMMAR:תחילית|{{SITENAME}}}} {{GENDER:$1|ביקש|ביקשה}} איפוס של הסיסמה שלכם ב{{GRAMMAR:תחילית|{{SITENAME}}}}\n($4). {{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": "×\90×\9d ×\96×\95×\94×\99 ×\9bת×\95×\91ת ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 ×¨×©×\95×\9e×\94 ×¢×\91×\95ר ×\94חשבון שלך, אז יישלח דואר אלקטרוני לאיפוס הסיסמה.",
-       "passwordreset-emailsentusername": "×\90×\9d ×¨×©×\95×\9e×\94 ×\9bת×\95×\91ת ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 ×\9eת×\90×\99×\9eה, אז יישלח דואר אלקטרוני לאיפוס הסיסמה.",
+       "passwordreset-emailsentemail": "×\90×\9d ×\9bת×\95×\91ת ×\94×\93×\95×\90ר ×\94×\90×\9cק×\98ר×\95× ×\99 ×\94×\96×\90ת ×\9eש×\95×\99×\9bת ×\9cחשבון שלך, אז יישלח דואר אלקטרוני לאיפוס הסיסמה.",
+       "passwordreset-emailsentusername": "×\90×\9d ×\99ש ×\9bת×\95×\91ת ×\93×\95×\90ר ×\90×\9cק×\98ר×\95× ×\99 ×©×\9eש×\95×\99×\9bת ×\9cש×\9d ×\94×\9eשת×\9eש ×\94×\96ה, אז יישלח דואר אלקטרוני לאיפוס הסיסמה.",
        "passwordreset-emailsent-capture": "נשלח דואר אלקטרוני לאיפוס הסיסמה, והוא מוצג להלן.",
        "passwordreset-emailerror-capture": "נוצר דואר אלקטרוני לאיפוס הסיסמה, והוא מוצג להלן, אך שליחתו ל{{GENDER:$2|משתמש|משתמשת}} נכשלה: $1",
        "changeemail": "שינוי או הסרת כתובת דוא\"ל",
        "userrights": "ניהול הרשאות משתמש",
        "userrights-lookup-user": "ניהול קבוצות משתמש",
        "userrights-user-editname": "שם משתמש:",
-       "editusergroup": "עריכת קבוצות משתמשים",
+       "editusergroup": "עריכת קבוצות {{GENDER:$1|משתמשים}}",
        "editinguser": "שינוי ההרשאות של {{GENDER:$1|המשתמש|המשתמשת}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "עריכת קבוצות משתמש",
-       "saveusergroups": "שמירת קבוצות משתמש",
+       "saveusergroups": "שמירת קבוצות {{GENDER:$1|משתמש}}",
        "userrights-groupsmember": "{{GENDER:$2|חבר|חברה}} ב{{PLURAL:$1|קבוצה|קבוצות}}:",
        "userrights-groupsmember-auto": "{{GENDER:$2|חבר|חברה}} אוטומטית ב{{PLURAL:$1|קבוצה|קבוצות}}:",
        "userrights-groups-help": "באפשרותכם לשנות את הקבוצות שמשתמש זה חבר בהן:\n* תיבה מסומנת פירושה שהמשתמש חבר בקבוצה.\n* תיבה בלתי מסומנת פירושה שהמשתמש אינו חבר בקבוצה.\n* סימון * פירושו שלא תוכלו להסיר משתמש מהקבוצה מרגע שהוספתם אותו אליה, או להפך.",
        "right-createpage": "יצירת דפים שאינם דפי שיחה",
        "right-createtalk": "יצירת דפי שיחה",
        "right-createaccount": "יצירת חשבונות משתמש חדשים",
+       "right-autocreateaccount": "כניסה אוטומטית עם חשבון משתמש חיצוני",
        "right-minoredit": "סימון עריכות כמשניות",
        "right-move": "העברת דפים",
        "right-move-subpages": "העברת דפים עם דפי המשנה שלהם",
        "right-blockemail": "חסימת משתמש משליחת דואר אלקטרוני",
        "right-hideuser": "חסימת שם משתמש תוך הסתרתו מהציבור",
        "right-ipblock-exempt": "עקיפת חסימות של כתובת IP, חסימות אוטומטיות וחסימות טווח",
-       "right-proxyunbannable": "עקיפת חסימות אוטומטיות של שרתי פרוקסי",
        "right-unblockself": "שחרור חסימה של המשתמש עצמו",
        "right-protect": "שינוי רמות הגנה ועריכת דפים המוגנים בהגנה מדורגת",
        "right-editprotected": "עריכת דפים המוגנים ברמת \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "יצירת ומחיקת [[Special:Tags|תגיות]] מבסיס הנתונים",
        "right-applychangetags": "החלת [[Special:Tags|תגיות]] יחד עם שינויים",
        "right-changetags": "הוספת והסרה של [[Special:Tags|תגיות]] כלשהן לגרסאות מסוימות ולרשומות יומן",
+       "grant-generic": "חבילת ההרשאות \"$1\"",
+       "grant-group-page-interaction": "פעילות הידודית בדפים",
+       "grant-group-file-interaction": "פעילות הידודית במדיה",
+       "grant-group-watchlist-interaction": "פעילות הידודית ברשימת המעקב שלך",
+       "grant-group-email": "שליחת דוא\"ל",
+       "grant-group-high-volume": "ביצוי פעולות מרובות",
+       "grant-group-customization": "התאמה אישית והעדפות",
+       "grant-group-administration": "ביצוע פעולות ניהול",
+       "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-protect": "הפעלת הגנה על דפים והסרתה",
+       "grant-rollback": "שחזור שינויים בדפים",
+       "grant-sendemail": "שליחת דואר אלקטרוני למשתמשים אחרים",
+       "grant-uploadeditmovefile": "העלאת קבצים, החלפתם והעברתם.",
+       "grant-uploadfile": "העלאת קבצים חדשים",
+       "grant-basic": "הרשאות בסיסיות",
+       "grant-viewdeleted": "צפייה בקבצים ודפים שנמחקו",
+       "grant-viewmywatchlist": "צפייה ברשימת מעקב שלך",
        "newuserlogpage": "יומן רישום משתמשים",
        "newuserlogpagetext": "זהו יומן המכיל הרשמות של משתמשים.",
        "rightslog": "יומן תפקידים",
        "action-createpage": "ליצור דפים",
        "action-createtalk": "ליצור דפי שיחה",
        "action-createaccount": "ליצור את חשבון המשתמש הזה",
+       "action-autocreateaccount": "ליצור את חשבון המשתמש החיצוני הזה",
        "action-history": "לצפות בהיסטוריה של דף זה",
        "action-minoredit": "לסמן עריכה זו כמשנית",
        "action-move": "להעביר דף זה",
        "upload-form-label-select-file": "בחירת קובץ",
        "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": "שם הקובץ",
        "foreign-structured-upload-form-label-own-work": "אני יצרתי את הקובץ",
        "zip-wrong-format": "הקובץ שצוין אינו קובץ ZIP.",
        "zip-bad": "הקובץ הוא קובץ ZIP פגום או בלתי קריא מסיבה אחרת.\nלא ניתן לבצע עליו בדיקת אבטחה כנדרש.",
        "zip-unsupported": "קובץ זה הוא קובץ ZIP המשתמש בתכונות ZIP שאינן נתמכות על ידי מדיה־ויקי.\nלא ניתן לבצע עליו בדיקת אבטחה כנדרש.",
-       "uploadstash": "×\9e×\90×\92ר העלאות",
+       "uploadstash": "ס×\9c×\99ק העלאות",
        "uploadstash-summary": "דף זה מאפשר גישה לקבצים שהועלו (או נמצאים בתהליך העלאה), אך טרם פורסמו באתר הוויקי. קבצים אלה אינם גלויים לאיש מלבד המשתמש שהעלה אותם.",
-       "uploadstash-clear": "×\9e×\97×\99קת ×\94ק×\91צ×\99×\9d ×\91×\9e×\90×\92ר",
-       "uploadstash-nofiles": "×\90×\99×\9f ×\9c×\9b×\9d ×§×\91צ×\99×\9d ×\91×\9e×\90×\92ר.",
+       "uploadstash-clear": "×\9e×\97×\99קת ×\94ק×\91צ×\99×\9d ×\91ס×\9c×\99ק",
+       "uploadstash-nofiles": "×\90×\99×\9f ×\9c×\9b×\9d ×§×\91צ×\99×\9d ×\91ס×\9c×\99ק.",
        "uploadstash-badtoken": "ביצוע הפעולה נכשל, אולי בגלל פקיעת תוקפו של אסימון העריכה שלכם. נסו שוב.",
        "uploadstash-errclear": "מחיקת הקבצים נכשלה.",
        "uploadstash-refresh": "רענון רשימת הקבצים",
        "log-title-wildcard": "חיפוש כותרות המתחילות באותיות אלה",
        "showhideselectedlogentries": "הצגת/הסתרת פעולות היומן שנבחרו",
        "log-edit-tags": "עריכת התגיות של רשומות היומן שנבחרו",
+       "checkbox-select": "בחירה: $1",
+       "checkbox-all": "הכול",
+       "checkbox-none": "לא כלום",
+       "checkbox-invert": "להפוך",
        "allpages": "כל הדפים",
        "nextpage": "הדף הבא ($1)",
        "prevpage": "הדף הקודם ($1)",
        "listgrouprights-namespaceprotection-header": "הגבלות על מרחבי שם",
        "listgrouprights-namespaceprotection-namespace": "מרחב השם",
        "listgrouprights-namespaceprotection-restrictedto": "ההרשאה או ההרשאות המאפשרות למשתמשים לערוך",
+       "listgrants": "זיכיונות",
+       "listgrants-summary": "להלן רשימת זיכיונות עם הגישות להרשאות משתמש שמשויכות אליהן. משתמשים יכולים לאשר ליישומים להשתמש בחשבון שלהם, אבל עם הרשאות מוגבלות בהתאם לזיכיון שהשמשתמש נתן ליישום. יישום שפועל בשמו של המשתמש אינו יכול להשתמש בהרשאות שאין למשתמש.\nייתכן שיש [[{{MediaWiki:Listgrouprights-helppage}}|מידע נוסף]] על הרשאות פרטניות.",
+       "listgrants-grant": "זיכיון",
+       "listgrants-rights": "הרשאות",
        "trackingcategories": "קטגוריות מעקב",
        "trackingcategories-summary": "דף זה כולל רשימה של קטגוריות מעקב, שנוצרות אוטומטית על־ידי תוכנת מדיה‏‏־ויקי. ניתן לשנות את שמותיהן על‏‏־ידי שינוי הודעות המערכת הרלוונטיות במרחב השם \"{{ns:8}}\".",
        "trackingcategories-msg": "קטגוריית מעקב",
        "wlshowhideanons": "משתמשים אנונימיים",
        "wlshowhidepatr": "עריכות בדוקות",
        "wlshowhidemine": "עריכות שלי",
+       "wlshowhidecategorization": "הוספות והסרות של דפים מקטגוריות",
        "watchlist-options": "אפשרויות ברשימת המעקב",
        "watching": "בהוספה לרשימת המעקב…",
        "unwatching": "בהסרה מרשימת המעקב…",
        "unblock": "שחרור משתמש",
        "blockip": "חסימת {{GENDER:$1|משתמש|משתמשת}}",
        "blockip-legend": "חסימת משתמש",
-       "blockiptext": "השתמשו בטופס שלהלן כדי לחסום את הרשאות הכתיבה מכתובת IP או משתמש מסוימים.\nחסימות כאלה צריכות להתבצע רק כדי למנוע השחתה, ובהתאם ל[[{{MediaWiki:Policy-url}}|נהלים]].\nאנא מלאו את הסיבה הפרטנית לחסימה להלן (לדוגמה, באמצעות ציון דפים מסוימים שהשחית המשתמש).",
+       "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 או שם משתמש:",
        "ipbexpiry": "פקיעה:",
        "ipbreason": "סיבה:",
        "block-log-flags-hiddenname": "שם המשתמש הוסתר",
        "range_block_disabled": "האפשרות לחסום טווח כתובות אינה פעילה.",
        "ipb_expiry_invalid": "זמן פקיעת החסימה אינו תקין.",
+       "ipb_expiry_old": "זמן תפוגה בעבר.",
        "ipb_expiry_temp": "חסימות הכוללות הסתרת שם משתמש חייבות להיות לזמן בלתי מוגבל.",
        "ipb_hide_invalid": "לא ניתן להעלים את החשבון הזה; {{PLURAL:$1|בוצעה ממנו יותר מעריכה אחת|בוצעו ממנו יותר מ‏‏֫־$1 עריכות}}.",
        "ipb_already_blocked": "המשתמש \"$1\" כבר נחסם.",
        "lockedbyandtime": "(על־ידי $1 ב־$3, $2)",
        "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אנא ודאו שאין [[Special:DoubleRedirects|הפניות כפולות]] או [[Special:BrokenRedirects|שבורות]].\nאתם אחראים לוודא שכל הקישורים ימשיכו להצביע למקום שאליו הם אמורים להצביע.\n\nשימו לב: הדף '''לא''' יועבר אם כבר יש דף תחת השם החדש, אלא אם הדף הזה הוא הפניה ואין לו היסטוריית עריכות קודמות.\nפירוש הדבר שאפשר לשנות חזרה את שמו של דף לשם המקורי אם נעשתה טעות, ושלא ניתן לדרוס דף קיים.\n\n'''אזהרה!'''\nשינוי זה עשוי להיות שינוי דרסטי ובלתי צפוי לדף פופולרי;\nאנא ודאו שאתם מבינים את השלכות המעשה לפני שאתם ממשיכים.",
+       "movepagetext": "שימוש בטופס שלהלן ישנה את שמו של דף, ויעביר את כל ההיסטוריה שלו לשם חדש.\nהשם הישן יהפוך לדף הפניה אל הדף עם השם החדש.\nבאפשרותכם לעדכן אוטומטית דפי הפניה לכותרת המקורית.\nאם תבחרו לא לעשות זאת, אנא ודאו שאין [[Special:DoubleRedirects|הפניות כפולות]] או [[Special:BrokenRedirects|שבורות]].\nאתם אחראים לוודא שכל הקישורים ימשיכו להצביע למקום שאליו הם אמורים להצביע.\n\nשימו לב: הדף <strong>לא</strong> יועבר אם כבר יש דף תחת השם החדש, אלא אם הדף השני הוא הפניה ואין לו היסטוריית עריכות קודמות.\nפירוש הדבר שאפשר לשנות חזרה את שמו של דף לשם המקורי אם נעשתה טעות, ושלא ניתן לדרוס דף קיים.\n\n<strong>לתשומת לבכם:</strong>\nשינוי זה עשוי להיות שינוי דרסטי ובלתי צפוי לדף פופולרי;\nאנא ודאו שאתם מבינים את השלכות המעשה לפני שאתם ממשיכים.",
+       "movepagetext-noredirectfixer": "שימוש בטופס שלהלן ישנה את שמו של דף, ויעביר את כל ההיסטוריה שלו לשם חדש.\nהשם הישן יהפוך לדף הפניה אל הדף עם השם החדש.\nאנא ודאו שאין [[Special:DoubleRedirects|הפניות כפולות]] או [[Special:BrokenRedirects|שבורות]].\nאתם אחראים לוודא שכל הקישורים ימשיכו להצביע למקום שאליו הם אמורים להצביע.\n\nשימו לב: הדף <strong>לא</strong> יועבר אם כבר יש דף תחת השם החדש, אלא אם הדף הזה הוא הפניה ואין לו היסטוריית עריכות קודמות.\nפירוש הדבר שאפשר לשנות חזרה את שמו של דף לשם המקורי אם נעשתה טעות, ושלא ניתן לדרוס דף קיים.\n\n<strong>לתשומת לבכם:</strong>\nשינוי זה עשוי להיות שינוי דרסטי ובלתי צפוי לדף פופולרי;\nאנא ודאו שאתם מבינים את השלכות המעשה לפני שאתם ממשיכים.",
        "movepagetalktext": "אם התיבה הזאת מסומנת, דף השיחה של דף זה יועבר אוטומטית לכותרת החדשה, אלא אם קיים שם דף שיחה שאינו ריק.\n\nבמקרה הזה, תצטרכו להעביר או לשלב את הדפים באופן ידני, אם תרצו.",
        "moveuserpage-warning": "'''אזהרה:''' אתם עומדים להעביר דף משתמש. שימו לב שרק הדף יועבר וששם המשתמש '''לא''' ישתנה.",
        "movecategorypage-warning": "<strong>אזהרה:</strong> אתם עומדים להעביר דף קטגוריה. שימו לב שרק הדף יועבר ושכל הדפים בקטגוריה הישנה <strong>לא</strong> יסווגו לקטגוריה החדשה.",
        "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": "כותרות המקור והיעד זהות; לא ניתן להעביר דף לעצמו.",
        "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}}]] ללא היסטוריית השינויים שלו.",
        "export-download": "שמירה כקובץ",
        "export-templates": "לכלול תבניות",
        "export-pagelinks": "לכלול דפים מקושרים עד לעומק של:",
+       "export-manual": "הוספה ידנית של דפים:",
        "allmessages": "הודעות המערכת",
        "allmessagesname": "שם",
        "allmessagesdefault": "טקסט ברירת המחדל של ההודעה",
        "javascripttest-pagetext-frameworks": "נא לבחור אחת מסביבות הבדיקות הבאות: $1",
        "javascripttest-pagetext-skins": "בחירת עיצוב שאיתו יורצו הבדיקות:",
        "javascripttest-qunit-intro": "ראו את [$1 תיעוד הבדיקות] באתר mediawiki.org.",
-       "tooltip-pt-userpage": "דף המשתמש שלך",
+       "tooltip-pt-userpage": "דף {{GENDER:|המשתמש|המשתמשת}} שלך",
        "tooltip-pt-anonuserpage": "דף המשתמש של משתמש אנונימי זה",
        "tooltip-pt-mytalk": "דף השיחה שלך",
        "tooltip-pt-anontalk": "שיחה על תרומות המשתמש האנונימי",
        "tooltip-t-recentchangeslinked": "השינויים האחרונים שבוצעו בדפים המקושרים מדף זה",
        "tooltip-feed-rss": "הזנת RSS עבור דף זה",
        "tooltip-feed-atom": "הזנת Atom עבור דף זה",
-       "tooltip-t-contributions": "תרומותיו של משתמש זה",
-       "tooltip-t-emailuser": "שליחת דואר אלקטרוני למשתמש זה",
+       "tooltip-t-contributions": "{{GENDER:$1|תרומותיו של משתמש זה|תרומותיה של משתמשת זו}}",
+       "tooltip-t-emailuser": "שליחת דואר אלקטרוני {{GENDER:$1|למשתמש זה|למשתמשת זו}}",
        "tooltip-t-info": "מידע נוסף על דף זה",
        "tooltip-t-upload": "העלאת קבצים",
        "tooltip-t-specialpages": "רשימת כל הדפים המיוחדים",
        "pageinfo-category-files": "מספר הקבצים",
        "markaspatrolleddiff": "סימון השינוי כבדוק",
        "markaspatrolledtext": "סימון דף זה כבדוק",
+       "markaspatrolledtext-file": "סימון גרסת קובץ זו כבדוקה",
        "markedaspatrolled": "השינוי סומן כבדוק",
        "markedaspatrolledtext": "השינוי שבחרת בדף [[:$1]] סומן כבדוק.",
        "rcpatroldisabled": "אפשרות סימון השינויים כבדוקים מבוטלת",
        "newimages-legend": "מסנן",
        "newimages-label": "שם הקובץ (או חלק ממנו):",
        "newimages-showbots": "הצגת העלאות שבוצעו על־ידי בוטים",
+       "newimages-hidepatrolled": "הסתרת העלאות בדוקות",
        "noimages": "אין קבצים.",
        "ilsubmit": "חיפוש",
        "bydate": "לפי תאריך",
        "hebrew-calendar-m11-gen": "באב",
        "hebrew-calendar-m12-gen": "באלול",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|שיחה]])",
+       "timezone-local": "מקומי",
        "duplicate-defaultsort": "'''אזהרה:''' המיון הרגיל \"$2\" דורס את המיון הרגיל המוקדם ממנו \"$1\".",
        "duplicate-displaytitle": "<strong>אזהרה:</strong> כותרת התצוגה \"$2\" דורסת את כותרת התצוגה הקודמת \"$1\".",
        "invalid-indicator-name": "<strong>שגיאה:</strong> התכונה <code>name</code> של מצייני מצב הדף אינה יכולה להיות ריקה.",
        "version-libraries-license": "רישיון",
        "version-libraries-description": "תיאור",
        "version-libraries-authors": "יוצרים",
-       "redirect": "×\94פנ×\99×\94 ×\9cפ×\99 ×©×\9d ×§×\95×\91×¥, ×\9eספר ×\9eשת×\9eש, ×\9eספר ×\93×£ ×\90×\95 ×\9eספר ×\92רס×\94",
+       "redirect": "×\94פנ×\99×\94 ×\9cפ×\99 ×§×\95×\91×¥, ×\9eשת×\9eש, ×\93×£, ×\92רס×\94 ×\90×\95 ×\9e×\96×\94×\94 ×\99×\95×\9e×\9d×\9f",
        "redirect-legend": "הפניה לקובץ או לדף",
-       "redirect-summary": "דף מיוחד זה מפנה לקובץ (בהינתן שם הקובץ), לדף (בהינתן מספר גרסה או מספר דף), או לדף משתמש (בהינתן מספר משתמש). דוגמאות לשימוש: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], או [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "דף מיוחד זה מפנה לקובץ (בהינתן שם הקובץ), לדף (בהינתן מספר גרסה או מספר דף), או לדף משתמש (בהינתן מספר משתמש), או עיול יומן (בהינתן מזהה יומן). דוגמאות לשימוש: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], או [[{{#Special:Redirect}}/user/101]], או [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "מעבר",
        "redirect-lookup": "סוג:",
        "redirect-value": "ערך:",
        "redirect-page": "מספר דף",
        "redirect-revision": "גרסת דף",
        "redirect-file": "שם קובץ",
+       "redirect-logid": "מזהה יומן",
        "redirect-not-exists": "הערך לא נמצא",
        "fileduplicatesearch": "חיפוש קבצים כפולים",
        "fileduplicatesearch-summary": "חיפוש קבצים כפולים על בסיס ערכי הגיבוב שלהם.",
        "api-error-overwrite": "לא מותרת החלפת קובץ קיים.",
        "api-error-stashfailed": "שגיאה פנימית: השרת נכשל באחסון הקובץ הזמני.",
        "api-error-publishfailed": "שגיאה פנימית: השרת נכשל בפרסום הקובץ הזמני.",
-       "api-error-stasherror": "×\94×\99×\99ת×\94 ×©×\92×\99×\90×\94 ×\91×\94×¢×\9c×\90ת ×\94ק×\95×\91×¥ ×\9c×\9e×\90×\92ר.",
-       "api-error-stashedfilenotfound": "×\94ק×\95×\91×¥ ×©×\91×\9e×\90×\92ר ×\9c×\90 × ×\9eצ×\90 ×\91עת ×\94× ×\99ס×\99×\95×\9f ×\9c×\94×¢×\9c×\95ת ×\90×\95ת×\95 ×\9e×\94×\9e×\90×\92ר.",
+       "api-error-stasherror": "×\94×\99×\99ת×\94 ×©×\92×\99×\90×\94 ×\91×\94×¢×\9c×\90ת ×\94ק×\95×\91×¥ ×\9cס×\9c×\99ק.",
+       "api-error-stashedfilenotfound": "×\94ק×\95×\91×¥ ×\94×\9e×\95ס×\9cק ×\9c×\90 × ×\9eצ×\90 ×\91עת ×\94× ×\99ס×\99×\95×\9f ×\9c×\94×¢×\9c×\95ת ×\90×\95ת×\95 ×\9e×\94ס×\9c×\99ק.",
        "api-error-stashpathinvalid": "הנתיב שבו הקובץ שבמאגר אמור היה להימצא היה בלתי תקין.",
-       "api-error-stashfilestorage": "×\94×\99×\99ת×\94 ×©×\92×\99×\90×\94 ×\91עת ×\90×\97ס×\95×\9f ×\94ק×\95×\91×¥ ×\91×\9e×\90×\92ר.",
+       "api-error-stashfilestorage": "×\94×\99×\99ת×\94 ×©×\92×\99×\90×\94 ×\91עת ×\90×\97ס×\95×\9f ×\94ק×\95×\91×¥ ×\91ס×\9c×\99ק.",
        "api-error-stashzerolength": "השרת לא יכול היה לאחסן במאגר את הקובץ, כי אורכו היה אפס.",
-       "api-error-stashnotloggedin": "× ×\93רשת ×\9b× ×\99ס×\94 ×\9c×\97ש×\91×\95×\9f ×\9b×\93×\99 ×\9cש×\9e×\95ר ×§×\91צ×\99×\9d ×\91×\9e×\90×\92ר ההעלאות.",
+       "api-error-stashnotloggedin": "× ×\93רשת ×\9b× ×\99ס×\94 ×\9c×\97ש×\91×\95×\9f ×\9b×\93×\99 ×\9cש×\9e×\95ר ×§×\91צ×\99×\9d ×\91ס×\9c×\99ק ההעלאות.",
        "api-error-stashwrongowner": "הקובץ שניסית לגשת אליו במאגר אינו שייך לך.",
-       "api-error-stashnosuchfilekey": "×\9eפת×\97 ×\94ק×\95×\91×¥ ×©× ×\99ס×\99ת ×\9c×\92שת ×\90×\9c×\99×\95 ×\91×\9e×\90×\92ר אינו קיים.",
+       "api-error-stashnosuchfilekey": "×\9eפת×\97 ×\94ק×\95×\91×¥ ×©× ×\99ס×\99ת ×\9c×\92שת ×\90×\9c×\99×\95 ×\91ס×\9c×\99ק אינו קיים.",
        "api-error-timeout": "השרת לא השיב בזמן המצופה.",
        "api-error-unclassified": "אירעה שגיאה בלתי ידועה.",
        "api-error-unknown-code": "שגיאה בלתי ידועה: \"$1\".",
        "expand_templates_preview": "תצוגה מקדימה",
        "expand_templates_preview_fail_html": "<em>מכיוון שב{{GRAMMAR:תחילית|{{SITENAME}}}} מופעלת הצגת HTML גולמית ואירע אבדן מידע כניסה, התצוגה המקדימה מוסתרת, וזאת כאמצעי זהירות מפני התקפות JavaScript.</em>\n\n<strong>אם זה ניסיון תקין להציג תצוגה מקדימה, יש לנסות שוב.</strong>\nאם זה עדיין לא עובד, יש לנסות [[Special:UserLogout|לצאת מהחשבון]] ולהיכנס שוב.",
        "expand_templates_preview_fail_html_anon": "<em>מכיוון שב{{GRAMMAR:תחילית|{{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 בתים}} ($2; $3%)",
+       "mediastatistics-bytespertype": "הגודל הכולל של הקבצים בפרק זה: {{PLURAL:$1|בית אחד|$1 בתים}} ($2; $3%).",
+       "mediastatistics-allbytes": "הגודל הכולל של כל הקבצים: {{PLURAL:$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": "{{PLURAL:$1|פסיק מסיים אחד הוסר|$1 פסיקים מסיימים הוסרו}} מטקסט ה־JSON",
        "json-error-unknown": "הייתה בעיה עם טקסט ה־JSON. שגיאה: $1",
        "json-error-depth": "הייתה חריגה מהעומק המקסימלי של המחסנית",
        "mw-widgets-dateinput-no-date": "לא נבחר תאריך",
        "mw-widgets-titleinput-description-new-page": "הדף עדיין לא קיים",
        "mw-widgets-titleinput-description-redirect": "הפניה ל{{GRAMMAR:תחילית|$1}}",
-       "api-error-blacklisted": "נא לבחור כותרת אחרת, המתארת טוב יותר את התוכן."
+       "api-error-blacklisted": "נא לבחור כותרת אחרת, המתארת טוב יותר את התוכן.",
+       "sessionmanager-tie": "לא ניתן לצרף מספר סוגי אימות זהות: $1.",
+       "sessionprovider-generic": "התחברויות של $1",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "התחברויות מבוססות־עוגיות",
+       "sessionprovider-nocookies": "אפשר לכבות עוגיות. יש לוודא שהעוגיות מופעלות ולהתחיל מחדש.",
+       "randomrootpage": "דף שורש אקראי"
 }
index 82a42ee..c8e9d1b 100644 (file)
@@ -68,7 +68,9 @@
                        "Matma Rex",
                        "Angpradesh",
                        "Sfic",
-                       "Niharika29"
+                       "Niharika29",
+                       "जनक राज भट्ट",
+                       "YmKavishwar"
                ]
        },
        "tog-underline": "कड़ियाँ अधोरेखन:",
        "yourdomainname": "आपका डोमेन:",
        "password-change-forbidden": "आप इस विकि पर कूटशब्द नहीं बदल सकते हैं।",
        "externaldberror": "या तो प्रमाणिकरण डाटाबेस में त्रुटि हुई है या फिर आपको अपना बाह्य खाता अपडेट करने की अनुमति नहीं है।",
-       "login": "लà¥\89à¤\97 à¤\87न",
+       "login": "पà¥\8dरवà¥\87श",
        "nav-login-createaccount": "सत्रारंभ / खाता खोलें",
        "userlogin": "सत्रारंभ / खाता खोलें",
        "userloginnocreate": "लॉग इन",
        "right-blockemail": "सदस्यों को ई-मेल भेजने से ब्लॉक करें",
        "right-hideuser": "सदस्यनाम ब्लॉक करें और उसे लोगों से छुपाएँ",
        "right-ipblock-exempt": "आइ॰पी ब्लॉक्स, ऑटो-ब्लॉक्स और रेंज ब्लॉक्स को नज़रंदाज़ करें",
-       "right-proxyunbannable": "स्वचालित प्रौक्सी ब्लॉक्स को नज़रंदाज़ करें",
        "right-unblockself": "स्वयं को अनावरुद्ध करें",
        "right-protect": "सुरक्षा स्तर बदलें और सीढ़ी-सुरक्षित पृष्ठ सम्पादित करें",
        "right-editprotected": "उन सुरक्षित पृष्ठ सम्पादित करें जिनके सम्पादन की \"{{int:protect-level-sysop}}\"",
        "log-title-wildcard": "इस पाठ से शुरू होने वाले शीर्षक खोजें",
        "showhideselectedlogentries": "चयनित लॉग प्रविष्टियाँ दिखाएँ/छुपाएँ",
        "log-edit-tags": "चुने गए लॉग प्रविक्तियों एक सम्पादन टैग",
+       "checkbox-select": "चुनें: $1",
+       "checkbox-all": "सभी",
        "allpages": "सभी पृष्ठ",
        "nextpage": "अगला पृष्ठ ($1)",
        "prevpage": "पिछला पृष्ठ ($1)",
        "hebrew-calendar-m11-gen": "एवी (Av)",
        "hebrew-calendar-m12-gen": "एलुल (Elul)",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|वार्ता]])",
+       "timezone-local": "स्थानीय",
        "duplicate-defaultsort": "'''Warning:''' पुरानी मूल क्रमांकन कुंजी \"$1\" के बजाय अब मूल क्रमांकन कुंजी \"$2\" होगी।",
        "duplicate-displaytitle": "<strong>चेतावनी:</strong> शीर्षक दिखाएँ \"$2\" पूर्व दिखाए गए शीर्षक \"$1\" पर छा रहा है।",
        "invalid-indicator-name": "<strong>त्रुटि:</strong> पृष्ठ स्थिति सांकेतक <code>नाम</code> गुण खाली नहीं रहना चाहिए।",
        "mw-widgets-dateinput-no-date": "कुछ चयनित नहीं",
        "mw-widgets-titleinput-description-new-page": "पृष्ठ अभी मौजूद नहीं है",
        "mw-widgets-titleinput-description-redirect": "$1 को अनुप्रेषित",
-       "api-error-blacklisted": "कृपया कोई दूसरा विवरणात्मक शीर्षक चुनें।"
+       "api-error-blacklisted": "कृपया कोई दूसरा विवरणात्मक शीर्षक चुनें।",
+       "randomrootpage": "अविशिष्ट मूल पृष्ठ"
 }
index 319a29f..7362565 100644 (file)
@@ -23,6 +23,7 @@
        "tog-hideminor": "Chhota aur nawaa badlao ke lukao",
        "tog-hidepatrolled": "Pahraa dewa gais badlao ke nawaa badlao me se lukao",
        "tog-newpageshidepatrolled": "Pahraa dewa gais badlao ke nawaa panna me se lukao",
+       "tog-hidecategorization": "Panna ke categorization ke lukao",
        "tog-extendwatchlist": "Dhyaan suchi ke khol ke sab badlao ke dekhao, khaali nawaa waala nai",
        "tog-usenewrc": "Dher jan se badla gais panna, haali ke badlao aur dhyan suchi me",
        "tog-numberheadings": "Sab heading ke apne se number karo",
        "tog-watchlisthidebots": "Bot waala badlao ke hamaar dhyaan suchi se lukao",
        "tog-watchlisthideminor": "Mamuli badlao ke hamaar dhyaan suchi se lukao",
        "tog-watchlisthideliu": "Logged in sadasya ke badlao ke dhyan suchi se lukao",
+       "tog-watchlistreloadautomatically": "Jab fillter ke badla jaae hae tab dhyan suchi ke automatically upload karo",
        "tog-watchlisthideanons": "Bina naam ke sadasya ke badlao ke dhyan suchi se lukao",
        "tog-watchlisthidepatrolled": "Pahraa dewa gais badlao ke dhyan suchi me se lukao",
+       "tog-watchlisthidecategorization": "Panna ke categorization ke lukao.",
        "tog-ccmeonemails": "Jon e-mail ham duusra sadasya ke lage bhejtaa hai uske copy hamaar lage bhi bhejo",
        "tog-diffonly": "Diff ke niche panna ke content ke nai dekhao",
        "tog-showhiddencats": "Lukawal waala vibhag ke dekhao",
        "morenotlisted": "Ii suchi puura nai hae",
        "mypage": "Panna",
        "mytalk": "Baat",
-       "anontalk": "Ii IP khatir bichar",
+       "anontalk": "Baat",
        "navigation": "Navigation",
        "and": "&#32;aur",
        "qbfind": "Khojo",
        "disclaimers": "Jimmewari se chhutkaari",
        "disclaimerpage": "Project:Saadharan jimmewari nai lo",
        "edithelp": "Badlao pe madat",
+       "helppage-top-gethelp": "Madat",
        "mainpage": "Pahila Panna",
        "mainpage-description": "Pahila Panna",
        "policy-url": "Project:Niti",
        "nstab-template": "Template",
        "nstab-help": "Madat waala panna",
        "nstab-category": "Vibhag",
+       "mainpage-nstab": "Pahila panna",
        "nosuchaction": "Koi aisan kaam nai hai",
        "nosuchactiontext": "Jon kaam ke URL kare ke batais hai uske ii wiki nai pahachane hai\nSaait aap URL ke thiik se type nai karaa hai, nai to galat jorr ke follow karaa hai.\nIi saait ii kaaran se bhi hoe ki  jon software {{SITENAME}} use kare hai, me bug hai",
        "nosuchspecialpage": "Aisan koi khaas panna nai hai",
        "databaseerror-query": "Khoj:$1",
        "databaseerror-function": "Kaam: $1",
        "databaseerror-error": "Galti: $1",
+       "transaction-duration-limit-exceeded": "High replication lag se bache ke khatir, ii transaction ke abort kar dewa gais hae kaheki 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.",
        "laggedslavemode": "Chetawni: Panna me nawaa badlao sait nai hoi.",
        "readonly": "Database band hai",
        "enterlockreason": "Band kare ke kaaran likho, aur ii bhi likho ki kab khola jaai.",
-       "readonlytext": "Database abhi nawaa badlao khatir band hai, saait database me mamuli kaam khatir lekin iske baad fir pahile jaise chale lagi.\n\nJon administrator database ke band karis rahaa, ii kaaran diis hai: $1",
+       "readonlytext": "Database abhi nawaa badlao khatir band hai, saait database me mamuli kaam khatir, lekin iske baad fir pahile jaise chale lagi.\n\nJon administrator database ke band karis rahaa, ii kaaran diis hai: $1",
        "missing-article": "Database, panna me likha akchhar, jiske naam \"$1\" hai, ke nai pais $2 .\n\nIske kaaran ii hoe sake ki aap ek purana antar nai to itihaas waala jorr ke use karaa jiske mitae dewa gais hai.\n\nAgar ii chij nai hai to sait aap ke software me bug hoi.\nIske, URL ke likh ke, koi administrator ke report karo.",
        "missingarticle-rev": "(badlao#: $1)",
        "missingarticle-diff": "(Antar: $1, $2)",
        "readonly_lag": "Database apne se band hoi gais hai jab tak ki duusra database, khaas database ke sanghe kaam nai kare lage.",
+       "nonwrite-api-promise-error": "The 'Promise-Non-Write-API-Action' HTTP header was sent but the request was to an API write module.",
        "internalerror": "Bhitri galti",
        "internalerror_info": "Bhitri galti: $1",
        "internalerror-fatal-exception": "Fatal exception of type \"$1\"",
        "no-null-revision": "Panna \"$1\" ke khatir nawaa null badlao nai banae sakaa hae",
        "badtitle": "Kharaab title",
        "badtitletext": "Jon panna aap mangta hai uske page title invalid, galat, nai to an incorrectly linked inter-language or inter-wiki title. Isme sait ek yah jaada character hoi jon ki title me nai kaam me lawa jae sake hai.",
+       "title-invalid-empty": "Maanga gais panna khaali hae, nai to, isme khaali namespace ke naam hae.",
+       "title-invalid-utf8": "Maanga gias panna me invalid UTF-8 sequence hae.",
+       "title-invalid-interwiki": "Maanga gais panna ke title me ek interwiki link hai, jiske title me nai kaam me lawa jaae sake hai.",
+       "title-invalid-talk-namespace": "Maaga gais panna ke title uu baat waala panna ke refer kare hae jon ki nai hai.",
+       "title-invalid-characters": "Maanga gais panna ke title me invalid character hai:\"$1\".",
+       "title-invalid-relative": "Title me relative path hai. Relative panna ke title (./,../) valid nai hai, kaaheki ii sab panna sadasya ke browser se nai pahuncha jaae sake hai.",
+       "title-invalid-magic-tilde": "Maanga gais panna ke title me invalid magic tilde sequence hai\n(<nowiki>~~~</nowiki>).",
+       "title-invalid-too-long": "Maanga gais panna bahut lamba hai. Ii UTF-8 encoding me  $1 {{PLURAL:$1|byte|bytes}} se lamba nai rahe sake hai.",
+       "title-invalid-leading-colon": "Maanga gais panna ke title ke suruu me invalid colon hai.",
        "perfcached": "Niche likha data ke cache karaa gais hai aur sait purana hoi. Jaada se jaada {{PLURAL:$1|ek result |$1 results}} cache me hae.",
        "perfcachedts": "Niche likha data ke cache kar dewa gais rahaa, aur pichhle time $1 ke badlaa gais rahaa. Jaada se jaada {{PLURAL:$4|ek result |$4 results}} cache me hae.",
        "querypage-no-updates": "Ii panna me badlao abhi band hai. Data ke abhi nawaa nai karaa jaai.",
        "viewsource": "Source dekho",
        "viewsource-title": "\"$1\" ke source dekho",
        "actionthrottled": "Kaam ke band kar dewa gais hai",
-       "actionthrottledtext": "Spam ke virod me, aap ke ii kaam thora deri me bahut time kare ke rukawat hai, aur aap time limit ke exceed kar diya hai.\nKuch deri be baad fir se kosis karna.",
+       "actionthrottledtext": "Barbaadi ke virod me, aap ke ii kaam thora deri me bahut time kare ke rukawat hai, aur aap time limit ke exceed kar diya hai.\nKuch deri be baad fir se kosis karna.",
        "protectedpagetext": "Ii panna ke badlao ke rok dewa gais hae, jisse ki ispe koi badlao aur koi action nai kare sake.",
-       "viewsourcetext": "Aap ii panna ke source ke dekhe aur nakal utare kare sakta hai:",
-       "viewyourtext": "Aap '''aapan badlao''' ke source ke dekhe aur copy kare saktaa hae",
+       "viewsourcetext": "Aap ii panna ke source ke dekhe aur nakal utare sakta hai.",
+       "viewyourtext": "Aap <strong>aapan badlao</strong> ke source ke ii panna pe  dekhe aur copy kare saktaa hae",
        "protectedinterface": "Ii panna, ii wiki ke khatir, software ke interface text dewe hai, aur iske barbaadi se roke ke khatir band kar dewa gais hai.\nSab wiki me anuwaad ke jorre nai to badle ke khatir, meharbaani kar ke [//translatewiki.net/ translatewiki.net], the MediaWiki localisation project ke kaam me laao.",
        "editinginterface": "'''Chetawani:''' Aap ek panna ke badaltaa hai jon ki software ke interface text dewe hae.\nIi panna me badlao ke asar duusra sadasya ke interface pe bhi hoi.",
        "translateinterface": "Sab wiki me translate kare ke khatir [//translatewiki.net/translatewiki.net], the MediaWiki localisation project, ke kaam me lao.",
-       "cascadeprotected": "Ii panna ke badlao se bachawa gais hai, kahe ki iske {{PLURAL:$1|panna, jon ki|panna, jon ki}} surakchhit hae \"cascading\" option turned on ke saathe me rakkhaa gais hai:\n$2",
+       "cascadeprotected": "Ii panna ke badlao se bachawa gais hai, kaheki iske {{PLURAL:$1|panna, jon ki|panna, jon ki}} surakchhit hae \"cascading\" option turned on ke saathe me rakkhaa gais hai:\n$2",
        "namespaceprotected": "Aap ke paas '''$1''' namespace me panna ke badle ke adhikar nai hai.",
        "customcssprotected": "Aap ke ii CSS panna ke badle ke ijaajat nai hae, kaahe ki isme duusra sadasya ke personal settings hae.",
        "customjsprotected": "Aap ke ii JavaScript panna ke badle ke ijaajat nai hae, kaahe ki isme duusra sadasya ke personal settings hae.",
        "mypreferencesprotected": "Aap ke aapan preferences ke badle ke ijaajat nai hae.",
        "ns-specialprotected": "Khaas panna ke badla nai jae sake hai.",
        "titleprotected": "Ii title ke banae se [[User:$1|$1]] rokis hai.\nIske kaaran hai ''$2''.",
-       "filereadonlyerror": "File \"$1\" ke nai badle sakaa hae, kaahe ki ii file repository \"$2\" me hae aur iske khaali parrha jaae sake hae.\nJon administrator iske lock karis hae, koi kaaran nai diis hae: \"$3\"",
+       "filereadonlyerror": "File \"$1\" ke nai badle sakaa hae, kaahe ki ii file repository \"$2\" me hae aur iske khaali parrha jaae sake hae.\nJon administrator iske lock karis hae, ii diis hae: \"$3\"",
        "invalidtitle-knownnamespace": "Namespace \"$2\" aur text \"$3\" ke kharaab title hae.",
        "invalidtitle-unknownnamespace": "Title gaer kaanuni hae aur iske namespace number \"$1\" aur text \"$2\" ke nai jaana jaawe hae",
        "exception-nologin": "Logged in nai hae",
        "createacct-reason": "Kaaran",
        "createacct-reason-ph": "Aap ke ii account ke banae ke kaaran",
        "createacct-submit": "Aapan account banao",
-       "createacct-another-submit": "Duusra account banao",
+       "createacct-another-submit": "Account banao",
        "createacct-benefit-heading": "Aap ke rakam log {{SITENAME}} ke banain hae.",
        "createacct-benefit-body1": "{{PLURAL:$1|badlao}}",
        "createacct-benefit-body2": "{{PLURAL:$1|panna}}",
        "createacct-benefit-body3": "haali ke {{PLURAL:$1|yogdaan de waala}}",
        "badretype": "Jon duuno password aap likha hai uu ek rakam nai hae.",
+       "usernameinprogress": "Ii sadasya ke account abhi banawa jaae hai.\nMeharbani kar ke sabur karo.",
        "userexists": "Ii sadasya ke naam aur koi ke hae.\nDuusra sadasya ke naam ke choose karo.",
        "loginerror": "Login me kuchh wrong hae",
        "createacct-error": "Account ke banae me galti",
        "wrongpassword": "Galat password likha gais hai. Fir se kosis karo.",
        "wrongpasswordempty": "Koi password nai likha gais hai. Fir se kosis karo.",
        "passwordtooshort": "Password me kamti se kamti {{PLURAL:$1|1 character|$1 characters}} hoe ke chahi.",
+       "passwordtoolong": "Password {{PLURAL:$1|1 character|$1 characters}} se lamba nai rahe sake hai.",
+       "passwordtoopopular": "Sadharan password ke nai kaam me lawa jaae sake hai. Meharbani kar ke aur tagrra password ke choose karo.",
        "password-name-match": "Aap ke password ke aap ke username se different rahe ke chaahi.",
        "password-login-forbidden": "Ii sadasya ke naam aur password ke kaam me laae ke ijaajat nai hae.",
        "mailmypassword": "Password ke badlo",
        "passwordreset-emailtext-ip": "Koi (hoe sake aap, IP address $1 se) {{SITENAME}} ($4) pe aap ke account ke baare me jaankari maanga hae. Niche likha gias sadasya ii e-mail se associated hae.  {{PLURAL:$3|account hae|accounts hae}}\n\n$2\n\n{{PLURAL:$3|Ii temporary password|Ii sab temporary passwords}}  {{PLURAL:$5|ek din|$5 din}} me khalaas hoi.\nAap ke chaahi ki aap login kar ke ek nawaa password banao.  Agar aur koi ii request karis hae, nai to agae aap aapan purana paasword ke yaad kar liya hae, tab ii sandes ke baare me bhuul jaao aur purana password use karte raho.",
        "passwordreset-emailtext-user": "\nSadasya $1 {{SITENAME}} pe aap ke account details ke {{SITENAME}} $4 ke khaatir  reminder maagis hae\n NIche ke sadasya {{PLURAL:$3|account hae|accounts hae}} ii e-mail address: $2 se associatied hae\n\n{{PLURAL:$3|Ii temporary password|Ii sab temporary passwords}}  {{PLURAL:$5|ek din|$5 din}} me khalaas hoi.\nAap ke chaahi ki aap login kar ke ek nawaa password banao.  Agar aur koi ii request karis hae, nai to agae aap aapan purana paasword ke yaad kar liya hae, tab ii sandes ke baare me bhuul jaao aur purana password use karte raho.",
        "passwordreset-emailelement": "Sadasya ke naam: \n$1\n\nKuchh din ke khatir password: \n$2",
-       "passwordreset-emailsent": "Aap ke password yaad karae ke khatir ek e-mail ke bhej dewa gais hae.",
+       "passwordreset-emailsentemail": "Agar ii email aap ke account se associated hai tab ek password reset email ke bheja jaai.",
+       "passwordreset-emailsentusername": "Agar ii email aap ke username se associated hai tab ek password reset email ke bheja jaai.",
        "passwordreset-emailsent-capture": "Ek password yaad karae waala e-mail, jiske niche dekhawa jaawe hae, ke bhej dewa gais hae.",
        "passwordreset-emailerror-capture": "Ek password yaad karae waala e-mail ke banawa gais hae, jiske niche dekhawa jaawe hae, lekin jiske {{GENDER:$2|user}} ke lage bheje nai jawa sake hae: $1",
-       "changeemail": "E-mail address ke badlo",
-       "changeemail-header": "Account e-mail address ke badlo",
+       "changeemail": "E-mail address ke badlo, nai to, hatao",
+       "changeemail-header": "Aapan email ke badle ke khatir ii form ke bharo. Agar aap koi email ke aapan account se nai associate kare mangtaa hai tab form ke submit kare ke time email address ke blank chhorr do.",
+       "changeemail-passwordrequired": "Ii badlao ke confirm kare ke khatir aap ke aapan password ke enter kare ke parri.",
        "changeemail-no-info": "Ii panna ke sidha dekhe ke khaatir, aap ke login kare ke parri.",
        "changeemail-oldemail": "Abhi ke E-mail address:",
        "changeemail-newemail": "Nawaa E-mail address:",
+       "changeemail-newemail-help": "Agar aap aapan email ke hatae mangtaa hai tab ii field ke blank chhorr do.\nAgar email hatae dewa gais hai tab aap bhulawa gais password ke nai reset kare sakegaa aur aap ke ii wiki se email nai mili.",
        "changeemail-none": "(kuchh nai)",
        "changeemail-password": "Aap ke {{SITENAME}} password:",
        "changeemail-submit": "E-mail badlo",
        "changeemail-throttled": "Aap bahut dher dafe login kare ke kosis karaa hae.\nMeharbaani kar ke $1 talak wait kar ke fir se try karo.",
+       "changeemail-nochange": "Meharbaani karke ek duusra email address ke likho.",
        "resettokens": "Token ke reset karo",
        "resettokens-text": "Aap aapan private data pe access roke ke khatir token ke reset kare saktaa hae.\n\nAap ke ii kare ke chaahi agar aap galti se ii jaankari ke aur koi ke de diya hae nai to aap ke account ke bare me aur koi ke pataa hae.",
        "resettokens-no-tokens": "Reset kare ke jhatir koi token nai hae.",
        "sig_tip": "Aapke signature time ke saathe",
        "hr_tip": "Samthar line (bahut jaada nai kaam me laana)",
        "summary": "Sanchhipt:",
-       "subject": "Visay/khaas samachar:",
+       "subject": "Visay:",
        "minoredit": "Ii chhota badlao hai",
        "watchthis": "Ii panna pe dhyaan rakkho",
        "savearticle": "Panna ke bachao",
        "missingsummary": "'''Suchna:''' Aap badlao ke sanchhit me nai likha hai.\nAgar aap Save ke fir se click karaa tab, aap ke badlao bina summary ke save kar lewa jaai.",
        "selfredirect": "<strong>Chetauni:</strong> Aap ii panna ke apne me redirect kartaa hae. \nAap saait wrong target ke specify karaa, nai to wrong panna ke badaltaa hae.\nAgar aap  \"{{int:savearticle}}\" ke fir se click karaa tab redirect ban jaai.",
        "missingcommenttext": "Meharbani kar ke niche aapan vichar deo.",
-       "missingcommentheader": "'''Chetauni:''' Aap ii vichar ke vishay nai likha hai.\nAgar aap \"{{int:savearticle}}\"  pe click karaa tab bina vishay ke iske bachae lewa jaai.",
+       "missingcommentheader": "<strong>Yaad karawa jaae hae:</strong> Aap ii vichar ke vishay nai likha hai.\nAgar aap \"{{int:savearticle}}\"  pe click karaa tab bina vishay ke iske bachae dewa jaai.",
        "summary-preview": "Sanchhep jhalak:",
        "subject-preview": "Suchi ke jhalak:",
+       "previewerrortext": "Aap ke badlao ke preview kare ke time kuchh garrbarro hae gais hai.",
        "blockedtitle": "Sadasya ke rok dewa gais hai",
        "blockedtext": "'''Aapke user name nai to IP address ke rok dewa gae hai.'''\n\nRoke waala hai $1.\nIske kaaran hai ''$2''.\n\n* Roke ke suruu: $8\n* Roke kab khatam hoi: $6\n* Kiske rokaa jae hai: $7\n\nAap $1 ke mile saktaa hai nai to duusra [[{{MediaWiki:Grouppage-sysop}}|administrator]] se rukawat ke baare me baat karo.\nAap ii sadasya ke 'email this user' feature ke kaam me lae ke baat nai kare saktaa hai jab tak ki ek kanuni email address aapke [[Special:Preferences|account preferences]] me nai hai aur aap ke iske kaam me laae ke roka nai gae hai.\nAap ke abhi ke IP address $3 hai, aur roka gae ID hai #$5.\nMeharbani kar ke chahe ek nai to duno ke aapan sawaal me rakho.",
        "autoblockedtext": "Aap ke IP address ke apne se rok dewa gais hai kahe ki koi duusra sadasya iske kaam me kawat rahaa, jiske $1 rokis hai.\n\nIske khatir kaaran hai:\n:''$2''\n\n* Roke ke suruu: $8\n* Roke kab khatam hoi: $6\n*Roke waala: $7\n\nAap $1 ke mile saktaa hai nai to duusra [[{{MediaWiki:Grouppage-sysop}}|administrator]] se rukawat ke baare me baat karo.\n\nAap ii sadasya ke 'email this user' feature ke kaam me lae ke baat nai kare saktaa hai jab tak ki ek kanuni email address aapke [[Special:Preferences|account preferences]] me nai hai aur aap ke iske kaam me laae ke roka nai gae hai.\n\nAap ke abhi ke IP address $3 hai, aur roka gae ID hai #$5.\nMeharbani kar ke chahe ek nai to duno ke aapan sawaal me rakho.",
        "yourdiff": "Antar",
        "copyrightwarning": "Dhyann me rakho ki {{SITENAME}} ke sab yog daan $2 ($1 ke dekho aur kaankari khatir) ke niche dewa gae hai. Agar aap nai mangtaa ki aap ke likha gae koi chij ke duusra logan badle tab hain par nahii likho.<br />\nAap ii bhi waada kartaa hai ki iske aap likha hai aur koi duusra jagah se copy nahi karaa hai.\n'''COPYRIGHT CHIJ KE BINA ANUMATI KE HIAN PAR NAHI SUBMIT KARNA!'''",
        "copyrightwarning2": "Yaad rakhna ki {{SITENAME}} pe sab yogdaan ke duusra sadasya LOG badle, nai to delete, kare sake hai.\nAgar aap nai mangta ki koi aur aap ke yogdaan ke badle, tab aap hian par nai likho.<br />\nAap ii bhi kasam khata hai ki aap iske apne se likha hai aur kahin se copy nai karaa hai (Aur jaankari khatir $1 ke dekho).\n''' COPYRIGHT WORK KE BINA AUNUMATI KE SUBMIT NAI KARNA!'''",
+       "editpage-cannot-use-custom-model": "Ii panna ke content model ke nai badla jaawe sake hai.",
        "longpageerror": "!'''ERROR: Jon text aap submit karaa hai uu {{PLURAL:$1|ek kilobyte|$1 kilobytes}} lamba hai, jon ki maximum {{PLURAL:$2|ek kilobyte|$2 kilobytes}} se lamba hai.'''\nIske bajawa nai karaa jae sake hai.",
-       "readonlywarning": "'''Chetauni: Database ke maintenance khatir band kar dewa gais hai, tab abhi aap aapan badlao ke save nai kare paega.'''\nAap saait aapan badlao ke ek text file me cut-n-paste kar ke baad me use kare khatir save kar le sakta hai.\nAdministrator jon ki iske lock karis hai ii kaaran diis :hai: $1",
+       "readonlywarning": "<strong>Chetauni: Database ke maintenance khatir band kar dewa gais hai, tab abhi aap aapan badlao ke save nai kare paega.</strong>\nAap saait aapan badlao ke ek text file me cut-n-paste kar ke baad me use kare khatir save kar le sakta hai.\nAdministrator jon ki iske lock karis hai ii kaaran diis hai: $1",
        "protectedpagewarning": "'''CHETAUNI: Ii panna ke band kar dewa gais hai jisse ke khaali uu sadasya jiske sysop adhikaar hai iske badle sake hai.'''\nNiche sab se nawaa suchi aap ke dekhe ke khatir dewa gais hae:",
        "semiprotectedpagewarning": "'''Suchna:''' Ii panna ke band kar dewa gais hai jisse ki khali registered sadasya iske badle sake hai.\nNiche sab se nawaa suchi ke aap ke dekhe ke khatir dewa gais hae:",
-       "cascadeprotectedwarning": "'''Chetawani:''' Ii panna ke band kar dewa gais jiske kaaran khali uu sadasya jiske lage sysop privileges hai iske badle sake hai, kahe ki iske niche likha gais cascade-protected {{PLURAL:$1|panna|panna}} me rakkha gais hai:",
+       "cascadeprotectedwarning": "<strong>Chetawani:</strong> Ii panna ke band kar dewa gais jiske kaaran khali uu sadasya jiske lage sysop privileges hai iske badle sake hai, kahe ki iske niche likha gais cascade-protected {{PLURAL:$1|panna|panna}} me rakkha gais hai:",
        "titleprotectedwarning": "'''CHETAUNI: Ii panna ke band dewa gais hai jisse ki [[Special:ListGroupRights|specific rights]] ke jarie iske badla jaae sake hai.'''\nAap ke jaankari ke khatir sab se nawaa suchi niche dewa gais hae:",
        "templatesused": "{{PLURAL:$1|Template|Templates}} ke ii panna me kaam me lawa gais hae:",
        "templatesusedpreview": "{{PLURAL:$1|Template|Templates}} ii jhalak me kaam me lawa gais hae:",
        "permissionserrors": "Permissions Errors",
        "permissionserrorstext": "Aap ke uu chij kare ke ijajat nai hai, ii {{PLURAL:$1|kaaran|kaaran}} khatir:",
        "permissionserrorstext-withaction": "Aap ke lage $2 kare khatir ijajat nai hai, ii {{PLURAL:$1|kaaran|kaaran}} se:",
+       "contentmodelediterror": "Aap iske badle nai saktaa hae kaaheki iske content model <code>$1</code> hae, aur ii  abhi ke content model <code>$2</code> ke rakam nai hae.",
        "recreate-moveddeleted-warn": "'''Chetawani: Jon panna ke pahile hatae dewa gais rahaa ke aap fir se banata hai.'''\n\nAap socho ki ii panna ke sampadan aap ke karte rahe ke chaahi ki nai.\nAap ke aaram khatir hatae waala suchi hian pe dewa jaawe hai:",
        "moveddeleted-notice": "Ii panna ke mitae dewa gais hai.\nIi panna ke mitae waala aur hatae waala log aap ke dekhe khatir niche dewa gais hai.",
        "log-fulllog": "Puura log dekho",
        "notextmatches": "Koi panna see text nai mile hae",
        "prevn": "pahile waala {{PLURAL:$1|$1}}",
        "nextn": "aage waala {{PLURAL:$1|$1}}",
+       "prev-page": "pahile waala panna",
+       "next-page": "aage waala panna",
        "prevn-title": "Pahile waala $1 {{PLURAL:$1|natija|natija}}",
        "nextn-title": "Aage waala $1 {{PLURAL:$1|result|results}}",
        "shown-title": "Ek panna me $1 {{PLURAL:$1|result|results}} dekhao",
        "search-category": "(category $1)",
        "search-file-match": "(file content ke match kare hae)",
        "search-suggest": "Ka aap ke matlab rahaa: $1",
+       "search-rewritten": "$1 ke result dekhawa jaae hai. Iske jagah $2 ke khojo.",
        "search-interwiki-caption": "Saathe ke project",
        "search-interwiki-default": "$1 ke result:",
        "search-interwiki-more": "(aur)",
        "showingresultsinrange": "Niche dekhae hai {{PLURAL:$1|<strong>1</strong> result|<strong>$1</strong> results}} #<strong>$2</strong> se suruu hoe ke #<strong>$3</strong> talak.",
        "search-showingresults": "{{PLURAL:$4|Result <strong>$1</strong> of <strong>$3</strong>|Results <strong>$1 - $2</strong> of <strong>$3</strong>}}",
        "search-nonefound": "Ii sawaal ke koi jawab nai hae.",
+       "search-nonefound-thiswiki": "Ii site me aap ke khoj ke koi result nai hai.",
        "powersearch-legend": "Gahira khoj",
        "powersearch-ns": "Namespaces me khojo:",
        "powersearch-togglelabel": "Check karo:",
        "prefs-watchlist-token": "Dhyan suchi ke nisani:",
        "prefs-misc": "Futkar",
        "prefs-resetpass": "Password badlo",
-       "prefs-changeemail": "E-mail badlo",
+       "prefs-changeemail": "E-mail badlo, nai to, hatao",
        "prefs-setemail": "Ek E-mail address ke banao",
        "prefs-email": "E-mail ke option",
        "prefs-rendering": "Dekhe me kaise lage hai",
        "rows": "Taytay:",
        "columns": "Column:",
        "searchresultshead": "Khojo",
-       "stub-threshold": "Threshold ke khatir <a href=\"#\" class=\"stub\">stub link</a> formatting (bytes):",
+       "stub-threshold": "Threshold stub link formatting ke khatir ($1):",
+       "stub-threshold-sample-link": "namuna",
        "stub-threshold-disabled": "Band kar dewa gais hae",
        "recentchangesdays": "Nawaa badlao me ketna roj dekhawa jaae:",
        "recentchangesdays-max": "(sab se jaada $1 {{PLURAL:$1|din|din}})",
        "prefs-help-recentchangescount": "Isme hai haali ke badlao, panna ke itihaas aur loga.",
        "prefs-help-watchlist-token2": "Aap ke dhyan suchi ke web feed ke ii secret key hae.\nAur koi agar iske bare me jaane hae aap ke dhyan suchi ke parrhae sake hae, tab iske aur ki ke nai dena.\n[[Special:ResetTokens|Agar aap iske reset kare mangtaa hae tab hian pe click karo]].",
        "savedprefs": "Aap ke pasand ke save kar lewa gais hai.",
+       "savedrights": "{{GENDER:$1|$1}} ke user rights ke bachae lewa gais hai.",
        "timezonelegend": "Time ke zone:",
        "localtime": "Sthaniye samay:",
        "timezoneuseserverdefault": "Wiki default ke kaam me laao ($1)",
        "badsig": "Invalid raw signature; HTML tags ke check karo.",
        "badsiglength": "Signature bahut lambaa hai.\nIske $1 {{PLURAL:$1|character|characters}} se kamti rahe ke chaahi.",
        "yourgender": "Aap kaise describe hoe mangtaa hae?",
-       "gender-unknown": "Ham bole nai mangtaa hae",
+       "gender-unknown": "Jahaan talak hoe sake, aap ke baare me likhe ke khatir, ii software gender neutral sabd ke kaam me laai.",
        "gender-male": "Uu wiki panna ke badle hae",
        "gender-female": "Uu wiki panna ke badle hae",
        "prefs-help-gender": "Ii preference ke set karna optional hae.\nSoftware aapan value ke use kar ke aap ke address kare hae aur aap ke ke bare me duusre ke batae hae, right grammar use kar ke\nIi jaankari janata ke dekhai.",
        "prefs-help-prefershttps": "Aap ke agla login pe ii preferences effect me aai.",
        "prefswarning-warning": "Aap aapan preferences ke badla hae, jiske abhi talak save nai karaa gae hae.\nAgar aap ii panna ke bina \"$1\" me click kare chhorra, tab aap ke preferences save nai hoi.",
        "prefs-tabs-navigation-hint": "Tip: Aap left aur right arrow key use kar ke tab list me navigate kare saktaa hae.",
-       "email-address-validity-valid": "E-mail address kanuni hae",
-       "email-address-validity-invalid": "Ek kanuni e-mail ke likho",
        "userrights": "Sadasya ke adhikaar ke chalao",
        "userrights-lookup-user": "Sadasya ke group ke manage karo",
        "userrights-user-editname": "Ek Username ke enter karo:",
        "editusergroup": "User groups ke badlo",
-       "editinguser": "Sadasya '''[[User:$1|$1]]'''  ke adhikaar ke badlaa jaawe hae $2",
+       "editinguser": "{{GENDER:$1|Sadasya}} <strong>[[User:$1|$1]]</strong>  ke adhikaar ke badlaa jaawe hae $2",
        "userrights-editusergroup": "User groupske badlo",
        "saveusergroups": "User groups ke save karo",
        "userrights-groupsmember": "Iske member hai:",
        "group-bot": "Bots",
        "group-sysop": "Sysops",
        "group-bureaucrat": "Bureaucrats",
-       "group-suppress": "Oversights",
+       "group-suppress": "Suppressors",
        "group-all": "(sab)",
        "group-user-member": "{{GENDER:$1|sadasya}}",
        "group-autoconfirmed-member": "{{GENDER:$1|autoconfirmed sadasya}}",
        "group-bot-member": "{{GENDER:$1|bot}}",
        "group-sysop-member": "{{GENDER:$1|administrator}}",
        "group-bureaucrat-member": "{{GENDER:$1|bureaucrat}}",
-       "group-suppress-member": "{{GENDER:$1|oversight}}",
+       "group-suppress-member": "{{GENDER:$1|suppressor}}",
        "grouppage-user": "{{ns:project}}:Sadasya",
        "grouppage-autoconfirmed": "{{ns:project}}:Autoconfirmed sadasya",
        "grouppage-bot": "{{ns:project}}:Bots",
        "grouppage-sysop": "{{ns:project}}:Администраторар",
        "grouppage-bureaucrat": "{{ns:project}}:Bureaucrats",
-       "grouppage-suppress": "{{ns:project}}:Oversight",
+       "grouppage-suppress": "{{ns:project}}:Suppress",
        "right-read": "Panna ke parrho",
        "right-edit": "Panna ke badlo",
        "right-createpage": "Panna banao (jon ki salah kare waala panna nai hai)",
        "recentchanges-label-plusminus": "Panna ke size etna bytes se badla",
        "recentchanges-legend-heading": "'''Legend:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (aur dekho [[Special:NewPages|nawaa panna ke suchi]])",
+       "recentchanges-submit": "Dekhao",
        "rcnotefrom": "Niche {{PLURAL:$5|badlao hae|badlao hae}} <strong>$3, $4</strong> (<strong>$1</strong> talak dekhawa gais) talak.",
        "rclistfrom": "$3 $2 se suruu kar ke nawaa badlao dekhao",
        "rcshowhideminor": "$1 chhota badlao",
        "rcshowhidemine": "$1 hamaar sampadan",
        "rcshowhidemine-show": "Dekhao",
        "rcshowhidemine-hide": "Lukao",
+       "rcshowhidecategorization-show": "Dekhao",
        "rclinks": "Pichhla $1 badlao pichle $2 din me dekhao <br />$3",
        "diff": "farka",
        "hist": "itihaas",
index efb07a1..b24743b 100644 (file)
                        "Fraxinus",
                        "Srdjan m",
                        "Teoo3",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Vrhnje"
                ]
        },
        "tog-underline": "Podcrtane poveznice",
        "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-hidecategorization": "Sakrij kategorizaciju stranica",
        "tog-extendwatchlist": "Proširi popis praćenih stranica tako da prikaže sve promjene, ne samo najnovije",
        "tog-usenewrc": "Grupne promjene po stranici u popisu nedavnih izmjena i popisu praćenih stranica (zahtijeva JavaScript)",
        "tog-numberheadings": "Automatski označi naslove brojevima",
        "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-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",
        "october-date": "$1. listopada",
        "november-date": "$1. studenoga",
        "december-date": "$1. prosinca",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategorija|Kategorije|Kategorija}}",
        "category_header": "Članci u kategoriji \"$1\"",
        "subcategories": "Potkategorije",
        "pool-timeout": "Istek vremena (''timeout'') čekajući zaključavanje",
        "pool-queuefull": "Red čekanja je pun",
        "pool-errorunknown": "Nepoznata pogrješka",
+       "poolcounter-usage-error": "Greška korištenja: $1",
        "aboutsite": "O projektu {{SITENAME}}",
        "aboutpage": "Project:O_projektu_{{SITENAME}}",
        "copyright": "Sadržaji se koriste u skladu s $1.",
        "nstab-user": "{{GENDER:{{BASEPAGENAME}}|Stranica suradnika|Stranica suradnice}}",
        "nstab-media": "Mediji",
        "nstab-special": "Posebna stranica",
-       "nstab-project": "Stranica o projektu",
+       "nstab-project": "Stranica projekta",
        "nstab-image": "Datoteka",
        "nstab-mediawiki": "Poruka",
        "nstab-template": "Predložak",
        "delete-hook-aborted": "Brisanje prekinuto softverskim priključkom (hook).\nNema obrazloženja ili poruke o pogrješci.",
        "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-utf8": "Traženi naziv stranice koristi nevažeću UTF-8 sekvencu.",
+       "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\"",
        "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.\n\nViše o ovoj [[Wikipedija:Posebne stranice|posebnoj stranici]] na [[Razgovor Wikipedija:Special:{{PAGENAME}}]].",
        "querypage-no-updates": "Osvježavanje ove stranice je trenutačno onemogućeno. Nove promjene neće biti vidljive.",
        "virus-scanfailed": "skeniranje neuspješno (kod $1)",
        "virus-unknownscanner": "nepoznati antivirus:",
        "logouttext": "'''Odjavili ste se.'''\n\nNeke se stranice mogu prikazivati kao da ste još uvijek prijavljeni, sve dok ne očistite međuspremnik svog preglednika.",
+       "cannotlogoutnow-title": "Odjava trenutno nije moguća.",
        "welcomeuser": "Dobrodošli, $1!",
        "welcomecreation-msg": "Vaš je suradnički račun otvoren.\nNe zaboravite prilagoditi Vaše [[Special:Preferences|{{SITENAME}} postavke]].",
        "yourname": "Suradničko ime",
        "remembermypassword": "Zapamti moju lozinku na ovom računalu (najduže $1 {{PLURAL:$1|dan|dana}})",
        "userlogin-remembermypassword": "Zapamti me",
        "userlogin-signwithsecure": "Rabi sigurnu vezu",
+       "cannotloginnow-title": "Prijava trenutno nije moguća.",
        "yourdomainname": "Vaša domena",
        "password-change-forbidden": "Ne možete promjeniti zaporku na ovom projektu.",
        "externaldberror": "Došlo je do pogreške s vanjskom autorizacijom ili Vam nije dopušteno osvježavanje vanjskog suradničkog računa.",
        "createacct-reason": "Razlog",
        "createacct-reason-ph": "Zašto stvarate drugi račun",
        "createacct-submit": "Stvorite svoj suradnički račun",
-       "createacct-another-submit": "Stvori još jedan račun",
+       "createacct-another-submit": "Otvori račun",
        "createacct-benefit-heading": "{{SITENAME}} su stvorili ljudi poput Vas.",
        "createacct-benefit-body1": "{{PLURAL:$1|uređivanje|uređivanja}}",
        "createacct-benefit-body2": "{{PLURAL:$1|stranica|stranice|stranica}}",
        "resetpass_submit": "Postavite lozinku i prijavite se",
        "changepassword-success": "Zaporka je uspješno postavljena!",
        "changepassword-throttled": "Nedavno ste se previše puta pokušali prijaviti.\nMolimo Vas pričekajte $1 prije nego što pokušate ponovno.",
+       "botpasswords-label-create": "Stvori",
+       "botpasswords-label-update": "Ažuriraj",
+       "botpasswords-label-cancel": "Odustani",
+       "botpasswords-label-resetpassword": "Reset 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.",
        "resetpass-submit-loggedin": "Promijeni lozinku",
        "passwordreset-emailsentemail": "E-mail podsjetnik zaporke je poslan.",
        "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 e-mail adresu",
+       "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.",
        "changeemail-oldemail": "Trenutačna adresa e-pošte:",
        "sig_tip": "Vaš potpis s datumom",
        "hr_tip": "Vodoravna crta (koristiti rijetko)",
        "summary": "Sažetak:",
-       "subject": "Predmet:",
+       "subject": "Tema:",
        "minoredit": "Ovo je manja promjena",
-       "watchthis": "Prati ovaj članak",
+       "watchthis": "Prati ovu stranicu",
        "savearticle": "Sačuvaj stranicu",
        "preview": "Pregled kako će stranica izgledati",
        "showpreview": "Prikaži kako će izgledati",
        "right-blockemail": "Blokiranje suradnika u slanju elektroničke pošte",
        "right-hideuser": "Blokiranje suradničkog imena, skrivajući ga od javnosti",
        "right-ipblock-exempt": "Iznimka od blokiranja IP adresa, auto-bloka i blokiranja opsega",
-       "right-proxyunbannable": "Iznimka od automatskih blokiranja posrednika (proxya)",
        "right-unblockself": "Odblokirati se",
        "right-protect": "Mijenjanje razina zaštićivanja i uređivanje zaštićenih stranica",
        "right-editprotected": "Uređivanje zaštićenih stranica (bez prenosive zaštite)",
        "right-override-export-depth": "Izvezi stranice uključujući i povezane stranice do dubine od 5",
        "right-sendemail": "Slanje e-pošte drugim suradnicima",
        "right-passwordreset": "Vidi poruku e-pošte o ponovnom postavljanju lozinke",
+       "grant-group-email": "Pošalji e-mail",
+       "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-highvolume": "Uređivanja velikog opsega",
+       "grant-basic": "Osnovna prava",
+       "grant-viewdeleted": "Prikaz izbrisanih datoteka i stranica",
+       "grant-viewmywatchlist": "Pregled popisa praćenih stranica",
        "newuserlogpage": "Evidencija novih suradnika",
        "newuserlogpagetext": "Ispod je popis nedavno otvorenih suradničkih imena.",
        "rightslog": "Evidencija suradničkih prava",
        "rcshowhidemine": "$1 moje promjene",
        "rcshowhidemine-show": "prikaži",
        "rcshowhidemine-hide": "sakrij",
+       "rcshowhidecategorization-show": "Prikaži",
+       "rcshowhidecategorization-hide": "Sakrij",
        "rclinks": "Prikaži posljednjih $1 promjena {{PLURAL:$2|prethodni dan|u posljednja $2 dana|u posljednjih $2 dana}}<br />$3",
        "diff": "razl",
        "hist": "pov",
        "newpageletter": "N",
        "boteditletter": "b",
        "number_of_watching_users_pageview": "[$1 {{PLURAL:$1|suradnik|suradnika|suradnika}} prati ovu stranicu]",
-       "rc_categories": "Ograniči na kategorije (odvojene znakom  \"|\")",
-       "rc_categories_any": "Sve",
+       "rc_categories": "Ograniči na kategorije (odvoji sa \"|\")",
+       "rc_categories_any": "Bilo koji od odabranih",
        "rc-change-size-new": "$1 {{PLURAL:$1|bajt|bajta|bajtova}} poslije uređivanja",
        "newsectionsummary": "/* $1 */ novi odlomak",
        "rc-enhanced-expand": "Pokaži detalje (potreban JavaScript)",
        "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-removed-from-category": "[[:$1]] uklonjeno iz kategorije",
        "upload": "Postavi datoteku",
        "uploadbtn": "Postavi datoteku",
        "reuploaddesc": "Vratite se u obrazac za postavljanje.",
        "upload-dialog-button-save": "Spremi",
        "upload-dialog-button-upload": "Postavi",
        "upload-form-label-select-file": "Odaberi datoteku",
+       "upload-form-label-infoform-title": "Detalji",
+       "upload-form-label-infoform-name": "Ime",
+       "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-usage-title": "Korištenje",
+       "upload-form-label-usage-filename": "Ime datoteke",
        "foreign-structured-upload-form-label-own-work": "Ovo je moje djelo",
+       "foreign-structured-upload-form-label-infoform-categories": "Kategorije",
+       "foreign-structured-upload-form-label-infoform-date": "Datum",
        "foreign-structured-upload-form-label-not-own-work-local-default": "Možete pokušati [[Special:Upload|postaviti datoteku na projektu {{SITENAME}}]], pod uvjetom da može biti tamo postavljena, sukladno pravilima projekta.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Potvrđujem da posjedujem autorska prava ove datoteke i slažem se da je nepozivo postavljam na Zajednički poslužitelj pod licencijom  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0], i pristajem na [https://wikimediafoundation.org/wiki/Terms_of_Use Uvjete uporabe].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Ako ne posjedujete autorska prava za ovu datoteku, ili je želite objaviti pod drugom licencijom, razmislite o uporabi [https://commons.wikimedia.org/wiki/Special:UploadWizard Čarobnjaka za postavljanje] na Zajedničkom poslužitelju.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Možete pokušati [[Special:Upload|postaviti datoteku na projektu {{SITENAME}}]], pod uvjetom da je dopušteno postavljanje ove datoteke, sukladno pravilima projekta.",
+       "foreign-structured-upload-form-3-label-yes": "Da",
        "backend-fail-stream": "Ne mogu prikazati datoteku $1.",
        "backend-fail-backup": "Izrada sigurnosne kopije datoteke \"$1\" nije uspjela.",
        "backend-fail-notexists": "Datoteka $1 ne postoji.",
        "filehist-filesize": "Veličina datoteke",
        "filehist-comment": "Komentar",
        "imagelinks": "Upotreba datoteke",
-       "linkstoimage": "{{PLURAL:$1|Sljedeća stranica povezuje|$1 sljedećih stranice povezuju}} na ovu datoteku:",
+       "linkstoimage": "{{PLURAL:$1|Sljedeća stranica povezuje|$1 sljedeće stranice povezuju|$1 sljedećih stranica povezuje}} na ovu datoteku:",
        "linkstoimage-more": "Više od $1 {{PLURAL:$1|stranice povezuje|stranica povezuje}} na ovu datoteku.\nSljedeći popis prikazuje {{PLURAL:$1|stranice koje|prvih $1 stranica koje}} vode na ovu datoteku.\n[[Special:WhatLinksHere/$2|Ovdje se nalazi]] potpuni popis.",
        "nolinkstoimage": "Nijedna stranica ne povezuje na ovu sliku.",
        "morelinkstoimage": "Pogledaj [[Special:WhatLinksHere/$1|više poveznica]] za ovu datoteku.",
        "mostrevisions": "Popis članaka po broju uređivanja",
        "prefixindex": "Sve stranice prema početku naslova",
        "prefixindex-namespace": "Sve stranice s predmetkom (imenski prostor $1)",
+       "prefixindex-submit": "Prikaži",
        "prefixindex-strip": "Ne prikazuj predmetak u popisu",
        "shortpages": "Kratke stranice",
        "longpages": "Duge stranice",
        "protectedpagesempty": "Nema zaštićenih stranica koje ispunjavaju uvjete koje ste postavili.",
        "protectedpages-page": "Stranica",
        "protectedpages-expiry": "Istječe",
+       "protectedpages-performer": "Zaštita suradnika",
        "protectedpages-params": "Stupanj zaštite",
        "protectedpages-reason": "Razlog",
+       "protectedpages-submit": "Prikaži stranice",
+       "protectedpages-unknown-timestamp": "Nepoznato",
+       "protectedpages-unknown-performer": "Nepoznati korisnik",
        "protectedtitles": "Zaštićeni naslovi",
        "protectedtitlesempty": "Nijedan naslov nije trenutačno zaštićen s tim parametrima.",
+       "protectedtitles-submit": "Prikaži nazive",
        "listusers": "Popis suradnika",
        "listusers-editsonly": "Prikaži samo suradnike s uređivanjem",
        "listusers-creationsort": "Razvrstaj po datumu stvaranja",
        "usereditcount": "$1 {{PLURAL:$1|uređivanje|uređivanja|uređivanja}}",
        "usercreated": "{{GENDER:$3|Otvorio|Otvorila}} račun $1 u $2",
        "newpages": "Nove stranice",
+       "newpages-submit": "Prikaži",
        "newpages-username": "Suradničko ime:",
        "ancientpages": "Najstarije stranice",
        "move": "Premjesti",
        "pager-older-n": "{{PLURAL:$1|starija $1|starije $1|starijih $1}}",
        "suppress": "Nadzor",
        "querypage-disabled": "Ova posebna stranica onemogućena je jer bi usporila funkcioniranje projekta.",
+       "apihelp": "Pomoć za API",
        "booksources": "Pretraživanje po ISBN-u",
        "booksources-search-legend": "Traženje izvora za knjigu",
        "booksources-search": "Traži",
        "specialloguserlabel": "Suradnik:",
        "speciallogtitlelabel": "Cilj (naslov ili suradnik):",
        "log": "Evidencije",
+       "logeventslist-submit": "Prikaži",
        "all-logs-page": "Sve javne evidencije",
        "alllogstext": "Skupni prikaz svih dostupnih evidencija za {{SITENAME}}.\nMožete suziti prikaz odabirući tip evidencije, suradničko ime ili stranicu u upitu.",
        "logempty": "Nema pronađenih stavki.",
        "log-title-wildcard": "Traži stranice koje počinju s navedenim izrazom",
        "showhideselectedlogentries": "Otkrij/sakrij odabrane evidencije",
+       "checkbox-select": "Odaberite: $1",
+       "checkbox-all": "Sve",
+       "checkbox-none": "Nijedan",
        "allpages": "Sve stranice",
        "nextpage": "Sljedeća stranica ($1)",
        "prevpage": "Prethodna stranica ($1)",
        "cachedspecial-viewing-cached-ts": "Gledate inačicu ove stranice iz privremene memorije, koja možda nije potpuno aktualna.",
        "cachedspecial-refresh-now": "Pogledaj najnoviju.",
        "categories": "Kategorije",
+       "categories-submit": "Prikaži",
        "categoriespagetext": "Sljedeće {{PLURAL:$1|kategorija sadrži|kategorije sadrže}} stranice ili datoteke.\n[[Special:UnusedCategories|Nekorištene kategorije]] ovdje nisu prikazane.\nTakođer pogledajte [[Special:WantedCategories|tražene kategorije]].",
        "categoriesfrom": "Prikaži kategorije počevši od:",
        "special-categories-sort-count": "razvrstavanje po broju",
        "activeusers-hidebots": "Sakrij botove",
        "activeusers-hidesysops": "Sakrij administratore",
        "activeusers-noresult": "Niti jedan suradnik nije nađen.",
+       "activeusers-submit": "Prikaz aktivnih sudionika",
        "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>",
        "listgrouprights-removegroup-self": "Ukloni {{PLURAL:$2|skupinu|skupine}} iz vlastitog računa: $1",
        "listgrouprights-addgroup-self-all": "Dodaj sve skupine vlastitom računu",
        "listgrouprights-removegroup-self-all": "Uklonite sve skupine iz vlastitog računa",
+       "listgrouprights-namespaceprotection-header": "Ograničenja prostora imena",
        "listgrouprights-namespaceprotection-namespace": "Imenski prostor",
        "trackingcategories-nodesc": "Opis nije dostupan.",
        "mailnologin": "Nema adrese pošiljatelja",
        "wlshowlast": "Prikaži posljednjih $1 sati $2 dana",
        "watchlistall2": "sve",
        "watchlist-hide": "Sakrij",
+       "watchlist-submit": "Prikaži",
        "wlshowtime": "Prikaži posljednjih:",
        "wlshowhideminor": "manje promjene",
        "wlshowhidebots": "botove",
        "delete-confirm": "Obriši \"$1\"",
        "delete-legend": "Izbriši",
        "historywarning": "<strong>Upozorenje:</strong> stranica koju želite izbrisati ima starije izmjene s $1 {{PLURAL:$1|inačicom|inačice|inačica}}:",
+       "historyaction-submit": "Prikaži",
        "confirmdeletetext": "Zauvijek ćete izbrisati stranicu ili sliku zajedno s prijašnjim inačicama.\nMolim potvrdite svoju namjeru, da razumijete posljedice i da ovo radite u skladu s [[{{MediaWiki:Policy-url}}|pravilima]].",
        "actioncomplete": "Zahvat završen",
        "actionfailed": "Radnja nije uspjela",
        "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-title-label": "Naziv stranice",
+       "changecontentmodel-model-label": "Novi model sadržaja",
+       "changecontentmodel-reason-label": "Razlog:",
+       "changecontentmodel-success-title": "Sadržaj modela je promijenjen",
        "protectlogpage": "Evidencija zaštićivanja",
        "protectlogtext": "Ispod je evidencija zaštićivanja i uklanjanja zaštite pojedinih stranica.\nPogledajte [[Special:ProtectedPages|zaštićene stranice]] za popis trenutačno zaštićenih stranica.",
        "protectedarticle": "članak \"[[$1]]\" je zaštićen",
        "movenotallowedfile": "Nemate ovlasti za premještanje datoteka.",
        "cant-move-user-page": "Nemate dopuštenja za premještanje root suradničkih stranica.",
        "cant-move-to-user-page": "Nemate dopuštenje za premještanje stranice na suradničku stranicu (osim kao podstranicu)",
-       "newtitle": "Na novi naslov",
+       "newtitle": "Novi naziv:",
        "move-watch": "Prati ovu stranicu",
        "movepagebtn": "Premjesti stranicu",
        "pagemovedsub": "Premještanje uspjelo",
        "allmessages-prefix": "Filtriraj prema prefiksu:",
        "allmessages-language": "Jezik:",
        "allmessages-filter-submit": "Idi",
+       "allmessages-filter-translate": "Prevedi",
        "thumbnail-more": "Povećaj",
        "filemissing": "Nedostaje datoteka",
        "thumbnail_error": "Pogreška pri izradbi sličice: $1",
        "javascripttest-pagetext-frameworks": "Molimo izaberite jednu od sljedećih testnih okolina: $1",
        "javascripttest-pagetext-skins": "Izaberite temu (''skin'') za testiranje:",
        "javascripttest-qunit-intro": "Pogledajte [$1 testnu dokumentaciju] na mediawiki.org.",
-       "tooltip-pt-userpage": "Moja suradnička stranica",
+       "tooltip-pt-userpage": "Stranica suradnika {{GENDER:|Your user}}",
        "tooltip-pt-anonuserpage": "Suradnička stranica za IP adresu pod kojom uređujete",
        "tooltip-pt-mytalk": "Moja stranica za razgovor",
        "tooltip-pt-anontalk": "Razgovor o suradnicima s ove IP adrese",
-       "tooltip-pt-preferences": "Moje postavke",
+       "tooltip-pt-preferences": "Vaše postavke",
        "tooltip-pt-watchlist": "Popis stranica koje pratite.",
        "tooltip-pt-mycontris": "Popis mojih doprinosa",
        "tooltip-pt-login": "Predlažemo Vam da se prijavite, ali nije obvezno.",
        "tooltip-t-recentchangeslinked": "Nedavne promjene na stranicama na koje vode ovdašnje poveznice",
        "tooltip-feed-rss": "RSS feed za ovu stranicu",
        "tooltip-feed-atom": "Atom feed za ovu stranicu",
-       "tooltip-t-contributions": "Pogledaj popis suradnikovih doprinosa",
+       "tooltip-t-contributions": "Pogledaj popis doprinosa suradnika  {{GENDER:$1|this user}}",
        "tooltip-t-emailuser": "Pošalji suradniku e-mail",
        "tooltip-t-info": "Više informacija o ovoj stranici",
        "tooltip-t-upload": "Postavi slike i druge medije",
        "pageinfo-category-files": "Broj datoteka",
        "markaspatrolleddiff": "Označi pregledanim",
        "markaspatrolledtext": "Označi ovaj članak pregledanim",
+       "markaspatrolledtext-file": "Označi ovu inačicu datoteke kao pregledanu",
        "markedaspatrolled": "Pregledano",
        "markedaspatrolledtext": "Odabrana promjena [[:$1]] označena je pregledanom.",
        "rcpatroldisabled": "Nadzor nedavnih promjena isključen",
        "newimages-legend": "Filtar",
        "newimages-label": "Naziv datoteke (ili njen dio):",
        "newimages-showbots": "Prikaži datoteke koje su postavili botovi",
+       "newimages-hidepatrolled": "Sakrij pregledana postavljanja",
        "noimages": "Nema slika.",
        "ilsubmit": "Traži",
        "bydate": "po datumu",
        "version-entrypoints-header-entrypoint": "Početna adresa",
        "version-entrypoints-header-url": "URL",
        "version-libraries": "Instalirane biblioteke",
+       "version-libraries-library": "Knjižnica",
        "version-libraries-version": "Inačica",
        "redirect-submit": "Idi",
        "redirect-value": "Vrijednost:",
        "fileduplicatesearch-result-n": "Datoteka \"$1\" ima {{PLURAL:$2|1 identičnu kopiju|$2 identične kopije}}.",
        "fileduplicatesearch-noresults": "Nije pronađena datoteka s imenom \"$1\".",
        "specialpages": "Posebne stranice",
+       "specialpages-note-top": "Legenda",
        "specialpages-note": "* Normalne posebne stranice\n* <span class=\"mw-specialpagerestricted\">Posebne stranice s ograničenim pristupom.</span>",
        "specialpages-group-maintenance": "Izvještaji za održavanje",
        "specialpages-group-other": "Ostale posebne stranice",
        "tags-deactivate-reason": "Razlog:",
        "tags-deactivate-not-allowed": "Nije moguće isključiti oznaku \"$1\".",
        "tags-deactivate-submit": "Isključi",
+       "tags-edit-title": "Uredi oznake",
+       "tags-edit-manage-link": "Upravljaj oznakama",
+       "tags-edit-existing-tags": "Postojeće oznake:",
+       "tags-edit-existing-tags-none": "\"Nema\"",
+       "tags-edit-new-tags": "Nove oznake:",
+       "tags-edit-add": "Dodaj ove oznake:",
+       "tags-edit-remove": "Ukloni ove oznake:",
+       "tags-edit-chosen-placeholder": "Odaberite neke oznake",
+       "tags-edit-chosen-no-results": "Nisu pronađene odgovarajuće oznake",
+       "tags-edit-reason": "Razlog:",
        "comparepages": "Usporedite stranice",
        "compare-page1": "Stranica 1",
        "compare-page2": "Stranica 2",
        "expand_templates_remove_nowiki": "Ukloni <nowiki> tagove u rezultatima.",
        "expand_templates_generate_xml": "Prikaži XML stablo",
        "expand_templates_preview": "Vidi kako će izgledati",
+       "pagelang-name": "Stranica",
+       "pagelang-language": "Jezik",
+       "pagelang-use-default": "Koristi zadani jezik",
        "pagelang-select-lang": "Odaberi jezik",
+       "pagelang-submit": "Pošalji",
+       "right-pagelang": "Promijeni jezik stranice",
        "mediastatistics": "Statistika datoteka",
        "mediastatistics-summary": "Slijede statistike postavljenih datoteka koje pokazuju zadnju inačicu datoteke. Starije ili izbrisane inačice nisu prikazane.",
+       "mediastatistics-header-drawing": "Crteži (vektorske slike)",
+       "mediastatistics-header-audio": "Audio",
+       "mediastatistics-header-video": "Videozapisi",
+       "mediastatistics-header-office": "Ured",
+       "mediastatistics-header-text": "Tekst",
+       "mediastatistics-header-total": "Sve datoteke",
        "headline-anchor-title": "Poveznica na ovaj odlomak",
        "special-characters-group-latin": "latinica",
        "special-characters-group-latinextended": "proširena latinica",
index 0cbe908..0eeb6d5 100644 (file)
@@ -41,7 +41,8 @@
                        "Macofe",
                        "Nyuszika7H",
                        "Matma Rex",
-                       "JulesWinnfield-hu"
+                       "JulesWinnfield-hu",
+                       "Bencoke"
                ]
        },
        "tog-underline": "Hivatkozások aláhúzása:",
        "october-date": "Október $1",
        "november-date": "November $1",
        "december-date": "December $1",
-       "pagecategories": "{{PLURAL:$1|Kategória|Kategória}}",
+       "pagecategories": "{{PLURAL:$1|Kategória|Kategóriák}}",
        "category_header": "A(z) „$1” kategóriába tartozó lapok",
        "subcategories": "Alkategóriák",
        "category-media-header": "A(z) „$1” kategóriába tartozó médiafájlok",
        "category-empty": "''Ebben a kategóriában pillanatnyilag egyetlen lap vagy médiafájl sem szerepel.''",
        "hidden-categories": "{{PLURAL:$1|Rejtett kategória|Rejtett kategóriák}}",
        "hidden-category-category": "Rejtett kategóriák",
-       "category-subcat-count": "''{{PLURAL:$2|Ennek a kategóriának csak egyetlen alkategóriája van.|Ez a kategória az alábbi {{PLURAL:$1|alkategóriával|$1 alkategóriával}} rendelkezik (összesen $2 alkategóriája van).}}''",
+       "category-subcat-count": "{{PLURAL:$2|Ennek a kategóriának csak egyetlen alkategóriája van.|Ez a kategória az alábbi {{PLURAL:$1|alkategóriával|$1 alkategóriával}} rendelkezik (összesen $2 alkategóriája van).}}",
        "category-subcat-count-limited": "Ebben a kategóriában {{PLURAL:$1|egy|$1}} alkategória található.",
-       "category-article-count": "{{PLURAL:$2|A kategóriában csak a következő lap található.|A következő $1 lap található a kategóriában, összesen $2 lapból.}}",
+       "category-article-count": "{{PLURAL:$2|A kategóriában csak a következő lap található.|A következő {{PLURAL:$2|lap|$1 lap}} található a kategóriában, összesen $2 lapból.}}",
        "category-article-count-limited": "Ebben a kategóriában a következő {{PLURAL:$1|lap|$1 lap}} található:",
        "category-file-count": "{{PLURAL:$2|Csak a következő fájl található ebben a kategóriában.|Az összesen $2 fájlból a következő $1-t listázza ez a kategórialap, a többi a további oldalakon található.}}",
        "category-file-count-limited": "Ebben a kategóriában {{PLURAL:$1|egy|$1}} fájl található.",
        "morenotlisted": "A lista nem teljes.",
        "mypage": "Lapom",
        "mytalk": "Vitalap",
-       "anontalk": "Az IP-címhez tartozó vitalap",
+       "anontalk": "Vitalap",
        "navigation": "Navigáció",
        "and": "&#32;és",
        "qbfind": "Keresés",
        "permalink": "Hivatkozás erre a változatra",
        "print": "Nyomtatás",
        "view": "Olvasás",
-       "view-foreign": "Megtekintés ezen: $1",
+       "view-foreign": "Megtekintés itt: $1",
        "edit": "Szerkesztés",
        "edit-local": "Helyi leírás szerkesztése",
        "create": "Létrehozás",
        "laggedslavemode": "'''Figyelem:''' Ez a lap nem feltétlenül tartalmazza a legfrissebb változtatásokat!",
        "readonly": "Az adatbázis le van zárva",
        "enterlockreason": "Add meg a lezárás okát, valamint egy becslést, hogy mikor lesz a lezárásnak vége",
-       "readonlytext": "A wiki adatbázisa ideiglenesen le van zárva (valószínűleg adatbázis-karbantartás miatt). A lezárás időtartama alatt a lapok nem szerkeszthetők, és új szócikkek sem hozhatók létre, az oldalakat azonban lehet böngészni.\n\nAz adminisztrátor, aki lezárta az adatbázist, az alábbi indoklást adta: $1",
+       "readonlytext": "A wiki adatbázisa ideiglenesen le van zárva (valószínűleg adatbázis-karbantartás miatt). A lezárás időtartama alatt a lapok nem szerkeszthetők, és új szócikkek sem hozhatók létre, az oldalakat azonban lehet böngészni.\n\nAz rendszeradminisztrátor, aki lezárta az adatbázist, az alábbi indoklást adta: $1",
        "missing-article": "Az adatbázisban nem található meg a(z) „$1” című lap szövege $2.\n\nEnnek az oka általában az, hogy egy olyan lapra vonatkozó linket követtél, amit már töröltek.\n\nHa ez nem így van, lehet, hogy hibát találtál a szoftverben.\nJelezd ezt egy [[Special:ListUsers/sysop|adminiszttrátornak]] az URL megadásával.",
        "missingarticle-rev": "(változat azonosítója: $1)",
        "missingarticle-diff": "(eltérés: $1, $2)",
        "mypreferencesprotected": "Nincs jogod módosítani a beállításaidat.",
        "ns-specialprotected": "A speciális lapok nem szerkeszthetők.",
        "titleprotected": "Ilyen címmel nem lehet szócikket készíteni, [[User:$1|$1]] letiltotta.\nAz indoklás: „''$2''”.",
-       "filereadonlyerror": "A(z) \"$1\" fájl nem módosítható, mert a(z) \"$2\" fájltároló csak olvasható módban üzemel.\n\nA lezárást végrehajtó rendszergazda az alábbi indoklást adta meg: \"$3\".",
+       "filereadonlyerror": "A(z) „$1” fájl nem módosítható, mert a(z) „$2” fájltároló csak olvasható módban üzemel.\n\nA lezárást végrehajtó rendszeradminisztrátor az alábbi indoklást adta meg: „$3”.",
        "invalidtitle-knownnamespace": "Érvénytelen cím \"$2\" névtérrel és \"$3\" szöveggel",
        "invalidtitle-unknownnamespace": "Érvénytelen cím az ismeretlen $1 névtérszámmal és \"$2\" szöveggel",
        "exception-nologin": "Nem vagy bejelentkezve.",
        "resetpass_submit": "Add meg a jelszót és jelentkezz be",
        "changepassword-success": "A jelszavad megváltoztatása sikeresen befejeződött!",
        "changepassword-throttled": "Túl sok hibás bejelentkezés.\nVárj $1, mielőtt újra próbálkozol.",
+       "botpasswords-label-delete": "Törlés",
        "resetpass_forbidden": "A jelszavak nem változtathatók meg",
        "resetpass-no-info": "Be kell jelentkezned, hogy közvetlenül elérd ezt a lapot.",
        "resetpass-submit-loggedin": "Jelszó megváltoztatása",
        "passwordreset-emailtext-ip": "Valaki (vélhetően Te, a $1 IP-címről) a jelszavad visszaállítását kérte a {{SITENAME}} ($4) oldalon felvett {{PLURAL:$3|fiókban|fiókokban}}. A következő felhasználói {{PLURAL:$3|fiók van|fiókok vannak}} hozzárendelve ehhez az e-mail címhez:\n\n$2\n\n{{PLURAL:$3|Ez az ideiglenes jelszó|Ezek az ideiglenes jelszavak}} $5 nap múlva {{PLURAL:$3|jár|járnak}} le. Jelentkezz be, és cseréld le a jelszavadat. Ha valaki más kérte az emlékeztetőt, vagy eszedbe jutott a régi jelszó, és nem akarod lecserélni a jelszavadat, hagyd figyelmen kívül ezt az üzenetet, és használd a régi jelszavadat.",
        "passwordreset-emailtext-user": "$1 felhasználó jelszó-visszaállítást kért a {{SITENAME}} ($4) oldalon felvett {{PLURAL:$3|fiókban|fiókokban}}. A következő felhasználói {{PLURAL:$3|fiók van|fiókok vannak}} hozzárendelve ehhez az e-mail címhez:\n\n$2\n\n{{PLURAL:$3|Ez az ideiglenes jelszó|Ezek az ideiglenes jelszavak}} $5 nap múlva {{PLURAL:$3|jár|járnak}} le. Jelentkezz be, és cseréld le a jelszavadat. Ha valaki más kérte az emlékeztetőt, vagy eszedbe jutott a régi jelszó, és nem akarod lecserélni a jelszavadat, hagyd figyelmen kívül ezt az üzenetet, és használd a régi jelszavadat.",
        "passwordreset-emailelement": "Felhasználónév: \n$1\n\nIdeiglenes jelszó: \n$2",
-       "passwordreset-emailsentemail": "Ha ez egy regisztrált e-mail-cím a fiókodhoz, egy jelszó-visszaállító e-mailt küldünk.",
+       "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",
        "changeemail": "E-mail cím megváltoztatása vagy eltávolítása",
-       "changeemail-header": "A fiókhoz tartozó e-mail cím megváltoztatá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:",
+       "changeemail-oldemail": "Jelenlegi e-mail-cím:",
+       "changeemail-newemail": "Új e-mail-cím:",
        "changeemail-none": "(nincs)",
        "changeemail-password": "A {{SITENAME}} jelszavad:",
        "changeemail-submit": "E-mail cím megváltoztatása",
        "sig_tip": "Aláírás időponttal",
        "hr_tip": "Vízszintes vonal (ritkán használd)",
        "summary": "Összefoglaló:",
-       "subject": "Téma/fÅ\91cím:",
+       "subject": "Tárgy:",
        "minoredit": "Apró változtatás",
        "watchthis": "A lap figyelése",
        "savearticle": "Lap mentése",
        "copyrightwarning2": "Vedd figyelembe, hogy a {{SITENAME}} wikin végzett összes módosítást szerkeszthetik, módosíthatják vagy eltávolíthatják más szerkesztők.\nHa nem akarod, hogy az írásodat módosítsák, akkor ne küldd be.<br />\nAzt is megígéred, hogy ezt magadtól írtad, vagy egy közkincsből vagy más szabad forrásból másoltad (lásd a(z) $1 lapot a részletekért).\n'''NE KÜLDJ BE JOGVÉDETT MUNKÁT ENGEDÉLY NÉLKÜL!'''",
        "editpage-cannot-use-custom-model": "Ennek a lapnak a tartalommodellje nem változtatható.",
        "longpageerror": "'''HIBA: Az általad beküldött szöveg {{PLURAL:$1|egy kilobájt|$1 kilobájt}} hosszú, ami több az engedélyezett {{PLURAL:$2|egy kilobájtnál|$2 kilobájtnál}}.\nA szerkesztést nem lehet elmenteni.'''",
-       "readonlywarning": "FIGYELMEZTETÉS: A wiki adatbázisát karbantartás miatt zárolták, ezért most nem fogod tudni elmenteni a szerkesztéseidet!\nA lap szövegét másold egy szövegfájlba, amit később felhasználhatsz!'''\n\nAz adatbázist lezáró adminisztrátor az alábbi magyarázatot adta: $1",
+       "readonlywarning": "<strong>FIGYELMEZTETÉS: A wiki adatbázisát karbantartás miatt zárolták, ezért most nem fogod tudni elmenteni a szerkesztéseidet!</strong>\nA lap szövegét másold egy szövegfájlba, amit később felhasználhatsz!\n\nAz adatbázist lezáró rendszeradminisztrátor az alábbi magyarázatot adta: $1",
        "protectedpagewarning": "'''Figyelem: Ez a lap le van védve, így csak adminisztrátori jogosultságokkal rendelkező szerkesztők módosíthatják.'''\nA legutolsó ide vonatkozó naplóbejegyzés alább látható:",
        "semiprotectedpagewarning": "'''Megjegyzés:''' ez a lap védett, így regisztrálatlan vagy újonnan regisztrált szerkesztők nem módosíthatják.",
        "cascadeprotectedwarning": "<strong>Figyelem:</strong> ez a lap le van zárva, csak adminisztrátorok szerkeszthetik, mert a következő kaszkádvédelemmel ellátott {{PLURAL:$1|lapon|lapokon}} be van illesztve:",
        "currentrev-asof": "A lap jelenlegi, $1-kori változata",
        "revisionasof": "A lap $1-kori változata",
        "revision-info": "A lap korábbi változatát látod, amilyen {{GENDER:$6|$2}} $1-kor történt szerkesztése után volt.$7",
-       "previousrevision": "←Régebbi változat",
+       "previousrevision": "← Régebbi változat",
        "nextrevision": "Újabb változat→",
        "currentrevisionlink": "Aktuális változat",
        "cur": "akt",
        "titlematches": "Címbeli egyezések",
        "textmatches": "Szövegbeli egyezések",
        "notextmatches": "Nincsenek szövegbeli egyezések",
-       "prevn": "előző {{PLURAL:$1|egy|$1}}",
-       "nextn": "következő {{PLURAL:$1|egy|$1}}",
+       "prevn": "előző $1",
+       "nextn": "következő $1",
        "prev-page": "előző oldal",
        "next-page": "következő oldal",
        "prevn-title": "Előző {{PLURAL:$1|egy|$1}} találat",
        "nextn-title": "Következő {{PLURAL:$1|egy|$1}} találat",
-       "shown-title": "{{PLURAL:$1|Egy|$1}} találat laponként",
+       "shown-title": "$1 találat megjelenítése laponként",
        "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''A wikin már van „[[:$1]]” nevű lap'''",
        "searchmenu-new": "<strong>Hozd létre a(z) „[[:$1]]” nevű lapot ezen a wikin!</strong>\n{{PLURAL:$2|0=|Lásd még a keresés során talált lapot.|Lásd még a keresés eredményét.}}",
        "searchprofile-images-tooltip": "Fájlok keresése",
        "searchprofile-everything-tooltip": "Minden névtérben keres (a vitalapokat is beleértve)",
        "searchprofile-advanced-tooltip": "Keresés adott névterekben",
-       "search-result-size": "$1 ({{PLURAL:$2|egy|$2}} szó)",
+       "search-result-size": "$1 ($2 szó)",
        "search-result-category-size": "$1 oldal, $2 alkategória, $3 fájl",
        "search-redirect": "(átirányítva innen: $1)",
        "search-section": "($1 szakasz)",
        "prefs-help-signature": "A vitalapra írt hozzászólásaidat négy hullámvonallal (<nowiki>~~~~</nowiki>) írd alá. A lap mentésekor ez lecserélődik az aláírásodra és egy időbélyegre.",
        "badsig": "Érvénytelen aláírás; ellenőrizd a HTML-formázást.",
        "badsiglength": "Az aláírásod túl hosszú.\n{{PLURAL:$1|Egy|$1}} karakternél rövidebbnek kell lennie.",
-       "yourgender": "Biológiai nem:",
+       "yourgender": "Milyen neműként hivatkozzunk rád?",
        "gender-unknown": "Amikor rólad van szó, a szoftver semleges szavakat fog használni, ha lehetséges",
-       "gender-male": "Férfi",
-       "gender-female": "Nő",
+       "gender-male": "Férfiként",
+       "gender-female": "Nőként",
        "prefs-help-gender": "Nem kötelező: a szoftver használja a nemtől függő üzenetek megjelenítéséhez. Az információ mindenki számára látható.",
        "email": "E-mail",
        "prefs-help-realname": "A valódi név nem kötelező.\nHa megadod, akkor leszel feltüntetve a munkád szerzőjeként.",
        "right-blockemail": "szerkesztő e-mail küldési lehetőségének blokkolása",
        "right-hideuser": "felhasználói név blokkolása és elrejtése a külvilág elől",
        "right-ipblock-exempt": "IP-, auto- és tartományblokkok megkerülése",
-       "right-proxyunbannable": "proxyk automatikus blokkjainak megkerülése",
        "right-unblockself": "saját felhasználói fiók blokkjának feloldása",
        "right-protect": "védelmi szintek megváltoztatása és kaszkádolt védelemmel rendelkező lapok szerkesztése",
        "right-editprotected": "„{{int:protect-level-sysop}}” védelmi szintű lapok szerkesztése",
        "right-managechangetags": "Adatbázis [[Special:Tags|címkék]] létrehozása és törlése",
        "right-applychangetags": "[[Special:Tags|címkék]] alkalmazása a változakra",
        "right-changetags": "Egyedi változtatásokon és napló bejegyzéseken tetszőleges [[Special:Tags|címkék]] hozzáadása és törlése",
+       "grant-generic": "„$1” jogosultságcsomag",
+       "grant-group-email": "E-mail küldése",
+       "grant-group-high-volume": "Nagy mennyiségű szerkesztés végrehajtása",
+       "grant-group-customization": "Személyre szabás és beállítások",
+       "grant-group-administration": "Adminisztratív műveletek végrehajtása",
+       "grant-blockusers": "felhasználók blokkolása és blokk feloldása",
+       "grant-createaccount": "fiókok létrehozása",
+       "grant-createeditmovepage": "Lapok készítése, szerkesztése és átnevezése",
+       "grant-delete": "lapok, lapváltozatok és naplóbejegyzések törlése",
+       "grant-editinterface": "MediaWiki-névtér és felhasználói CSS/JavaScript szerkesztése",
+       "grant-editmycssjs": "Felhasználói CSS-ed/JavaScripted szerkesztése",
+       "grant-editmyoptions": "Felhasználói beállításaid szerkesztése",
+       "grant-editmywatchlist": "figyelőlista szerkesztése",
+       "grant-editpage": "létező lapok szerkesztése",
+       "grant-editprotected": "védett lapok szerkesztése",
+       "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-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",
+       "grant-uploadeditmovefile": "fájlok feltöltése, felülírása és átnevezése",
+       "grant-uploadfile": "fájlok feltöltése",
+       "grant-viewdeleted": "Törölt fájlok és lapok megtekintése",
+       "grant-viewmywatchlist": "figyelőlista megtekintése",
        "newuserlogpage": "Új szerkesztők naplója",
        "newuserlogpagetext": "Ez a napló az újonnan regisztrált szerkesztők listáját tartalmazza.",
        "rightslog": "Szerkesztői jogosultságok naplója",
        "recentchanges-label-bot": "Ezt a szerkesztést egy bot hajtotta végre",
        "recentchanges-label-unpatrolled": "Ezt a szerkesztést még nem ellenőrizték",
        "recentchanges-label-plusminus": "Az oldal mérete ennyi bájttal módosult",
-       "recentchanges-legend-heading": "Jelmagyarázat:",
+       "recentchanges-legend-heading": "'''Jelmagyarázat:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (lásd még: [[Special:NewPages|új lapok listája]])",
+       "recentchanges-submit": "Megjelenítés",
        "rcnotefrom": "Alább a <strong>$3 $4</strong> óta történt változtatások láthatóak (legfeljebb <b>$1</b> db).",
-       "rclistfrom": "$3 $2 után történt változtatások megtekintése",
+       "rclistfrom": "$3, $2 után történt változtatások megtekintése",
        "rcshowhideminor": "apró szerkesztések $1",
        "rcshowhideminor-show": "megjelenítése",
        "rcshowhideminor-hide": "elrejtése",
        "number_of_watching_users_pageview": "[Jelenleg {{PLURAL:$1|egy|$1}} felhasználó figyeli]",
        "rc_categories": "Szűkítés kategóriákra („|” jellel válaszd el őket)",
        "rc_categories_any": "Bármelyik",
-       "rc-change-size-new": "{{PLURAL:$1| egy bájt|$1 bájt}} módosítás után",
+       "rc-change-size-new": "$1 bájt módosítás után",
        "newsectionsummary": "/* $1 */ (új szakasz)",
        "rc-enhanced-expand": "Részletek megjelenítése",
        "rc-enhanced-hide": "Részletek elrejtése",
        "filerevert-legend": "Fájl visszaállítása",
        "filerevert-intro": "<span class=\"plainlinks\">A(z) '''[[Media:$1|$1]]''' fájl [$4 verzióját állítod vissza, dátum: $3, $2].</span>",
        "filerevert-comment": "Ok:",
-       "filerevert-defaultcomment": "A $2, $1-i verzió visszaállítása",
+       "filerevert-defaultcomment": "A $1, $2 ($3)-kori verzió visszaállítása",
        "filerevert-submit": "Visszaállítás",
        "filerevert-success": "<span class=\"plainlinks\">A(z) '''[[Media:$1|$1]]''' fájl visszaállítása a(z) [$4 verzióra, $3, $2] sikerült.</span>",
        "filerevert-badversion": "A megadott időbélyegzésű fájlnak nincs helyi változata.",
        "withoutinterwiki-legend": "Előtag",
        "withoutinterwiki-submit": "Megjelenítés",
        "fewestrevisions": "Legrövidebb laptörténetű lapok",
-       "nbytes": "{{PLURAL:$1|egy|$1}} bájt",
+       "nbytes": "$1 bájt",
        "ncategories": "$1 kategória",
        "ninterwikis": "{{PLURAL:$1|egy|$1}} interwiki",
        "nlinks": "{{PLURAL:$1|egy|$1}} hivatkozás",
        "allpagesto": "Lapok listázása a következő címig:",
        "allarticles": "Az összes lap listája",
        "allinnamespace": "Összes lap ($1 névtér)",
-       "allpagessubmit": "Keresés",
+       "allpagessubmit": "Menj",
        "allpagesprefix": "Lapok listázása, amik ezzel az előtaggal kezdődnek:",
        "allpagesbadtitle": "A megadott lapnév nyelvközi vagy wikiközi előtagot tartalmazott, vagy érvénytelen volt. Talán olyan karakter van benne, amit nem lehet lapnevekben használni.",
        "allpages-bad-ns": "A(z) {{SITENAME}} webhelyen nincs \"$1\" névtér.",
        "listgrouprights-namespaceprotection-header": "Névtér korlátozások",
        "listgrouprights-namespaceprotection-namespace": "Névtér",
        "listgrouprights-namespaceprotection-restrictedto": "A szerkesztéshez szükséges jogosultság(ok)",
+       "listgrants-summary": "Lenn láthatóak az OAuth által használt jogosultsági szintek, valamint az azokhoz tartozó jogok. A felhasználók engedélyezhetnek alkalmazásokat, hogy használják a fiókjukat, de csak korlátozott engedélyekkel, amelyek a felhasználó által engedélyezett jogosultsági szinteken alapulnak. Egy ilyen alkalmazás nem használhatja ezeket a jogokat, ha azokkal a felhasználó sem rendelkezik.\nLehetnek [[{{MediaWiki:Listgrouprights-helppage}}|további információk]] az egyes jogokról.",
+       "listgrants-grant": "Jogosultsági szint",
+       "listgrants-rights": "Jogok",
        "trackingcategories": "Nyomkövető kategóriák",
        "trackingcategories-summary": "Ez az oldal azokat a nyomkövető kategóriákat tartalmazza, amelyet a MediaWiki szoftver magától feltölt. Ezen neveket a megfelelő rendszer üzenet módosításával lehet megváltoztatni a {{ns:8}} névtérben.",
        "trackingcategories-msg": "Nyomkövető kategória",
        "wlshowlast": "Az elmúlt $1 órában | $2 napon történt változtatások legyenek láthatóak",
        "watchlistall2": "bármikor",
        "watchlist-hide": "Elrejtés",
+       "watchlist-submit": "Megjelenítés",
+       "wlshowtime": "Időszak:",
+       "wlshowhideminor": "apró szerkesztések",
+       "wlshowhidebots": "botok",
+       "wlshowhideliu": "bejelentkezett felhasználók",
+       "wlshowhideanons": "névtelen felhasználók",
+       "wlshowhidemine": "saját szerkesztéseim",
        "watchlist-options": "A figyelőlista beállításai",
        "watching": "Figyelés...",
        "unwatching": "Figyelés befejezése...",
        "deletepage": "Lap törlése",
        "confirm": "Megerősítés",
        "excontent": "a lap tartalma: „$1”",
-       "excontentauthor": "a lap tartalma: „$1” (és csak „[[Special:Contributions/$2|$2]]” szerkesztette)",
+       "excontentauthor": "a lap tartalma: „$1”, és csak „[[Special:Contributions/$2|$2]]” ([[User talk:$2|vita]]) szerkesztette",
        "exbeforeblank": "az eltávolítás előtti tartalom: „$1”",
        "delete-confirm": "$1 törlése",
        "delete-legend": "Törlés",
        "undeletepagetext": "Az alábbi {{PLURAL:$1|lapot törölték, de még helyreállítható|$1 lapot törölték, de még helyreállíthatók}} az archívumból.\nAz archívumot időről időre üríthetik!",
        "undelete-fieldset-title": "Változatok helyreállítása",
        "undeleteextrahelp": "A lap teljes helyreállításához ne jelölj be egy jelölőnégyzetet sem, csak kattints a '''''{{int:undeletebtn}}''''' gombra.\nA lap részleges helyreállításához jelöld be a kívánt változatok melletti jelölőnégyzeteket, és kattints a '''''{{int:undeletebtn}}''''' gombra.",
-       "undeleterevisions": "$1 változat archiválva",
+       "undeleterevisions": "$1 változat törölve",
        "undeletehistory": "Ha helyreállítasz egy lapot, azzal visszahozod laptörténet összes változatát.\nHa lap törlése óta azonos néven már létrehoztak egy újabb lapot, a helyreállított\nváltozatok a laptörténet végére kerülnek be, a jelenlegi lapváltozat módosítása nélkül.",
        "undeleterevdel": "A törlés visszavonása nem hajtható végre, ha a legfrissebb lapváltozat részleges törlését eredményezi.\nIlyen esetekben vissza kell vonnod a legújabb törölt változatok kijelölését vagy azok elrejtését.",
        "undeletehistorynoadmin": "Ezt a szócikket törölték. A törlés okát alább az összegzésben\nláthatod, az oldalt a törlés előtt szerkesztő felhasználók részleteivel együtt. Ezeknek\na törölt változatoknak a tényleges szövege csak az adminisztrátorok számára hozzáférhető.",
        "tooltip-invert": "Pipáld ki a dobozt, ha el szeretnéd rejteni a kiválasztott névterekben történt változtatásokat (és a kapcsolódó névterekben, amennyiben úgy van beállítva)",
        "tooltip-whatlinkshere-invert": "Pipáld ki a dobozt, ha el szeretnéd rejteni a kiválasztott névterekben található hivatkozásokat.",
        "namespace_association": "Kapcsolódó névtér",
-       "tooltip-namespace_association": "Pipáld ki ezt a dobozt, ha a kiválasztott névtérhez tartozó vita- vagy tárgynévteret is bele szeretnéd venni.",
+       "tooltip-namespace_association": "Pipáld ki ezt a dobozt, ha a kiválasztott névtérhez tartozó vita- vagy tartalmi névteret is bele szeretnéd venni.",
        "blanknamespace": "(Fő)",
        "contributions": "{{GENDER:$1|Szerkesztő}} közreműködései",
        "contributions-title": "$1 közreműködései",
        "whatlinkshere-hidelinks": "linkek $1",
        "whatlinkshere-hideimages": "fájlhivatkozások $1",
        "whatlinkshere-filters": "Elemek szűrése",
+       "whatlinkshere-submit": "Indítás",
        "autoblockid": "$1. autoblokk",
        "block": "Felhasználó blokkolása",
        "unblock": "Felhasználó blokkolásának feloldása",
        "allmessages-language": "Nyelv:",
        "allmessages-filter-submit": "Szűrés",
        "allmessages-filter-translate": "Fordítás",
-       "thumbnail-more": "A kép nagyítása",
+       "thumbnail-more": "Nagyítás",
        "filemissing": "A fájl nincs meg",
        "thumbnail_error": "Hiba a bélyegkép létrehozásakor: $1",
        "thumbnail_error_remote": "Hiba üzenet $1-tól: $2",
        "tooltip-n-currentevents": "Háttérinformáció az aktuális eseményekről",
        "tooltip-n-recentchanges": "A wikiben történt legutóbbi változtatások listája",
        "tooltip-n-randompage": "Egy véletlenszerűen kiválasztott lap betöltése",
-       "tooltip-n-help": "Ha bármi problémád van...",
+       "tooltip-n-help": "Ha bármi problémád van",
        "tooltip-t-whatlinkshere": "Az erre a lapra hivatkozó más lapok listája",
        "tooltip-t-recentchangeslinked": "Az erről a lapról hivatkozott lapok utolsó változtatásai",
        "tooltip-feed-rss": "A lap tartalma RSS hírcsatorna formájában",
        "tooltip-feed-atom": "A lap tartalma Atom hírcsatorna formájában",
-       "tooltip-t-contributions": "A felhasználó közreműködéseinek listája",
+       "tooltip-t-contributions": "A {{GENDER:$1|felhasználó}} közreműködéseinek listája",
        "tooltip-t-emailuser": "Írj levelet ennek a felhasználónak!",
        "tooltip-t-info": "További információk erről a lapról",
        "tooltip-t-upload": "Képek vagy egyéb fájlok feltöltése",
        "exif-urgency-low": "Alacsony ($1)",
        "exif-urgency-high": "Magas ($1)",
        "exif-urgency-other": "Egyedi prioritás ($1)",
-       "namespacesall": "Ã\96sszes",
+       "namespacesall": "Ãsszes",
        "monthsall": "mind",
        "confirmemail": "E-mail cím megerősítése",
        "confirmemail_noemail": "Nincs érvényes e-mail cím megadva a [[Special:Preferences|beállításaidnál]].",
        "logentry-newusers-create2": "$1 létrehozta $3 felhasználói fiókját",
        "logentry-newusers-byemail": "Szerkesztői lap $3 néven létrehozva $1 által, jelszó kiküldve emailben.",
        "logentry-newusers-autocreate": "$1 felhasználói fiók automatikusan létrehozva",
+       "logentry-protect-move_prot": "$1 {{GENDER:$2|áthelyezte}} a védelmi beállításokat a(z) $4 címről a(z) $3 címre",
        "logentry-protect-protect": "$1 {{GENDER:$2|levédte}} a(z) $3 lapot $4",
+       "logentry-protect-protect-cascade": "$1 {{GENDER:$2|levédte}} a(z) $3 lapot $4 [kaszkádvédelem]",
+       "logentry-protect-modify": "$1 {{GENDER:$2|megváltoztatta}} a(z) $3 lap védelmi szintjét $4",
        "logentry-rights-rights": "$1 megváltoztatta $3 csoporttagságát erről: $4 erre: $5",
        "logentry-rights-rights-legacy": "$1 megváltoztatta $3 csoporttagságát",
        "logentry-rights-autopromote": "$1 automatikusan előléptetve erről: $4 erre: $5",
        "api-error-badaccess-groups": "Nincs jogod fájlokat feltölteni erre a wikire.",
        "api-error-badtoken": "Belső hiba: hibás token.",
        "api-error-copyuploaddisabled": "Az URL-címes feltöltés nem engedélyezett ezen a kiszolgálón.",
-       "api-error-duplicate": "Már van {{PLURAL:$1|egy|néhány}} másik fájl az oldalon ugyanilyen tartalommal",
+       "api-error-duplicate": "Már van {{PLURAL:$1|egy|néhány}} másik fájl az oldalon ugyanilyen tartalommal.",
        "api-error-duplicate-archive": "Az oldalon {{PLURAL:$1|szerepelt|szerepeltek}} más {{PLURAL:$1|fájl|fájlok}} is ugyanezzel a tartalommal, de törölve {{PLURAL:$1|lett|lettek}}.",
        "api-error-empty-file": "Az általad elküldött fájl üres volt.",
        "api-error-emptypage": "Új, üres lap létrehozása nem engedélyezett.",
        "log-name-pagelang": "Nyelvváltoztatások naplója",
        "log-description-pagelang": "Ebben a naplóban a lap nyelvének változásait követheted nyomon.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|megváltoztatta}} a(z) $3 lap nyelvét $4 nyelvről $5 nyelvre.",
-       "default-skin-not-found": "Upsz! A wiki alapértelmezett felülete, amely a <code dir=\"ltr\">$wgDefaultSkin</code> szerint <code>$1</code>, nem áll rendelkezésre.\n\nA telepítés az alábbi {{PLURAL:$4|felületet|felületeket}} tartalmazza.\nTovábbi információkat a felület konfigurálásáról és az alapértelmezett felület beállításáról  a [https://www.mediawiki.org/wiki/Manual:Skin_configuration Kézikönyv: Felület konfigurálása] helyen találsz.\n\n$2\n\n; Ha frissen telepítetted a MediaWikit:\n: Valószínűleg a git-ről telepítetted, vagy forrás kódból más módon. Ebben az esetben ez várható. Próbálj telepíteni felületeket a [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org felület könyvtárából], az alábbi módokon:\n:* Töltsd le a [https://www.mediawiki.org/wiki/Download tarball telepítőt], amely számos felületet és kiegészítést tartalmaz. Simán másold ki és beilleszt be a <code>skins/</code> könyvtárat belőle.\n:* Töltsd le az egyedi felület telepítő készleteket a [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] helyről.\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Felület letöltése Git segítségével].\n: Ez nem ütközik a git repository-val, ha MediaWiki fejlesztő vagy.\n\n; Ha most frissítetted a MediaWikit:\n: MediaWiki 1.24 és újabb verziók már nem engedélyezik automatikusan a felületeket (lásd [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Kézikönyv: Felület automatikus felderítése]). Illeszd be az alábbi {{PLURAL:$5|sort|sorokat}} a  <code>LocalSettings.php</code> fájlba, ha engedélyezni akarod {{PLURAL:$5|a|az összes}} telepített felületet:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Ha most módosítottad a <code>LocalSettings.php</code> fájlt:\n: Ellenőrizd a felület neveket, mert lehet, hogy elírtad!",
+       "default-skin-not-found": "Upsz! A wiki alapértelmezett felülete, amely a <code dir=\"ltr\">$wgDefaultSkin</code> szerint <code>$1</code>, nem áll rendelkezésre.\n\nA telepítés az alábbi {{PLURAL:$4|felületet|felületeket}} tartalmazza.\nTovábbi információkat a felület konfigurálásáról és az alapértelmezett felület beállításáról a [https://www.mediawiki.org/wiki/Manual:Skin_configuration Kézikönyv: Felület konfigurálása] helyen találsz.\n\n$2\n\n; Ha frissen telepítetted a MediaWikit:\n: Valószínűleg a gitről telepítetted, vagy közvetlenül forráskódból más módon. Ebben az esetben ez várható. Próbálj telepíteni felületeket a [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org felületkönyvtárából], az alábbi módokon:\n:* Töltsd le a [https://www.mediawiki.org/wiki/Download tarball telepítőt], amely számos felületet és kiegészítést tartalmaz. Simán másold át belőle a <code>skins/</code> könyvtárat.\n:* Tölts le az egyedi felülettelepítő-készleteket a [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] helyről.\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Felület letöltése Git segítségével].\n: Ez nem ütközik a git repository-val, ha MediaWiki fejlesztő vagy.\n\n; Ha most frissítetted a MediaWikit:\n: A MediaWiki 1.24 és újabb verziók már nem engedélyezik automatikusan a telepített felületeket (lásd [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Kézikönyv: Felület automatikus felderítése]). Illeszd be az alábbi {{PLURAL:$5|sort|sorokat}} a <code>LocalSettings.php</code> fájlba, ha engedélyezni akarod {{PLURAL:$5|a|az összes}} telepített felületet:\n\n<pre dir=\"ltr\">$3</pre>\n\n; Ha most módosítottad a <code>LocalSettings.php</code> fájlt:\n: Ellenőrizd a felületneveket, mert lehet, hogy elírtad!",
        "default-skin-not-found-no-skins": "Upsz! A wiki alapértelmezett felülete, amely a <code dir=\"ltr\">$wgDefaultSkin</code> szerint <code>$1</code>, nem áll rendelkezésre.\n\nNincs telepített felület\n\n; Ha frissen telepítetted a MediaWikit:\n: Valószínűleg a git-ről telepítetted, vagy forrás kódból más módon. Ebben az esetben ez várható. Próbálj telepíteni felületeket a [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org felület könyvtárából], az alábbi módokon:\n:* Töltsd le a [https://www.mediawiki.org/wiki/Download tarball telepítőt], amely számos felületet és kiegészítést tartalmaz. Simán másold ki és beilleszt be a <code>skins/</code> könyvtárat belőle.\n:* Töltsd le az egyedi felület telepítő készleteket a [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org] helyről.\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Felület letöltése Git segítségével].\n: Ez nem ütközik a git repository-val, ha MediaWiki fejlesztő vagy.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (engedélyezve)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''letiltva''')",
        "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."
+       "api-error-blacklisted": "Válasszon egy másik, leíró címet.",
+       "randomrootpage": "Véletlen lap a gyökérből"
 }
index 1fb05f4..2e93731 100644 (file)
        "databaseerror-query": "Consulta: $1",
        "databaseerror-function": "Function: $1",
        "databaseerror-error": "Error: $1",
-       "transaction-duration-limit-exceeded": "A fin de evitar un grande retardo de replication, iste transaction ha essite abortate perque le duration de scriptura ($1) excedeva le limite de $2 secundas.\nSi tu modifica multe elementos insimul, tenta facer plure operationes minor in loco de un grande.",
+       "transaction-duration-limit-exceeded": "A fin de evitar un grande retardo de replication, iste transaction ha essite abortate perque le duration de scriptura ($1) excedeva le limite de $2 {{PLURAL:$2|secunda|secundas}}.\nSi tu modifica multe elementos insimul, tenta facer plure operationes minor in loco de un grande.",
        "laggedslavemode": "Attention: Es possibile que le pagina non contine actualisationes recente.",
        "readonly": "Base de datos blocate",
        "enterlockreason": "Describe le motivo del blocada, includente un estimation\nde quando illo essera terminate",
-       "readonlytext": "Al momento, le base de datos es blocate contra nove entratas e altere modificationes, probabilemente pro mantenentia routinari del base de datos, post le qual illo retornara al normal.\n\nLe administrator responsabile dava iste explication: $1",
+       "readonlytext": "In iste momento le base de datos es blocate contra nove entratas e altere modificationes, probabilemente pro mantenentia routinari, post le qual le base de datos essera de novo accessibile.\n\nLe administrator responsabile pro le blocada ha fornite iste explication: $1",
        "missing-article": "Le base de datos non ha trovate le texto de un pagina que illo deberea haber trovate, nominate \"$1\" $2.\n\nCausas normal de iste problema es: tu ha consultate un ''diff'' obsolete, o tu sequeva un ligamine de historia verso un pagina que ha essite delite.\n\nSi isto non es le caso, es possibile que tu ha trovate un error in le software.\nPer favor reporta isto a un [[Special:ListUsers/sysop|administrator]], faciente nota del adresse URL.",
        "missingarticle-rev": "(numero del version: $1)",
        "missingarticle-diff": "(Diff: $1, $2)",
        "mypreferencesprotected": "Tu non ha le permission de modificar le proprie preferentias.",
        "ns-specialprotected": "Le paginas special non es modificabile.",
        "titleprotected": "Iste titulo ha essite protegite contra creation per [[User:$1|$1]].\nLe motivo specificate es ''$2''.",
-       "filereadonlyerror": "Impossibile modificar le file \"$1\" perque le repositorio de files \"$2\" es in modo de lectura sol.\n\nLe administrator qui lo blocava offereva iste explication: \"$3\".",
+       "filereadonlyerror": "Impossibile modificar le file \"$1\" perque le repositorio de files \"$2\" es in modo de lectura sol.\n\nLe administrator de systema qui lo blocava offereva iste explication: \"$3\".",
        "invalidtitle-knownnamespace": "Titulo invalide con spatio de nomines \"$2\" e texto \"$3\"",
        "invalidtitle-unknownnamespace": "Titulo invalide con spatio de nomines incognite $1 e texto \"$2\"",
        "exception-nologin": "Non identificate",
        "passwordreset-emailtext-ip": "Un persona (probabilemente tu, ab le adresse IP $1) requestava le reinitialisation de tu\ncontrasigno de {{SITENAME}} ($4). Le {{PLURAL:$3|conto|contos}} de usator sequente es\nassociate con iste adresse de e-mail:\n\n$2\n\nIste {{PLURAL:$3|contrasigno|contrasignos}} temporari expirara post {{PLURAL:$5|un die|$5 dies}}.\nTu deberea ora aperir session e eliger un nove contrasigno. Si un altere persona faceva iste\nrequesta, o si tu te ha rememorate tu contrasigno original e non plus\nvole cambiar lo, tu pote ignorar iste message e continuar a usar le ancian\ncontrasigno.",
        "passwordreset-emailtext-user": "Le usator $1 in {{SITENAME}} requestava un reinitialisation de tu contrasigno in {{SITENAME}}\n($4). Le {{PLURAL:$3|conto|contos}} de usator sequente es associate con iste adresse de e-mail:\n\n$2\n\nIste {{PLURAL:$3|contrasigno|contrasignos}} temporari expirara post {{PLURAL:$5|un die|$5 dies}}.\nTu deberea ora aperir session e eliger un nove contrasigno. Si un altere persona faceva iste\nrequesta, o si tu te ha rememorate tu contrasigno original e non plus\nvole cambiar lo, tu pote ignorar iste message e continuar a usar le ancian\ncontrasigno.",
        "passwordreset-emailelement": "Nomine de usator: \n$1\n\nContrasigno temporari: \n$2",
-       "passwordreset-emailsentemail": "Si iste es le adresse de e-mail registrate pro tu conto, alora un message de e-mail pro le reinitialisation del contrasigno essera inviate.",
+       "passwordreset-emailsentemail": "Si iste es un adresse de e-mail registrate pro tu conto, alora un message de e-mail pro le reinitialisation del contrasigno essera inviate.",
+       "passwordreset-emailsentusername": "Si il ha un correspondente adresse de e-mail registrate, 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",
        "changeemail": "Cambiar o remover adresse de e-mail",
        "copyrightwarning2": "Nota ben que tote le contributiones a {{SITENAME}} pote esser redigite, alterate, o eliminate per altere contributores.\nSi tu non vole que tu scripto sia modificate impietosemente, alora non lo submitte hic.<br />\nIn addition, tu nos garanti que tu es le autor de isto, o que tu lo ha copiate de un ressource a dominio public o alteremente libere de derectos (vide $1 pro detalios).\n'''Non submitte material subjecte a copyright sin autorisation expresse!'''",
        "editpage-cannot-use-custom-model": "Le modello de contento de iste pagina non pote esser cambiate.",
        "longpageerror": "'''Error: Le texto que tu submitteva occupa {{PLURAL:$1|un kilobyte|$1 kilobytes}}, excedente le maximo de {{PLURAL:$2|un kilobyte|$2 kilobytes}}.'''\nIllo non pote esser salveguardate.",
-       "readonlywarning": "'''Attention: Le base de datos ha essite blocate pro mantenentia. Tu non pote salveguardar tu modificationes in iste momento.'''\nNos recommenda copiar-e-collar le texto in un file e salveguardar lo pro plus tarde.\n\nLe administrator qui ha blocate le base de datos ha fornite iste explication: $1",
+       "readonlywarning": "<strong>Attention: Le base de datos ha essite blocate pro mantenentia. Tu non pote salveguardar tu modificationes in iste momento.</strong>\nNos recommenda copiar e collar le texto in un file e salveguardar lo pro plus tarde.\n\nLe administrator de systema qui ha blocate le base de datos ha fornite iste explication: $1",
        "protectedpagewarning": "'''Attention:  Iste pagina ha essite protegite de sorta que solmente usatores con privilegios de administrator pote modificar lo.''' Le ultime entrata del registro es fornite hic infra pro referentia:",
        "semiprotectedpagewarning": "'''Nota:''' Iste pagina ha essite protegite de maniera que solmente usatores registrate pote modificar lo. Le ultime entrata del registro es fornite hic infra pro referentia:",
        "cascadeprotectedwarning": "<strong>Attention:</strong> Iste pagina ha essite protegite de maniera que solmente usatores con privilegios de administrator pote modificar lo, perque illo es transcludite in le sequente {{PLURAL:$1|pagina|paginas}} protegite in cascada:",
        "permissionserrors": "Error de permission",
        "permissionserrorstext": "Tu non ha le permission de facer isto, pro le sequente {{PLURAL:$1|motivo|motivos}}:",
        "permissionserrorstext-withaction": "Tu non ha le permission de $2, pro le sequente {{PLURAL:$1|motivo|motivos}}:",
-       "contentmodelediterror": "Non es possibile modificar iste version perque su modello de contento es <code>$1</code>, e le modello de contento actual del pagina es <code>$2</code>.",
+       "contentmodelediterror": "Non es possibile modificar iste version perque su modello de contento es <code>$1</code>, un altere que le modello de contento actual del pagina, <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Attention: Tu es sur le puncto de recrear un pagina que ha essite delite anteriormente.'''\n\nTu deberea considerar si il es appropriate continuar a modificar iste pagina.\nEcce le registro de deletiones e de renominationes pro iste pagina:",
        "moveddeleted-notice": "Iste pagina ha essite delite.\nIn basso se revela le registro de deletiones e de modificationes del pagina pro ulterior informationes.",
        "moveddeleted-notice-recent": "Regrettabilemente iste pagina ha essite delite (in le ultime 24 horas).\nLe registro de deletion e renomination pro le pagina es fornite hic infra pro vostre information.",
        "recentchanges-legend-heading": "'''Legenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (vide etiam le [[Special:NewPages|lista de nove paginas]])",
        "recentchanges-legend-plusminus": "(''±123'')",
+       "recentchanges-submit": "Monstrar",
        "rcnotefrom": "Ecce le {{PLURAL:$5|modification|modificationes}} a partir del <strong>$3 a $4</strong> (usque a <strong>$1</strong> entratas monstrate).",
        "rclistfrom": "Monstrar nove modificationes a partir del $3 a $2",
        "rcshowhideminor": "$1 modificationes minor",
index c06a6f9..0aefcf4 100644 (file)
@@ -43,7 +43,8 @@
                        "Ilham",
                        "Matma Rex",
                        "WongKentir",
-                       "Rachmat.Wahidi"
+                       "Rachmat.Wahidi",
+                       "Arief"
                ]
        },
        "tog-underline": "Garis bawahi pranala:",
        "passwordreset-emailtext-ip": "Seseorang (mungkin Anda, dari alamat IP $1) meminta pengingat\ndetail akun untuk {{SITENAME}} ($4). {{PLURAL:$3|Akun|Akun-akun}} berikut\nterkait dengan alamat surel ini:\n\n$2\n\n{{PLURAL:$3|Sandi sementara}} berikut akan kedaluwarsa dalam {{PLURAL:$5|$5 hari}}.\nAnda harus masuk dan memilih sandi baru sekarang. Jika orang lain membuat\npermintaan ini atau jika Anda ingat sandi asli dan tidak lagi\ningin mengubahnya, Anda dapat mengabaikan pesan ini dan terus menggunakan sandi lama.",
        "passwordreset-emailtext-user": "Seseorang (mungkin Anda, dari alamat IP $1) meminta pengingat detail akun untuk {{SITENAME}} ($4).\n{{PLURAL:$3|Akun|Akun-akun}} berikut terkait dengan alamat surel ini:\n\n$2\n\n{{PLURAL:$3|Sandi sementara}} berikut akan kedaluwarsa dalam {{PLURAL:$5|$5 hari}}.\nAnda harus masuk dan memilih sandi baru sekarang. Jika orang lain membuat\npermintaan ini atau jika Anda ingat sandi asli dan tidak lagi\ningin mengubahnya, Anda dapat mengabaikan pesan ini dan terus menggunakan sandi lama.",
        "passwordreset-emailelement": "Nama pengguna: \n$1\n\nSandi sementara: \n$2",
-       "passwordreset-emailsent": "Surel setel ulang kata sandi telah dikirimkan.",
+       "passwordreset-emailsentemail": "Surel setel ulang kata sandi telah dikirimkan.",
        "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",
        "changeemail": "Ubah alamat surel",
        "prefs-help-prefershttps": "Preferensi ini akan diaktifkan kali berikutnya Anda masuk log.",
        "prefswarning-warning": "Perubahan preferensi anda belum tersimpan. Apabila anda meninggalkan halaman ini tanpa men-klik \"$1\" preferensi anda tidak akan diperbarui.",
        "prefs-tabs-navigation-hint": "Tip: Anda dapat menggunakan tombol panah kiri dan kanan untuk bernavigasi antartab di dalam daftar tab.",
-       "email-address-validity-valid": "Alamat surel tampaknya sah",
-       "email-address-validity-invalid": "Masukkan alamat surel yang sah",
        "userrights": "Manajemen hak pengguna",
        "userrights-lookup-user": "Mengatur kelompok pengguna",
        "userrights-user-editname": "Masukkan nama pengguna:",
        "right-blockemail": "Memblokir pengiriman surel oleh pengguna",
        "right-hideuser": "Memblokir nama pengguna dan menyembunyikannya dari publik",
        "right-ipblock-exempt": "Mengabaikan pemblokiran IP, pemblokiran otomatis, dan rentang pemblokiran",
-       "right-proxyunbannable": "Mengabaikan pemblokiran otomatis atas proksi",
        "right-unblockself": "Lepaskan blokir sendiri",
        "right-protect": "Ubah tingkat pelindungan dan sunting halaman yang dilindungi beruntun",
        "right-editprotected": "Sunting halaman yang dilindungi sebagai \"{{int:protect-level-sysop}}\"",
        "right-sendemail": "Mengirim surel ke pengguna lain",
        "right-passwordreset": "Lihat surel pengaturulangan kata sandi",
        "right-managechangetags": "Membuat dan menghapus [[Special:Tags|tag]] dari basis data",
+       "grant-generic": "\"$1\" bundel hak akses",
+       "grant-group-page-interaction": "Berinteraksi dengan halaman",
+       "grant-group-file-interaction": "Berinteraksi dengan media",
+       "grant-group-watchlist-interaction": "Berinteraksi dengan daftar pantauan Anda",
+       "grant-group-email": "Mengirim surel",
+       "grant-group-high-volume": "Melakukan aktivitas yang amat banyak",
+       "grant-group-customization": "Kustomisasi dan preferensi",
+       "grant-group-administration": "Lakukan tindakan administratif",
+       "grant-group-other": "Aktivitas lain-lain",
+       "grant-blockusers": "Blokir dan buka pemblokiran pengguna",
+       "grant-createaccount": "Buat akun",
+       "grant-createeditmovepage": "Membuat, menyunting dan memindahkan halaman",
+       "grant-delete": "Menghapus halaman, revisi, dan log entri",
+       "grant-editinterface": "Menyunting ruang nama MediaWiki dan CSS/JavaScript pengguna",
+       "grant-editmycssjs": "Menyunting halaman CSS/JavaScript Anda",
+       "grant-editmyoptions": "Menyunting preferensi Anda",
+       "grant-editmywatchlist": "Menyunting daftar pantauan Anda",
+       "grant-editpage": "Menyunting halaman yang ada",
+       "grant-editprotected": "Menyunting halaman yang dilindungi",
+       "grant-highvolume": "Amat sering menyunting",
+       "grant-oversight": "Sembunyikan pengguna dan revisinya",
+       "grant-patrol": "Tandai halaman terpatroli",
+       "grant-protect": "Melindungi dan membuka perlindungan halaman",
+       "grant-rollback": "Membalikkan halamn",
+       "grant-sendemail": "Mengirim surel pada pengguna lain",
+       "grant-uploadeditmovefile": "Mengunggah, mengganti, dan memindahkan berkas",
+       "grant-uploadfile": "Mengunggah berkas baru",
+       "grant-viewdeleted": "Melihat informasi halaman yang dihapus",
+       "grant-viewmywatchlist": "Melihat daftar pantauan Anda",
        "newuserlogpage": "Log pengguna baru",
        "newuserlogpagetext": "Di bawah ini adalah log pendaftaran pengguna baru",
        "rightslog": "Log perubahan hak akses",
        "upload-form-label-select-file": "Pilih berkas",
        "upload-form-label-infoform-title": "Rincian",
        "upload-form-label-infoform-name": "Nama",
+       "upload-form-label-infoform-name-tooltip": "Judul singkat yang unik untuk berkas, yang akan menjadi nama berkas. Anda dapat gunakan bahasa yang sederhana berikut spasi. Jangan menyertakan ekstensi berkas.",
        "upload-form-label-infoform-description": "Deskripsi",
+       "upload-form-label-infoform-description-tooltip": "Jelaskan dengan singkat hal-hal penting tentang karya ini.\nUntuk foto, sebutkan hal-hal utama yang ditampilkan, kesempatan atau tempat yang ditampilkan di foto.",
        "upload-form-label-usage-title": "Penggunaan",
        "upload-form-label-usage-filename": "Nama berkas",
        "foreign-structured-upload-form-label-own-work": "Ini adalah karya saya sendiri",
        "listgrouprights-namespaceprotection-header": "Batasan ruang nama",
        "listgrouprights-namespaceprotection-namespace": "Ruang nama",
        "listgrouprights-namespaceprotection-restrictedto": "Hak yang mengizinkan pengguna untuk menyunting",
+       "listgrants-grant": "Izin",
+       "listgrants-rights": "Hak",
        "trackingcategories": "Kategori pelacak",
        "trackingcategories-summary": "Halaman ini mendaftarkan kategori-kategori pelacak yang secara otomatis dibuat oleh perangkat lunak MediaWiki. Nama kategori ini dapat diubah dengan mengubah pesan sistem yang sesuai dalam ruang nama {{ns:8}}.",
        "trackingcategories-msg": "Kategori pelacak",
        "unblock": "Buka blokir pengguna",
        "blockip": "Blokir {{GENDER:$1|pengguna}}",
        "blockip-legend": "Blokir pengguna",
-       "blockiptext": "Gunakan formulir di bawah untuk memblokir akses penulisan dari sebuah alamat IP atau pengguna tertentu.\nIni hanya boleh dilakukan untuk mencegah vandalisme, dan sejalan dengan [[{{MediaWiki:Policy-url}}|kebijakan]].\nMasukkan alasan Anda di bawah (contoh, menuliskan nama halaman yang telah divandalisasi).",
+       "blockiptext": "Gunakan formulir di bawah untuk memblokir akses penulisan dari sebuah alamat IP atau pengguna tertentu.\nIni hanya boleh dilakukan untuk mencegah vandalisme, dan sejalan dengan [[{{MediaWiki:Policy-url}}|kebijakan]].\nMasukkan alasan Anda di bawah (contoh, menuliskan nama halaman yang telah divandalisasi).\nAnda dapat memblok rentang IP menggunakan [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] syntax; the largest allowed range is /$1 for IPv4 and /$2 for IPv6.",
        "ipaddressorusername": "Alamat IP atau nama pengguna:",
        "ipbexpiry": "Kedaluwarsa:",
        "ipbreason": "Alasan:",
        "movenosubpage": "Halaman ini tak memiliki subhalaman.",
        "movereason": "Alasan:",
        "revertmove": "batalkan",
-       "delete_and_move": "Hapus dan pindahkan",
        "delete_and_move_text": "==Penghapusan diperlukan==\nHalaman yang dituju, \"[[:$1]]\", telah mempunyai isi. Apakah Anda hendak menghapusnya untuk memberikan ruang bagi pemindahan?",
        "delete_and_move_confirm": "Ya, hapus halaman tersebut",
        "delete_and_move_reason": "Dihapus untuk mengantisipasikan pemindahan halaman dari \"[[$1]]\"",
        "mediastatistics": "Statistik media",
        "mediastatistics-summary": "Statistik mengenai jenis berkas yang diunggah. Hanya mencakup versi terbaru dari berkas. Berkas lama dan berkas yang sudah dihapus tidak termasuk.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bita}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Ukuran total untuk seksi ini: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Ukuran total berkas untuk semua berkas: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "Tipe MIME",
        "mediastatistics-table-extensions": "Ekstensi",
        "mediastatistics-table-count": "Jumlah file",
        "special-characters-title-minus": "tanda kurang",
        "mw-widgets-dateinput-placeholder-day": "TTTT-BB-HH",
        "mw-widgets-dateinput-placeholder-month": "TTTT-BB",
-       "api-error-blacklisted": "Pilih judul lain yang deskriptif"
+       "api-error-blacklisted": "Pilih judul lain yang deskriptif",
+       "randomrootpage": "Halaman dasar sembarang"
 }
index 54d78c3..8ef32b7 100644 (file)
        "laggedslavemode": "<strong>Ballaag:</strong> Ti panid ket mabalin a saan nga aglaon kadagiti kinaudi a panagpabaro.",
        "readonly": "Narikepan ti database",
        "enterlockreason": "Agikabil ti rason para iti pannakarikep, mangiraman ti maysa a karkulo no kaanonto a malukatan",
-       "readonlytext": "Ti database ket agdama a narikpan kadagiti baro a panagikabil ken panagbaliw, mabalin a gapu dagiti kadawyan a pagsimpa, kalpasanna a normalto nga agsubli.\n\nTi administrador a nangrikep ket nangited iti daytoy a palawag: $1",
+       "readonlytext": "Ti database ket agdama a narikpan kadagiti baro a panagikabil ken panagbaliw, mabalin a gapu dagiti kadawyan a pagsimpa, kalpasanna a normalto nga agsubli.\n\nTi administrador ti sistema a nangrikep ket nangited iti daytoy a palawag: $1",
        "missing-article": "Ti database ket saan a nakabiruk ti testo ti panid a mabirukanna koma, a nanaganan ti \"$1\" $2.\n\nDaytoy ket kadawyan a gapuanan babaen ti sumaganad a baak a paggiddiatan wenno silpo ti pakasaritaan ti maysa panid a dati a naikkat.\n\nNo saan a kasta, mabalin a nakasarakka ti parikut ti sopwer.\n\nPangngaasi nga ipadamagmo kadagiti [[Special:ListUsers/sysop|administrador]], isuratmo ti pakaammo dayta nga URL.",
        "missingarticle-rev": "(rebision#: $1)",
        "missingarticle-diff": "(Dip: $1, $2)",
        "mypreferencesprotected": "Awan pammalubosmo nga agurnos kadagiti kakaykayatam.",
        "ns-specialprotected": "Saan a mabalin nga urnosen dagiti espesial a panid.",
        "titleprotected": "Daytoy a titulo ket nasalakniban manipud iti pannakapartuat babaen ni [[User:$1|$1]].\nTi naited a rason ket \"<em>$2</em>\".",
-       "filereadonlyerror": "Di nabaliwan ti papeles ti \"$1\" gapu ta ti repositorio ti papeles ti \"$2\" ket mabasa laeng a moda.\n\nTi administrador a nangserra ket nangited iti daytoy a panagilawlawag: \"$3\".",
+       "filereadonlyerror": "Di nabaliwan ti papeles ti \"$1\" gapu ta ti repositorio ti papeles ti \"$2\" ket mabasa laeng a moda.\n\nTi administrador ti sistema a nangserra ket nangited iti daytoy a panagilawlawag: \"$3\".",
        "invalidtitle-knownnamespace": "Imbalido a titulo iti nagan ti espasio \"$2\" ken teksto \"$3\"",
        "invalidtitle-unknownnamespace": "Imbalido a titulo iti di ammo a nagan ti espasio a bilang $1 ken teksto \"$2\"",
        "exception-nologin": "Saan a nakastrek",
        "passwordreset-emailtext-ip": "Adda (baka sika, ti naggapuan ti IP a pagtaengan $1) a nagkiddaw ti maysa a panangisaad manen ti kontrasenias para iti {{SITNAME}} ($4) . {{PLURAL:$3|Ti |Dagiti}} sumaganad a pakabilangan ti agar-aramat ket\nmainaig iti daytoy nga esurat a pagtaengan:\n\n$2\n\n{{PLURAL:$3|Daytoy temporario a kontrasenias|Dagitoy temporario a kontrasenias}} ket agpaso {{PLURAL:$5|iti maysa nga aldaw|kadagiti $5 nga aldaw}}.\nSumrekka koman tapno agpilika ti baro a kontraseniasmo tattan. No adda met sabali a nagaramid daytoy a \npanagkiddaw, wenno malagipmo ti dati a kontraseniasmo, ket saanmo a kayaten a sukatan, saanmo nga ikaskaso daytoy a mensahe ken \nagtuloyka nga agusar ti daan a kontrasenias.",
        "passwordreset-emailtext-user": "Daytoy nga agar-aramat $1 iti {{SITENAME}} ket nagkiddaw ti maysa a panangisaad manen ti bukod a kontrasenias para iti {{SITENAME}}\n($4) . {{PLURAL:$3|Ti|Dagiti}} sumaganad a pakabilangan ti agar-aramat ket\nmainaig iti daytoy nga esurat a pagtaengan:\n\n$2\n\n{{PLURAL:$3|Daytoy temporario a kontrasenias|Dagitoy temporario a kontrasenias}} ket agpaso {{PLURAL:$5|iti maysa nga aldaw|kadagiti $5 nga aldaw}}.\nSumrekka koman tapno agpilika ti baro a kontraseniasmo tattan. No adda met sabali a nagaramid daytoy a \npanagkiddaw, wenno malagipmo ti dati a kontraseniasmo, ken saanmo a kayaten a sukatan, saanmo nga ikaskaso daytoy a mensahe ken \nagtuloykan nga agusar ti daan a kontraseniasmo.",
        "passwordreset-emailelement": "Nagan ti agar-aramat: \n$1\n\nTemporario a kontrasenias: \n$2",
-       "passwordreset-emailsent": "No daytoy ket nairehistro nga adres ti esurat para iti pakabilangam, maipatulodto ti maysa nga esurat iti panangisaad manen ti kontrasenias.",
+       "passwordreset-emailsentemail": "No daytoy nga adres ti esurat ket mainaig iti pakabilangam, maipatulodto ti maysa nga esurat iti panangisaad manen ti kontrasenias.",
        "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",
        "changeemail": "Sukatan wenno ikkaten ti adres ti esurat",
        "copyrightwarning2": "Pangngaasi a laglagipen nga amin a kontribusion iti {{SITENAME}} ket mabalin a maurnos, mabaliwan, wenno ikkaten dagiti sabali a kontributor.\nNo dimo kayat a ti sinuratmo ket maurnos nga awanan-asi ken maiwaras nga awan sungsungbatan kenka, saanmon nga ited ditoy.<br />\nIkarkarim pay kadakami a bukodmo a sinurat daytoy, wenno kinopia manipud iti publiko a dominio wenno ti kapadpadana a nawaya a nagtaudan. (kitaen ti $1 para kadagiti salaysay).\n<strong>Saan a mangited ti nakarbengan ti kopia nga obra no awan iti pammalubos!</strong>",
        "editpage-cannot-use-custom-model": "Saan a mabaliwan ti modelo ti linaon iti daytoy a panid.",
        "longpageerror": "<strong>Biddut: Ti teksto nga intedmo ket {{PLURAL:$1|maysa a kilobyte|$1 kilkilobyte}} ti katiddogna, nga at-atiddog ngem ti kangatuan iti  {{PLURAL:$2|maysa a kilobyte|$2 kilkilobyte}}.</strong>\nSaan a mabalin a maidulin.",
-       "readonlywarning": "<strong>Ballaag: Narikepan ti database tapno mataripato, isu a saanmo a mabalin nga idulin dagita inurnosmo tattan.</strong>\nMabalinmo ti agkopia ken agipegket ti testom iti papeles ti testo ken idulinmo daytoy intono madamdama.\n\nTi administrador a nangrikep ket nangited iti daytoy a palawag: $1",
+       "readonlywarning": "<strong>Ballaag: Narikepan ti database tapno mataripato, isu a saanmo a mabalin nga idulin dagita inurnosmo tattan.</strong>\nMabalinmo ti agkopia ken agipegket ti testom iti papeles ti testo ken idulinmo daytoy intono madamdama.\n\nTi administrador ti sistema a nangrikep ket nangited iti daytoy a palawag: $1",
        "protectedpagewarning": "<strong>Ballaag: Daytoy a panid ket nasalakniban tapno dagiti laeng agar-aramat nga addaan iti gundaway nga administrador ti makaurnos ditoy.</strong>\nTi naudi a naikabil iti listaan ket naited dita baba para iti reperensia:",
        "semiprotectedpagewarning": "<strong>Nota:</strong> Nasalakniban daytoy a panid tapno dagiti laeng nakarehistro nga agar-aramat ti makaurnos ditoy.\nTi naudi a naikabil iti listaan ket naited dita baba para iti reperensia:",
        "cascadeprotectedwarning": "<strong>Ballaag:</strong> Daytoy a panid ket nasalakniban tapno dagiti laeng agar-aramat nga addaan iti gundaway nga administrador ti makaurnos gapu ta nailak-am {{PLURAL:$1|iti sumaganad a panid|kadagiti sumaganad a panid}} a nasalakniban iti sariap:",
        "permissionserrors": "Biddut ti pammalubos",
        "permissionserrorstext": "Awan ti pammalubosmo nga agaramid iti dayta, gapu ti sumaganad {{PLURAL:$1|a rason|a rasrason}}:",
        "permissionserrorstext-withaction": "Awan ti pammalubosmo nga $2, gapu ti sumaganad a {{PLURAL:$1|rason|rasrason}}:",
-       "contentmodelediterror": "Saanmo a maurnos daytoy a rebision gapu ta ti modelo ti linaon ket <code>$1</code>, ken ti agdama a linaon ti panid ket <code>$2</code>.",
+       "contentmodelediterror": "Saanmo a maurnos daytoy a rebision gapu ta ti modelo ti linaon ket <code>$1</code>, a maigiddiat manipud iti agdama a modelo ti linaon ti panid ti <code>$2</code>.",
        "recreate-moveddeleted-warn": "<strong>Ballaag: Agparpartuatka manen ti dati a naikkat a panid.</strong>\n\nUsigem koma no maitutop ti agtuloy nga agurnos iti daytoy a panid.\nTi listaan ti pannakaikkat ken pannakaiyalis para iti daytoy a panid ket naited ditoy para iti pakainugotan:",
        "moveddeleted-notice": "Naikkaten daytoy a panid.\nTi listaan ti pannakaikkat ken pannakaiyalis para iti panid ket naited dita baba para iti reperensia.",
        "moveddeleted-notice-recent": "Pasensian, daytoy a panid ket kaik-ikkat idi (iti kaunegan dagiti 24 nga oras).\nTi listaan ti pannakaikkat ken pannakaiyalis para iti panid ket naited dita baba para iti reperensia.",
        "prefs-help-prefershttps": "Daytoy a kakaykayatan ket mapakabaelanto iti sumaruno nga iseserrekmo.",
        "prefswarning-warning": "Nagaramikka kadagiti panagbalbaliw kadagiti kakaykayatam a saan pay a naidulin.\nNo panawan daytoy a panid a saan nga agpindut iti \"$1\" dagiti kakaykayatam ket saanto a mapabaro.",
        "prefs-tabs-navigation-hint": "Pakaammo: Mabalinmo nga usaren dagiti kanigid ken kanawan a tekla ti pana tapno madaliasat ti baetan dagiti etiketa iti listaan dagiti etiketa.",
-       "email-address-validity-valid": "Ti esurat a pagtaengan ket kasla umisu",
-       "email-address-validity-invalid": "Ikabil ti umisu nga esurat a pagtaengan",
        "userrights": "Panagtaripato kadagiti karbengan ti agar-aramat",
        "userrights-lookup-user": "Agtaripato kadagiti grupo ti agar-aramat",
        "userrights-user-editname": "Mangiserrek iti nagan ti agar-aramat:",
        "recentchanges-label-plusminus": "Ti panagbaliw ti kadakkel ti panid babaen ti bilang dagiti byte",
        "recentchanges-legend-heading": "'''Leyenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (kitaen pay ti [[Special:NewPages|listaan ti baro a pampanid]])",
+       "recentchanges-submit": "Ipakita",
        "rcnotefrom": "Dita baba ket {{PLURAL:$5|ti sinukatan|dagiti sinukatan}} manipud idi <strong>$3, $4</strong> (aginggana iti <strong>$1</strong> a naipakita).",
        "rclistfrom": "Ipakita dagiti kabarbaro a sinukatan a mangrugi manipud idi $2, $3",
        "rcshowhideminor": "$1 dagiti bassit a panagurnos",
        "foreign-structured-upload-form-label-own-work": "Daytoy ket bukodko nga obra",
        "foreign-structured-upload-form-label-infoform-categories": "Katkategoria",
        "foreign-structured-upload-form-label-infoform-date": "Petsa",
+       "foreign-structured-upload-form-3-label-yes": "Wen",
+       "foreign-structured-upload-form-3-label-no": "Saan",
        "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.",
        "mostrevisions": "Dagiti panid a kaaduan kadagiti rebision",
        "prefixindex": "Amin a pampanid nga addaan iti pasakbay",
        "prefixindex-namespace": "Amin a pampanid nga addaan iti pasaruno (nagan ti espasio ti $1)",
+       "prefixindex-submit": "Ipakita",
        "prefixindex-strip": "Ikkaten ti pasakbay iti listaan",
        "shortpages": "Dagiti ababa a panid",
        "longpages": "Dagiti atiddog a panid",
        "protectedpages-performer": "Nangsalaknib nga agar-aramat",
        "protectedpages-params": "Dagiti parametro ti panagsalaknib",
        "protectedpages-reason": "Rason",
+       "protectedpages-submit": "Ipakita dagiti panid",
        "protectedpages-unknown-timestamp": "Di ammo",
        "protectedpages-unknown-performer": "Di ammo nga agar-aramat",
        "protectedtitles": "Dagiti nasalakniban a titulo",
        "protectedtitles-summary": "Daytoy a panid ket ilistana dagiti titulo nga agdama a nasalakniban manipud ti pannakapartuat. Para iti listaan dagiti adda a panid a nasalakniban, kitaen ti  [[{{#special:ProtectedPages}}|{{int:protectedpages}}]]..",
        "protectedtitlesempty": "Awan dagiti titulo nga agdama a nasalakniban kadagitoy a parametro.",
+       "protectedtitles-submit": "Ipakita dagiti titulo",
        "listusers": "Listaan ti agar-aramat",
        "listusers-editsonly": "Ipakita laeng dagiti agar-aramat nga addaan kadagiti inurnos",
        "listusers-creationsort": "Ilasin babaen ti petsa a pannakapartuat",
        "usereditcount": "$1 {{PLURAL:$1|nga inurnos|kadagiti inurnos}}",
        "usercreated": "{{GENDER:$3|Pinartuat}} idi $1, $2",
        "newpages": "Baro a pampanid",
+       "newpages-submit": "Ipakita",
        "newpages-username": "Nagan ti agar-aramat:",
        "ancientpages": "Dagiti kadaanan a panid",
        "move": "Iyalis",
        "specialloguserlabel": "Nangitungpal:",
        "speciallogtitlelabel": "Puntaan (titulo wenno {{ns:user}}:nagan ti agar-aramat para iti agar-aramat):",
        "log": "Dagiti listaan",
+       "logeventslist-submit": "Ipakita",
        "all-logs-page": "Amin a listaan a publiko",
        "alllogstext": "Naikaykaysa a panagiparang kadagiti amin a magun-od a listaan iti {{SITENAME}}.\nMapabassitmo ti panagkita babaen ti panagpili ti kita ti listaan, ti nagan ti agar-aramat (sensitibo ti kadakkel ti letra), wenno ti naapektaran a panid (sensitibo pay ti kadakkel ti letra).",
        "logempty": "Awan dagiti maipada a banag iti listaan.",
        "cachedspecial-viewing-cached-ts": "Kitkitaem ti maysa a naidulin a bersion iti daytoy a panid, a mabalin daytoy a saan a kompleto nga agpayso.",
        "cachedspecial-refresh-now": "Kitaen ti kinaudian.",
        "categories": "Katkategoria",
+       "categories-submit": "Ipakita",
        "categoriespagetext": "Ti sumaganad a {{PLURAL:$1|kategoria ket aglaon|katkategoria ket aglaon}} kadagiti panid wenno midia.\n[[Special:UnusedCategories|Dagiti saan a nausar a kategoria]] ket saan a maiparang ditoy.\nKitaen met [[Special:WantedCategories|dagiti makiddaw a kategoria]].",
        "categoriesfrom": "Ipakita dagiti kategoria a mangrugi iti:",
        "special-categories-sort-count": "ilasin babaen ti bilang",
        "activeusers-hidebots": "Ilemmeng dagiti bot",
        "activeusers-hidesysops": "Ilemmeng dagiti administrador",
        "activeusers-noresult": "Awan ti nasarakan nga agar-aramat.",
+       "activeusers-submit": "Ipakita dagiti aktibo nga agar-aramat",
        "listgrouprights": "Dagiti karbengan ti grupo ti agar-aramat",
        "listgrouprights-summary": "Dagiti sumaganad a listaan ti grupo ti agar-aramat a naipalawag iti daytoy a wiki, a nairaman dagiti mainaig a karbengan ti panagserrekda.\nAdda pay mabalin nga [[{{MediaWiki:Listgrouprights-helppage}}|adu a pakaammo]] a maipanggep kadagiti kabukbuodan a karbengan.",
        "listgrouprights-key": "Leyenda: \n* <span class=\"listgrouprights-granted\">Naited a karbengan</span> \n* <span class=\"listgrouprights-revoked\">Naukas a karbengan</span>",
        "wlshowlast": "Ipakita dagiti naudi a $1 nga or-oras $2 nga al-aldaw",
        "watchlistall2": "amin",
        "watchlist-hide": "Ilemmeng",
-       "wlshowtime": "Ipakita ti naudi:",
+       "watchlist-submit": "Ipakita",
+       "wlshowtime": "Ipakita a paset ti panawen:",
        "wlshowhideminor": "dagiti bassit a panagurnos",
        "wlshowhidebots": "dagiti bot",
        "wlshowhideliu": "dagiti nakarehistro nga agar-aramat",
        "wlshowhideanons": "dagiti di ammo nga agar-aramat",
        "wlshowhidepatr": "dagiti napatrulian a panagurnos",
        "wlshowhidemine": "dagiti inurnosko",
+       "wlshowhidecategorization": "pannakaikategoria ti panid",
        "watchlist-options": "Dagiti pagpilian ti listaan a bambantayan",
        "watching": "Bambantayan...",
        "unwatching": "Saanen a bantayan...",
        "delete-confirm": "Ikkaten ti \"$1\"",
        "delete-legend": "Ikkaten",
        "historywarning": "<strong>Ballaag:</strong> Ti panid a kayatmo nga ikkaten ket adda pakasaritaanna iti $1 {{PLURAL:$1|a rebision|kadagiti rebision}}:",
+       "historyaction-submit": "Ipakita",
        "confirmdeletetext": "Mangrugrugika a mangikkat iti maysa a panid a kakuyog amin ti pakasaritaanna.\nPangngaasi a pasingkedam a naikeddeng a kayatmo nga aramiden daytoy, a maawatam ti pagbnagan ti panangikkatmo, ken aramidem daytoy a kas maiyannugot iti [[{{MediaWiki:Policy-url}}|annuroten]].",
        "actioncomplete": "Nalpasen ti aramid",
        "actionfailed": "Napaay ti aramid",
        "whatlinkshere-hidelinks": "$1 dagiti silpo",
        "whatlinkshere-hideimages": "$1 dagiti silpo ti papeles",
        "whatlinkshere-filters": "Dagiti sagat",
+       "whatlinkshere-submit": "Inkan",
        "autoblockid": "Auto a panagserra #$1",
        "block": "Seraan ti agar-aramat",
        "unblock": "Ikkaten ti serra ti agar-aramat",
        "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.",
-       "movepagetalktext": "Ti mainaig a tungtungan ti panid ket automatikonto a maiyalis a karamanna <strong>malaksid:</strong>\n*Ti addan ti awan linaon a tungtungan ti panid babaen ti baro a nagan, wenno\n*No ikkatem ti kur-itna ti kahon iti baba.\n\nKadagitoy a kaso, masapul nga iyalis wenno manual nga itiponmo ti panid no kayatmo.",
+       "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.",
        "movenologintext": "Masapul a nakarehistroka nga agar-aramat ken [[Special:UserLogin|nakastrek]] tapno makaiyalis iti panid.",
        "export-download": "Idulin a kas papeles",
        "export-templates": "Mangiraman kadagiti plantilia",
        "export-pagelinks": "Mangiraman kadagiti nakasilpo a panid iti kauneg iti:",
+       "export-manual": "Manual nga inayon dagiti panid:",
        "allmessages": "Dagiti mensahe ti sistema",
        "allmessagesname": "Nagan",
        "allmessagesdefault": "Kasisigud a testo ti mensahe",
        "exif-compression-1": "Saan a napespes",
        "exif-copyrighted-true": "Nakarbengan ti kopia",
        "exif-copyrighted-false": "Saan a naisaad ti kasasaad ti karbengan ti kopia",
+       "exif-photometricinterpretation-1": "Nangisit ken puraw (Ti nangisit ket 0)",
        "exif-unknowndate": "Di ammo a petsa",
        "exif-orientation-1": "Kadawyan",
        "exif-orientation-2": "Horisontal a binaliktad",
        "pagelang-language": "Pagsasao",
        "pagelang-use-default": "Usaren ti kasisigud a pagsasao",
        "pagelang-select-lang": "Agpili iti pagsasao",
+       "pagelang-submit": "Ited",
        "right-pagelang": "Baliwan ti pagsasao ti panid",
        "action-pagelang": "baliwan ti pagsasao ti panid",
        "log-name-pagelang": "Listaan ti panagbaliw ti pagsasao",
        "mediastatistics-header-video": "Dagiti video",
        "mediastatistics-header-office": "Opisina",
        "mediastatistics-header-text": "Tekstual",
+       "mediastatistics-header-total": "Amin a papeles",
        "json-error-unknown": "Adda idi parikut ti JSON. Biddut: $1",
        "json-error-state-mismatch": "Imbalido wenno nadadael a JSON",
        "json-error-syntax": "Biddut ti sintaksis",
index 2ae8f63..f1b13ed 100644 (file)
        "morenotlisted": "Þessi listi er ekki tæmandi.",
        "mypage": "Síða",
        "mytalk": "Spjall",
-       "anontalk": "Spjallsíða þessa vistfangs.",
+       "anontalk": "Spjall",
        "navigation": "Flakk",
        "and": "&#32;og",
        "qbfind": "Finna",
        "view-foreign": "Skoða á $1",
        "edit": "Breyta",
        "create": "Skapa",
+       "create-local": "Bæta við staðbundinni lýsingu",
        "editthispage": "Breyta þessari síðu",
        "create-this-page": "Skapa þessari síðu",
        "delete": "Eyða",
        "viewsource": "Skoða efni",
        "viewsource-title": "Skoða efni $1",
        "actionthrottled": "Aðgerðin kafnaði",
-       "actionthrottledtext": "Til þess að verjast ruslpósti, er ekki hægt að framkvæma þessa aðgerð of oft, og þú hefur farið fram yfir þau takmörk. Gjörðu svo vel og reyndu aftur eftir nokkrar mínútur.",
+       "actionthrottledtext": "Til þess að verjast misnotkun, er ekki hægt að framkvæma þessa aðgerð of oft, og þú hefur farið fram yfir þau takmörk. Vinsamlegast reyndu aftur eftir nokkrar mínútur.",
        "protectedpagetext": "Þessari síðu hefur verið læst til að koma í veg fyrir breytingar eða aðrar aðgerðir.",
-       "viewsourcetext": "Þú getur skoðað og afritað kóða þessarar síðu:",
+       "viewsourcetext": "Þú getur skoðað og afritað kóða þessarar síðu.",
        "viewyourtext": "Þú getur skoðað og afritað kóða <strong>breytinganna þinna</strong> yfir á þessa síðu.",
        "protectedinterface": "Þessi síða útvegar textann sem birtist í viðmóti hugbúnaðarins sem keyrir þessa síðu, og er læst til að koma í veg fyrir misnotkun.\nTil þess að bæta við eða breyta þýðingum fyrir öll wiki verkefni, vinsamlegast notaðu [//translatewiki.net/ translatewiki.net], staðfæringaverkefni MediaWiki",
        "editinginterface": "<strong>Aðvörun:</strong> Þú ert að breyta síðu sem hefur að geyma texta fyrir notendaumhverfi hugbúnaðarins.\nBreytingar á þessari síðu munu hafa áhrif á notendaumhverfi annarra notenda á þessu vefsvæði.",
        "mycustomjsprotected": "Þú hefur ekki leyfi til þess að breyta þessari JavaScript-síðu.",
        "ns-specialprotected": "Kerfissíðum er ekki hægt að breyta.",
        "titleprotected": "Þessi titill hefur verið verndaður fyrir sköpun af [[User:$1|$1]].\nÁstæðan sem gefin var ''$2''.",
-       "filereadonlyerror": "Ekki var hægt að breyta skránni \"$1\" því skráin í skráarsafninu \"$2\" er engöngu hægt að lesa.\n\nMöppudýrið sem læsti skránni gaf þessa ástæðu: \"''$3''\".",
+       "filereadonlyerror": "Ekki var hægt að breyta skránni \"$1\" því skráin í skráarsafninu \"$2\" er engöngu hægt að lesa.\n\nKerfisstjórinn sem læsti skránni gaf þessa ástæðu: \"$3\".",
        "invalidtitle-knownnamespace": "Ógildur titill í nafnrými \"$2\" og með textann \"$3\"",
        "invalidtitle-unknownnamespace": "Ógildur titill með óþekkt nafnrými númer $1 og texta \"$2\"",
        "exception-nologin": "Óinnskráð(ur)",
        "createacct-reason": "Ástæða",
        "createacct-reason-ph": "Afhverju ertu að búa til annan aðgang",
        "createacct-submit": "Búa til aðganginn",
-       "createacct-another-submit": "Stofna annan aðgang",
+       "createacct-another-submit": "Stofna aðgang",
        "createacct-benefit-heading": "{{SITENAME}} er skrifuð af fólki eins og þér.",
        "createacct-benefit-body1": "{{PLURAL:$1|breyting|breytingar}}",
        "createacct-benefit-body2": "{{PLURAL:$1|síða|síður}}",
        "passwordreset-emailtext-ip": "Einhver (líklegast þú, á vistfanginu $1) hefur beðið um \nendursetningu lykilorðsins þíns fyrir {{SITENAME}} ($4). Aðgangur eftirfarandi {{PLURAL:$3|notanda er|notendum eru}} tengd þessu netfangi:\n\n$2\n\nEf þetta er það sem þú vildir, þarftu að skrá þig inn og velja nýtt lykilorð. {{PLURAL:$3|Tímabundna lykilorðið rennur|Tímabundnu lykilorðin renna}} út eftir $5 {{PLURAL:$5|dag|daga}}.\n\nEf það varst ekki þú sem fórst fram á þetta, eða ef þú manst lykilorðið þitt, og villt ekki lengur breyta því, skaltu hunsa þessi skilaboð og halda áfram að nota gamla lykilorðið.",
        "passwordreset-emailtext-user": "Notandinn $1 á {{SITENAME}} hefur beðið um endursetningu lykilorðsins þíns fyrir {{SITENAME}} ($4). Aðgangur eftirfarandi {{PLURAL:$3|notanda er|notendum eru}} tengd þessu netfangi:\n\n$2\n\nEf þetta er það sem þú vildir, þarftu að skrá þig inn og velja nýtt lykilorð. {{PLURAL:$3|Tímabundna lykilorðið rennur|Tímabundnu lykilorðin renna}} út eftir $5 {{PLURAL:$5|dag|daga}}.\n\nEf það varst ekki þú sem fórst fram á þetta, eða ef þú manst aftur lykilorðið þitt, og vilt ekki lengur breyta því, skaltu hunsa þessi skilaboð og halda áfram að nota gamla lykilorðið.",
        "passwordreset-emailelement": "Notendanafn: \n$1\n\nTímabundið lykilorð: \n$2",
-       "passwordreset-emailsent": "Töluvpóstur til að endursetja lykilorðið hefur verið sendur.",
+       "passwordreset-emailsentemail": "Ef þetta netfang er skráð fyrir aðganginum þínum þá hefur töluvpóstur verið sendur 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": "Breyting netfangs",
-       "changeemail-header": "Breyta skráðu netfangi",
+       "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-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:",
        "sig_tip": "Undirskrift þín auk tímasetningar",
        "hr_tip": "Lárétt lína (notist sparlega)",
        "summary": "Breytingarágrip:",
-       "subject": "Fyrirsögn:",
+       "subject": "Umræðuefni:",
        "minoredit": "Þetta er minniháttar breyting",
        "watchthis": "Vakta þessa síðu",
        "savearticle": "Vista síðu",
        "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.",
        "missingcommenttext": "Gerðu svo vel og skrifaðu athugasemd fyrir neðan.",
-       "missingcommentheader": "'''Áminning:''' Þú hefur ekki gefið upp umræðuefni/fyrirsögn.\nEf þú smellir á \"{{int:savearticle}}\" aftur, verður breyting þín vistuð án þess.",
+       "missingcommentheader": "<strong>Áminning:</strong> Þú hefur ekki gefið upp umræðuefni.\nEf þú smellir á \"{{int:savearticle}}\" aftur, verður breyting þín vistuð án þess.",
        "summary-preview": "Forskoða breytingarágrip:",
-       "subject-preview": "Forskoðun umræðuefnis/fyrirsagnar:",
+       "subject-preview": "Forskoðun umræðuefnis:",
        "blockedtitle": "Notandi er bannaður",
        "blockedtext": "'''Notandanafn þitt eða vistfang hefur verið bannað.'''\n\nBannið var sett af $1.\nÁstæðan er eftirfarandi: ''$2''.\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\nÞú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|notandastillingum þínum]] og að þér hafi ekki verið óheimilað það.\nNúverandi vistfang þitt er $3, og bönnunarnúmerið er #$5.\nVinsamlegast tilgreindu allt að ofanverðu í fyrirspurnum þínum.",
        "autoblockedtext": "Vistfang þitt hefur verið sjálfvirkt bannað því það var notað af öðrum notanda, sem var bannaður af $1.\nÁstæðan er eftirfarandi:\n\n:''$2''\n\n* Bannið hófst: $8\n* Banninu lýkur: $6\n* Sá sem banna átti: $7\n\nÞú getur haft samband við $1 eða annan [[{{MediaWiki:Grouppage-sysop}}|stjórnanda]] til að ræða bannið.\n\nAthugaðu að þú getur ekki notað „Senda þessum notanda tölvupóst“ aðgerðina nema gilt netfang sé skráð í [[Special:Preferences|notandastillingum þínum]] og að þér hafi ekki verið óheimilað það.\n\nNúverandi vistfang þitt er $3, og bönnunarnúmerið er #$5.\nVinsamlegast tilgreindu allt að ofanverðu í fyrirspurnum þínum.",
        "copyrightwarning": "Vinsamlegast athugaðu að öll framlög á {{SITENAME}} eru álitin leyfisbundin samkvæmt $2 (sjá $1 fyrir frekari upplýsingar).  Ef þú vilt ekki að skrif þín falli undir þetta leyfi og öllum verði frjálst að breyta og endurútgefa efnið samkvæmt því skaltu ekki leggja þau fram hér.<br />\nÞú berð ábyrgð á framlögum þínum, þau verða að vera þín skrif eða afrit texta í almannaeigu eða sambærilegs frjáls texta.\n'''AFRITIРEKKI HÖFUNDARRÉTTARVARIN VERK Á ÞESSA SÍÐU ÁN LEYFIS'''",
        "copyrightwarning2": "Vinsamlegast athugið að aðrir notendur geta breytt eða fjarlægt öll framlög til {{SITENAME}}.\nEf þú vilt ekki að textanum verði breytt skaltu ekki senda hann inn hér.<br />\nÞú lofar okkur einnig að þú hafir skrifað þetta sjálfur, að efnið sé í almannaeigu eða að það heyri undir frjálst leyfi. (sjá $1).\n'''EKKI SENDA INN HÖFUNDARRÉTTARVARIРEFNI ÁN LEYFIS RÉTTHAFA!'''",
        "longpageerror": "'''VILLA: Textinn sem þú sendir inn er $1 {{PLURAL:$1|kílóbæti}} að lengd, en hámarkið er $2 {{PLURAL:$2|kílóbæti}}. Ekki er hægt að vista textann.'''",
-       "readonlywarning": "'''AÐVÖRUN: Gagnagrunninum hefur verið læst til að unnt sé að framkvæma viðhaldsaðgerðir, svo þú getur ekki vistað breytingar þínar núna.'''\nÞú ættir að klippa og líma textann yfir í textaskjal til þess að geyma hann til seinni tíma.\n\nStjórnandinn sem læsti honum gaf þessa skýringu: $1",
+       "readonlywarning": "<strong>AÐVÖRUN: Gagnagrunninum hefur verið læst til að unnt sé að framkvæma viðhaldsaðgerðir, svo þú getur ekki vistað breytingar þínar núna.</strong>\nÞú ættir að klippa og líma textann yfir í textaskjal til þess að geyma hann til seinni tíma.\n\nKerfisstjórinn sem læsti honum gaf þessa skýringu: $1",
        "protectedpagewarning": "'''Viðvörun: Þessari síðu hefur verið læst svo aðeins notendur með möppudýraréttindi geti breytt henni.'''\nSíðasta færsla síðunnar úr verndunarskrá er sýnd til skýringar:",
        "semiprotectedpagewarning": "'''Athugið''': Þessari síðu hefur verið læst þannig að aðeins innskráðir notendur geti breytt henni.\nSíðasta færsla síðunnar úr verndunarskrá er sýnd til skýringar:",
        "cascadeprotectedwarning": "<strong>Viðvörun:</strong> Þessari síðu hefur verið læst svo aðeins möppudýr geta breytt henni, því hún er ítengd keðjuvörn eftirfarandi {{PLURAL:$1|síðu|síðna}}:",
        "template-protected": "(vernduð)",
        "template-semiprotected": "(hálfvernduð)",
        "hiddencategories": "Þessi síða er meðlimur í $1 {{PLURAL:$1|földum flokki|földum flokkum}}:",
+       "edittools": "<!-- Þessi texti verður sýndur undir breytingar og upphölunar eyðublöðum. -->",
        "nocreatetext": "{{SITENAME}} hefur takmarkað eiginleikann að gera nýjar síður.\nÞú getur farið til baka og breytt núverandi síðum, eða [[Special:UserLogin|skráð þið inn eða búið til aðgang]].",
        "nocreate-loggedin": "Þú hefur ekki leyfi til að búa til nýjar síður.",
        "sectioneditnotsupported-title": "Hlutabreyting er ekki virk",
        "mergehistory-go": "Sýna breytingar sem hægt er að sameina",
        "mergehistory-submit": "Sameina útgáfur",
        "mergehistory-empty": "Engar útgáfur sem hægt er að sameina.",
-       "mergehistory-done": "$3 {{PLURAL:$3|útgáfa|útgáfur}} af $1 sameinaðar í [[:$2]].",
+       "mergehistory-done": "$3 {{PLURAL:$3|útgáfa|útgáfur}} af $1 {{PLURAL:$3|var|voru}} sameinaðar í [[:$2]].",
        "mergehistory-fail": "Gat ekki sameinað breytingasögur. Vinsamlegast athugaðu síðuna og tímabreyturnar.",
        "mergehistory-no-source": "Upprunasíðan $1 er ekki til.",
        "mergehistory-no-destination": "Marksíðan $1 er ekki til.",
        "prefs-watchlist-token": "Tóki vaktlistans:",
        "prefs-misc": "Aðrar stillingar",
        "prefs-resetpass": "Breyta lykilorði",
-       "prefs-changeemail": "Breyta netfangi",
+       "prefs-changeemail": "Breyta eða fjarlægja netfang",
        "prefs-setemail": "Skrá netfang",
        "prefs-email": "Tölvupóststillingar",
        "prefs-rendering": "Útlit",
        "rows": "Raðir",
        "columns": "Dálkar",
        "searchresultshead": "Leit",
-       "stub-threshold": "Þröskuldur fyrir <a href=\"#\" class=\"stub\">stubbatengla</a> (bæt):",
+       "stub-threshold": "Þröskuldur fyrir stílsnið stubbatengla ($1):",
        "stub-threshold-disabled": "Óvirkt",
        "recentchangesdays": "Fjöldi daga sem nýlegar breytingar ná yfir:",
        "recentchangesdays-max": "(hámark $1 {{PLURAL:$1|dag|daga}})",
        "prefs-tokenwatchlist": "Lykill",
        "prefs-diffs": "Breytingar",
        "prefs-help-prefershttps": "Þessi stilling tekur gildi í næsta skiptið sem þú skráir inn.",
-       "email-address-validity-valid": "Netfang virðist vera virkt.",
-       "email-address-validity-invalid": "Settu inn rétt netfang",
        "userrights": "Breyta notandaréttindum",
        "userrights-lookup-user": "Yfirlit notandahópa",
        "userrights-user-editname": "Skráðu notandanafn:",
        "right-blockemail": "Banna notanda að senda tölvupóst",
        "right-hideuser": "Banna notandanafn, og þannig fela það frá almenningi",
        "right-ipblock-exempt": "Hunsa bönn vistfanga, sjálfvirk bönn og fjöldabönn",
-       "right-proxyunbannable": "Sneiða hjá sjálfvirkum proxy-bönnum",
        "right-unblockself": "Afbanna sjálfan sig",
        "right-protect": "Breyta verndunarstigi og breyta keðjuvernduðum síðum",
        "right-editprotected": "Breyta síðum vernduðum sem „{{int:protect-level-sysop}}“",
        "enhancedrc-history": "breytingaskrá",
        "recentchanges": "Nýlegar breytingar",
        "recentchanges-legend": "Stillingar nýlegra breytinga",
-       "recentchanges-summary": "Hér geturðu fylgst með nýjustu breytingunum. {{SITENAME}} inniheldur '''[[Special:NewPages|{{NUMBEROFARTICLES}}]]''' {{PLURAL:{{NUMBEROFARTICLES}}|grein|greinar}} og '''[[Special:Statistics|{{NUMBEROFEDITS}}]]''' {{PLURAL:{{NUMBEROFEDITS}}|breytingu|breytingar}}. '''[[Special:ActiveUsers|{{NUMBEROFACTIVEUSERS}}]]''' {{PLURAL:{{NUMBEROFACTIVEUSERS}}|notandi|notendur}} hafa hjálpað til í þessum mánuði.",
+       "recentchanges-summary": "Hér geturðu fylgst með nýjustu breytingunum.",
        "recentchanges-noresult": "Engar breytingar í uppgefna tímabilinu sem passa við þessa mælikvarða.",
        "recentchanges-feed-description": "Hér er hægt að fylgjast með nýlegum breytingum á {{SITENAME}}.",
        "recentchanges-label-newpage": "Þessi breyting skapaði nýja síðu",
        "recentchanges-label-plusminus": "Stærð síðunnar breyttist um svona mörg bæti",
        "recentchanges-legend-heading": "'''Fyrirsögn:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (sjá einng [[Special:NewPages|lista yfir nýjar síður]])",
+       "recentchanges-submit": "Sýna",
        "rcnotefrom": "Að neðan {{PLURAL:$5|er breyting síðan|eru breytingar síðan}} <strong>$3, $4</strong> (allt að <strong>$1</strong> sýndar).",
        "rclistfrom": "Sýna breytingar frá og með $3 $2",
        "rcshowhideminor": "$1 minniháttar breytingar",
        "badfilename": "Skáarnafninu hefur verið breytt í „$1“.",
        "filetype-mime-mismatch": "Skráarendingin \".$1\" samræmist ekki MIME-gerð skrárinnar ($2).",
        "filetype-badmime": "Skrárir af MIME-gerðinni „$1“ er ekki leyfilegt að hlaða inn.",
-       "filetype-bad-ie-mime": "Mistókst að hlaða inn skrá því Internet Explorer myndi uppgvötva hana sem \"$1\" sem er óheimil og mögulega hættulegt skráarsnið.",
+       "filetype-bad-ie-mime": "Mistókst að hlaða inn skrá því Internet Explorer myndi uppgötva hana sem \"$1\" sem er óheimil og mögulega hættulegt skráarsnið.",
        "filetype-unwanted-type": "'''„.$1“''' er óæskileg skráargerð.\n{{PLURAL:$3|Ákjósanleg skráargerð er|Ákjósanlegar skráargerðir eru}} $2.",
        "filetype-banned-type": "'''„.$1“''' {{PLURAL:$4|er ekki leifileg skráargerð|eru ekki leifilegar skráargerðir}}.\n{{PLURAL:$3|Leyfileg skráargerð er|Leyfilegar skráargerðir eru}} $2.",
        "filetype-missing": "Skráin hefur engan viðauka (dæmi \".jpg\").",
        "usercreated": "{{GENDER:$3|Stofnað|}} $1 $2",
        "newpages": "Nýjustu greinar",
        "newpages-username": "Notandanafn:",
-       "ancientpages": "Elstu síður",
+       "ancientpages": "Síst uppfærðar síður",
        "move": "Færa",
        "movethispage": "Færa þessa síðu",
        "unusedimagestext": "Eftirfarandi skrár eru til, en eru ekki notaðar í greinum.\nVinsamlegast athugið að aðrar vefsíður gætu tengt beint í skrár héðan, svo að þær gætu komið fram á þessum lista þrátt fyrir að vera í notkun.",
        "booksources-text": "Fyrir neðan er listi af tenglum í aðrar síður sem selja nýjar og notaðar bækur og gætu einnig haft nánari upplýsingar í sambandi við bókina sem þú varst að leita að:",
        "booksources-invalid-isbn": "ISBN gildið virðist ekki vera gilt; leitaðu eftir villum við innslátt eða afritun gildisins frá upsprettu þess.",
        "specialloguserlabel": "Gerandi:",
-       "speciallogtitlelabel": "Beinist að (titill eða notandi):",
+       "speciallogtitlelabel": "Beinist að (titill eða {{ns:user}}:notendanafn fyrir notanda):",
        "log": "Aðgerðaskrár",
        "all-logs-page": "Allar aðgerðir",
        "alllogstext": "Safn allra aðgerðaskráa {{SITENAME}}.\nÞú getur takmarkað listann með því að velja tegund aðgerðaskráar, notandanafn, eða síðu.",
        "activeusers-noresult": "Enginn notandi fannst.",
        "listgrouprights": "Notandahópréttindi",
        "listgrouprights-summary": "Hér er listi yfir notendahópa á þessum wiki, með þeirra réttindum. \nÞað gæti verið til síða með [[{{MediaWiki:Listgrouprights-helppage}}|frekari upplýsingar]] um einstök réttindi.",
-       "listgrouprights-key": "* <span class=\"listgrouprights-granted\">Veitt réttindi</span>\n* <span class=\"listgrouprights-revoked\">Afturkölluð réttindi</span>",
+       "listgrouprights-key": "Skýringar:\n* <span class=\"listgrouprights-granted\">Veitt réttindi</span>\n* <span class=\"listgrouprights-revoked\">Afturkölluð réttindi</span>",
        "listgrouprights-group": "Hópur",
        "listgrouprights-rights": "Réttindi",
        "listgrouprights-helppage": "Help:Hópréttindi",
        "emailccsubject": "Afrit af skilaboðinu þínu til $1: $2",
        "emailsent": "Sending tókst",
        "emailsenttext": "Skilaboðin þín hafa verið send.",
-       "emailuserfooter": "Þessi tölvupóstur var sendur af $1 til $2 með möguleikanum \"{{int:emailuser}}\" á {{SITENAME}}.",
+       "emailuserfooter": "Þessi tölvupóstur var {{GENDER:$1|sendur}} af $1 til {{GENDER:$2|$2}} með möguleikanum \"{{int:emailuser}}\" á {{SITENAME}}.",
        "usermessage-summary": "Skil eftir meldingu.",
        "usermessage-editor": "Meldinga sendiboði",
        "watchlist": "Vaktlistinn",
        "deletepage": "Eyða",
        "confirm": "Staðfesta",
        "excontent": "innihaldið var: „$1“",
-       "excontentauthor": "innihaldið var: '$1' (og öll framlög voru frá '[[Special:Contributions/$2|$2]]')",
+       "excontentauthor": "innihaldið var: „$1“ og öll framlög voru frá „[[Special:Contributions/$2|$2]]“ ([[User talk:$2|talk]])",
        "exbeforeblank": "innihald fyrir tæmingu var: '$1'",
        "delete-confirm": "Eyða „$1“",
        "delete-legend": "Eyða",
        "undeletepagetext": "Eftirfarandi $1 {{PLURAL:$1|síðu hefur verið eytt en hún er þó enn í gagnagrunninum og getur verið endurvakin|síðum hefur verið eytt en eru þó enn í gagnagrunninum og geta verið endurvaknar}}.\nGagnagrunnurinn kann að vera tæmdur reglulega.",
        "undelete-fieldset-title": "Endurvekja breytingar",
        "undeleteextrahelp": "Til þess að endurvekja alla breytingarskrá síðunnar, skildu öll box eftir óhökuð og ýttu á '''''{{int:undeletebtn}}'''''.\nTil þess að framkvæma ákveðna endurvakningu, ýttu á þau box sem standa hliðiná þeim útgáfum sem á að endurvekja og ýttu á '''''{{int:undeletebtn}}'''''.",
-       "undeleterevisions": "$1 {{PLURAL:$1|breyting|breytingar}}",
+       "undeleterevisions": "$1 {{PLURAL:$1|breytingu|breytingum}} eytt",
        "undeletehistory": "Ef þú endurvekur síðuna verða allar útgáfur færðar í breytingarsögu.\nEf ný síða með sama nafni hefur verið stofnuð síðan henni var eytt, verða breytingar síðunnar færðar síðast í breytingarskránna.",
        "undeleterevdel": "Endurvakning síðu verður ekki framkvæmd ef það leiðir til þess að haus síðunnar eða breytingarsaga hennar verði að hluta til eydd.\nÍ slíkum málum, þarft þú að afhaka við eða affela nýjustu eyddu breytinguna.",
        "undeletehistorynoadmin": "Þessari síðu hefur verið eytt. Ástæðan sést í ágripinu fyrir neðan, ásamt upplýsingum um hvaða notendur breyttu síðunni fyrir eyðingu.\nInnihald greinarinnar er einungis aðgengilegt möppudýrum.",
        "contributions": "Framlög {{GENDER:$1|notanda}}",
        "contributions-title": "Framlög notanda $1",
        "mycontris": "Framlög",
+       "anoncontribs": "Framlög",
        "contribsub2": "Eftir {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Engar breytingar fundnar sem passa við þessa viðmiðun.",
        "uctop": "(núverandi)",
        "move-page-legend": "Færa síðu",
        "movepagetext": "Hér er hægt að endurnefna síðu. Hún færist, ásamt breytingaskránni, yfir á nýtt heiti og eldra heitið myndar tilvísun á það. Þú getur sjálfkrafa uppfært tilvísanir á nýja heitið. Ef þú vilt það síður, athugaðu þá hvort nokkuð myndist [[Special:DoubleRedirects|tvöfaldar]] eða [[Special:BrokenRedirects|brotnar tilvísanir]].\nÞú berð ábyrgð á því að tenglar vísi á rétta staði.\n\nAthugaðu að síðan mun '''ekki''' færast ef þegar er síða á nafninu sem þú hyggst færa hana á, nema sú síða sé tóm eða tilvísun sem vísar á síðuna sem þú ætlar að færa. Þú getur þar með fært síðuna aftur til baka án þess að missa breytingarsöguna, en ekki fært hana yfir venjulega síðu.\n\n'''Varúð:'''\nAthugaðu að þessi aðgerð getur kallað fram viðbrögð annarra notenda og getur þýtt mjög rótækar breytingar á vinsælum síðum.",
        "movepagetext-noredirectfixer": "Með þessu eyðublaði er hægt að endurnefna síðu og færa alla breytingarskrá hennar á nýja nafnið. Gamli titillinn verður að tilvísun á nýja titilinn. \nAthugaðu hvort síðan tengist [[Special:DoubleRedirects|tvöfaldri]]- eða [[Special:BrokenRedirects|brotinni]] tilvísun.\nÞú berð ábyrgð á því að tenglarnir haldi áfram að tengjast á réttan stað.\n\nAthugaðu að síðan verður '''ekki''' færð ef síða er þegar til á nýja titlinum, nema hann sé annaðhvort tómur, tilvísun eða hafi enga breytingarskrá.\nÞetta merkir að þú getur fært síðu aftur til baka á þann stað sem hún var færð frá ef þú gerir mistök og þú getur ekki skrifað yfir síðu sem er þegar til.\n\n'''Varúð:'''\nEf síðan er vinsæl þá getur þessi aðgerð kallað fram viðbrögð annara notenda og getur þýtt mjög rótækar breytingar á öðrum síðum. Vertu viss um að þú skiljir hættuna áður en þú heldur áfram.",
-       "movepagetalktext": "Spallsíða síðunnar verður sjálfkrafa færð með ef hún er til nema:\n* Þú sért að færa síðuna á milli nafnrýma\n* Spallsíða sé þegar til undir nýja nafninu\n* Þú veljir að færa hana ekki\nÍ þeim tilfellum verður að færa hana handvirkt.",
+       "movepagetalktext": "Ef þú hakar við þennan reit mun viðeigandi spjallsíða vera færð sjálfkrafa á nýja titilinn, nema að spjallsíða sem er ekki tóm sé þegar til staðar.\n\nÍ því tilfelli þarft þú að færa eða sameina síðuna handvirkt ef þess er óskað.",
        "moveuserpage-warning": "'''Viðvörun:''' Þú ert í þann mund að færa notendasíðu. Athugaðu aðeins síðan verður færð og notendanafni hans verður '''ekki''' breytt.",
        "movenologintext": "Þú verður að vera [[Special:UserLogin|innskráð(ur)]] til að geta fært síður.",
        "movenotallowed": "Þú hefur ekki leyfi til að færa síður.",
        "movenotallowedfile": "Þú hefur ekki leyfi til að færa skrár.",
        "cant-move-user-page": "Þú hefur ekki leyfi til að færa notandasíðu (fyrir utan undirsíður).",
        "cant-move-to-user-page": "Þú hefur ekki leyfi til að færa síðu á notandasíðu (að frátöldum undirsíðum notanda).",
-       "newtitle": "Á nýja titilinn:",
+       "newtitle": "Nýr titill:",
        "move-watch": "Vakta þessa síðu",
        "movepagebtn": "Færa síðu",
        "pagemovedsub": "Færsla tókst",
index 75d5fcb..32571c5 100644 (file)
@@ -93,7 +93,8 @@
                        "Bultro",
                        "Oggioniale",
                        "Wim b",
-                       "V6rg"
+                       "V6rg",
+                       "JackLantern"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "october-date": "{{PLURAL:$1|1°|$1}} ottobre",
        "november-date": "{{PLURAL:$1|1°|$1}} novembre",
        "december-date": "{{PLURAL:$1|1°|$1}} dicembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categoria|Categorie}}",
        "category_header": "Pagine nella categoria \"$1\"",
        "subcategories": "Sottocategorie",
        "virus-scanfailed": "scansione fallita (codice $1)",
        "virus-unknownscanner": "antivirus sconosciuto:",
        "logouttext": "'''Logout effettuato.'''\n\nNota che alcune pagine potrebbero continuare ad apparire come se il logout non fosse avvenuto finché non viene pulita la cache del proprio browser.",
+       "cannotlogoutnow-title": "Impossibile uscire ora",
+       "cannotlogoutnow-text": "La disconnessione non è possibile quando si sta usando $1.",
        "welcomeuser": "Benvenuto, $1!",
        "welcomecreation-msg": "L'account è stato creato correttamente.\nNon dimenticare di personalizzare le [[Special:Preferences|preferenze di {{SITENAME}}]].",
        "yourname": "Nome utente:",
        "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",
+       "cannotloginnow-text": "L'accesso non è possibile quando si sta usando $1.",
        "yourdomainname": "Specificare il dominio",
        "password-change-forbidden": "Non è possibile modificare le password su questo wiki.",
        "externaldberror": "Si è verificato un errore con il server di autenticazione esterno, oppure non si dispone delle autorizzazioni necessarie per aggiornare il proprio accesso esterno.",
        "resetpass_submit": "Imposta la password e accedi al sito",
        "changepassword-success": "La password è stata modificata correttamente!",
        "changepassword-throttled": "Sono stati effettuati troppi tentativi di accesso in breve tempo.\nAttendi $1 e riprova in seguito.",
+       "botpasswords": "Password bot",
+       "botpasswords-summary": "<em>Password bot</em> consente l'accesso ad un'utenza tramite API senza usare le credenziali di accesso principali dell'utenza. I diritti utente disponibili quando si è effettuato l'accesso con una password bot possono essere limitati.\n\nSe non conosci il motivo per cui potresti farlo, allora probabilmente non dovresti farlo. Nessuno dovrebbe mai chiederti di generare uno di questi e darli a loro.",
+       "botpasswords-disabled": "Le password bot sono disabilitate.",
+       "botpasswords-no-central-id": "Per utilizzare una password bot, è necessario accedere ad un'utenza centralizzata.",
+       "botpasswords-existing": "Password bot esistenti",
+       "botpasswords-createnew": "Crea una nuova password bot",
+       "botpasswords-editexisting": "Modifica password bot esistenti",
+       "botpasswords-label-appid": "Nome bot:",
+       "botpasswords-label-create": "Crea",
+       "botpasswords-label-update": "Aggiorna",
+       "botpasswords-label-cancel": "Annulla",
+       "botpasswords-label-delete": "Cancella",
+       "botpasswords-label-resetpassword": "Reimposta la password",
+       "botpasswords-label-grants": "Assegnazioni applicabili:",
+       "botpasswords-help-grants": "Ogni assegnazione dà accesso ai diritti utente elencati che un'utenza ha già. Vedi la [[Special:ListGrants|tabella delle assegnazioni]] per ulteriori informazioni.",
+       "botpasswords-label-restrictions": "Restrizioni d'uso:",
+       "botpasswords-label-grants-column": "Assegnazioni",
+       "botpasswords-bad-appid": "Il nome bot \"$1\" non è valido.",
+       "botpasswords-insert-failed": "Impossibile aggiungere il nome bot \"$1\". È stato già aggiunto?",
+       "botpasswords-update-failed": "Impossibile aggiornare il nome bot \"$1\". È stato cancellato?",
+       "botpasswords-created-title": "Password bot creata",
+       "botpasswords-created-body": "La password bot \"$1\" è stata creata con successo.",
+       "botpasswords-updated-title": "Password bot aggiornata",
+       "botpasswords-updated-body": "La password bot \"$1\" è stata aggiornata con successo.",
+       "botpasswords-deleted-title": "Password bot cancellata",
+       "botpasswords-deleted-body": "La password bot \"$1\" è stata cancellata.",
+       "botpasswords-newpassword": "La nuova password per accedere con <strong>$1</strong> è <strong>$2</strong>. <em>Registratelo per consultazioni future.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider non è disponibile.",
+       "botpasswords-restriction-failed": "Le restrizioni di password bot impediscono questo accesso.",
+       "botpasswords-invalid-name": "Il nome utente indicato non contiene il separatore per password bot (\"$1\").",
+       "botpasswords-not-exist": "L'utente \"$1\" non dispone di una password bot chiamata \"$2\".",
        "resetpass_forbidden": "Non è possibile modificare le password",
        "resetpass-no-info": "Devi aver effettuato l'accesso per accedere a questa pagina direttamente.",
        "resetpass-submit-loggedin": "Cambia password",
        "passwordreset-emailtext-ip": "Qualcuno (probabilmente tu, con indirizzo IP $1) ha richiesto l'invio di una nuova password per l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associato|Gli utenti associati}} a questo indirizzo email sono:\n\n$2\n\n{{PLURAL:$3|Questa password temporanea scadrà|Queste password temporanee scadranno}} dopo {{PLURAL:$5|un giorno|$5 giorni}}.\nDovresti accedere e scegliere una nuova password ora. \n\nSe non sei stato tu a fare la richiesta, o se ti sei ricordato la password originale e non vuoi più cambiarla, puoi ignorare questo messaggio e continuare ad utilizzare la tua vecchia password.",
        "passwordreset-emailtext-user": "L'utente $1 di {{SITENAME}} ha richiesto l'invio di una nuova password per l'accesso a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associato|Gli utenti associati}} a questo indirizzo email sono:\n\n$2\n\n{{PLURAL:$3|Questa password temporanea scadrà|Queste password temporanee scadranno}} dopo {{PLURAL:$5|un giorno|$5 giorni}}.\nDovresti accedere e scegliere una nuova password ora. \n\nSe non sei stato tu a fare la richiesta, o se ti sei ricordato la password originale e non vuoi più cambiarla, puoi ignorare questo messaggio e continuare al utilizzare la tua vecchia password.",
        "passwordreset-emailelement": "Nome utente: \n$1\n\nPassword temporanea: \n$2",
-       "passwordreset-emailsentemail": "Se questo è un indirizzo di posta elettronica registrato per la tua utenza, allora verrà inviata una email per reimpostare la password.",
-       "passwordreset-emailsentusername": "Se c'è un corrispondente indirizzo di posta elettronica registrato, allora verrà inviata una email per reimpostare la password.",
+       "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",
        "changeemail": "Modifica o rimuovi indirizzo email",
        "userrights": "Gestione dei permessi degli utenti",
        "userrights-lookup-user": "Gestione dei gruppi utente",
        "userrights-user-editname": "Inserire il nome utente:",
-       "editusergroup": "Modifica gruppi utente",
+       "editusergroup": "Modifica gruppi {{GENDER:$1|utente}}",
        "editinguser": "Modifica in corso dei diritti dell'{{GENDER:$1|utente}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Modifica gruppi utente",
-       "saveusergroups": "Salva gruppi utente",
+       "saveusergroups": "Salva gruppi {{GENDER:$1|utente}}",
        "userrights-groupsmember": "Appartiene {{PLURAL:$1|al gruppo|ai gruppi}}:",
        "userrights-groupsmember-auto": "Membro implicito di:",
        "userrights-groups-help": "È possibile modificare i gruppi cui è assegnato l'utente.\n* Una casella di spunta selezionata indica l'appartenenza dell'utente al gruppo\n* Una casella di spunta deselezionata indica la sua mancata appartenenza al gruppo.\n* Il simbolo * indica che non è possibile eliminare l'appartenenza al gruppo dopo averla aggiunta (o viceversa).",
        "right-createpage": "Crea pagine (escluse le pagine di discussione)",
        "right-createtalk": "Crea pagine di discussione",
        "right-createaccount": "Crea nuove utenze",
+       "right-autocreateaccount": "Accede automaticamente con un'utenza esterna",
        "right-minoredit": "Segna le modifiche come minori",
        "right-move": "Sposta le pagine",
        "right-move-subpages": "Sposta le pagine insieme alle relative sottopagine",
        "right-blockemail": "Impedisce a un utente di inviare email",
        "right-hideuser": "Blocca un nome utente, nascondendolo al pubblico",
        "right-ipblock-exempt": "Ignora i blocchi degli IP, i blocchi automatici e i blocchi di range di IP",
-       "right-proxyunbannable": "Ignora i blocchi automatici dei proxy",
        "right-unblockself": "Sblocca se stesso",
        "right-protect": "Cambia i livelli di protezione e modifica pagine protette ricorsivamente",
        "right-editprotected": "Modifica pagine protette con \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Crea ed elimina dal database i [[Special:Tags|tag]]",
        "right-applychangetags": "Applica delle [[Special:Tags|etichette]] alle proprie modifiche",
        "right-changetags": "Aggiunge e rimuove specifiche [[Special:Tags|etichette]] su singole versioni o voci di registro",
+       "grant-generic": "Pacchetto diritti \"$1\"",
+       "grant-group-page-interaction": "Interagisce con le pagine",
+       "grant-group-file-interaction": "Interagisce con i file multimediali",
+       "grant-group-watchlist-interaction": "Interagisce con i tuoi osservati speciali",
+       "grant-group-email": "Invia email",
+       "grant-group-high-volume": "Esegue azioni massive",
+       "grant-group-customization": "Personalizzazione e preferenze",
+       "grant-group-administration": "Esegue azioni amministrative",
+       "grant-group-other": "Attività varie",
+       "grant-blockusers": "Blocca e sblocca utenti",
+       "grant-createaccount": "Crea un'utenza",
+       "grant-createeditmovepage": "Crea, modifica e sposta le pagine",
+       "grant-delete": "Cancella pagine, versioni, e voci di registro",
+       "grant-editinterface": "Modifica il namespace MediaWiki e i CSS/JavaScript degli utenti",
+       "grant-editmycssjs": "Modifica i CSS/JavaScript della tua utenza",
+       "grant-editmyoptions": "Modifica le preferenze della tua utenza",
+       "grant-editmywatchlist": "Modifica i tuoi osservati speciali",
+       "grant-editpage": "Modifica pagine esistenti",
+       "grant-editprotected": "Modifica pagine protette",
+       "grant-highvolume": "Modifiche massive",
+       "grant-oversight": "Nasconde utenti e sopprime le versioni",
+       "grant-patrol": "Segna le modifiche alle pagine come verificate",
+       "grant-protect": "Protegge e sprotegge pagine",
+       "grant-rollback": "Rollback delle modifiche alle pagine",
+       "grant-sendemail": "Invia email ad altri utenti",
+       "grant-uploadeditmovefile": "Carica, sostituisce e sposta i file",
+       "grant-uploadfile": "Carica nuovi file",
+       "grant-basic": "Diritti di base",
+       "grant-viewdeleted": "Vede i file e le pagine cancellati",
+       "grant-viewmywatchlist": "Vede i tuoi osservati speciali",
        "newuserlogpage": "Nuovi utenti",
        "newuserlogpagetext": "Di seguito sono elencate le utenze di nuova creazione.",
        "rightslog": "Diritti degli utenti",
        "action-createpage": "creare pagine",
        "action-createtalk": "creare pagine di discussione",
        "action-createaccount": "effettuare questa registrazione",
+       "action-autocreateaccount": "creare automaticamente questa utenza esterna",
        "action-history": "vedere la cronologia di questa pagina",
        "action-minoredit": "segnare questa modifica come minore",
        "action-move": "spostare questa pagina",
        "rcshowhidemine": "$1 le mie modifiche",
        "rcshowhidemine-show": "Mostra",
        "rcshowhidemine-hide": "Nascondi",
-       "rcshowhidecategorization": "$1 la categorizzazione della pagina",
+       "rcshowhidecategorization": "$1 categorizzazione della pagina",
        "rcshowhidecategorization-show": "Mostra",
        "rcshowhidecategorization-hide": "Nascondi",
        "rclinks": "Mostra le $1 modifiche più recenti apportate negli ultimi $2 giorni<br />$3",
        "upload-form-label-select-file": "Seleziona file",
        "upload-form-label-infoform-title": "Dettagli",
        "upload-form-label-infoform-name": "Nome",
+       "upload-form-label-infoform-name-tooltip": "Un titolo breve e distintivo per il file, che verrà utilizzato come suo nome. Puoi usare un linguaggio semplice con spazi. Non includere l'estensione del file.",
        "upload-form-label-infoform-description": "Descrizione",
+       "upload-form-label-infoform-description-tooltip": "Descrivi sinteticamente tutto quanto sia degno di nota a proposito di quest'opera.\nPer le foto, indica le cose principali che vi sono rappresentate, l'occasione e/o il luogo in cui sono state scattate.",
        "upload-form-label-usage-title": "Utilizzo",
        "upload-form-label-usage-filename": "Nome del file",
        "foreign-structured-upload-form-label-own-work": "Questo è un mio lavoro",
        "log-title-wildcard": "Ricerca dei titoli che iniziano con",
        "showhideselectedlogentries": "Mostra/nascondi le voci di registro selezionate",
        "log-edit-tags": "Modifica le etichette delle voci di registro selezionate",
+       "checkbox-select": "Seleziona: $1",
+       "checkbox-all": "Tutto",
+       "checkbox-none": "Nessuno",
+       "checkbox-invert": "Inverti",
        "allpages": "Tutte le pagine",
        "nextpage": "Pagina successiva ($1)",
        "prevpage": "Pagina precedente ($1)",
        "listgrouprights-namespaceprotection-header": "Restrizioni per namespace",
        "listgrouprights-namespaceprotection-namespace": "Namespace",
        "listgrouprights-namespaceprotection-restrictedto": "Diritto che consente all'utente di modificare",
+       "listgrants": "Assegnazioni",
+       "listgrants-summary": "Di seguito è riportato un elenco delle concessioni, con i loro diritti utente associati. Gli utenti possono autorizzare le applicazioni a utilizzare la propria utenza, ma con autorizzazioni limitate in base alle assegnazioni che l'utente ha dato all'applicazione. Tuttavia, un'applicazione che agisce per conto di un utente non può effettivamente utilizzare i diritti di cui l'utente non dispone.\nPuoi trovare [[{{MediaWiki:Listgrouprights-helppage}}|ulteriori informazioni]] sui diritti individuali.",
+       "listgrants-grant": "Assegnazioni",
+       "listgrants-rights": "Diritti",
        "trackingcategories": "Categorie di monitoraggio",
        "trackingcategories-summary": "Questa pagina elenca le categorie di monitoraggio che vengono popolate automaticamente dal software MediaWiki. I loro nomi possono essere cambiati modificando i relativi messaggi di sistema nel namespace {{ns:8}}.",
        "trackingcategories-msg": "Categoria di monitoraggio",
        "wlshowhideanons": "utenti anonimi",
        "wlshowhidepatr": "modifiche verificate",
        "wlshowhidemine": "mie modifiche",
+       "wlshowhidecategorization": "categorizzazione della pagina",
        "watchlist-options": "Opzioni osservati speciali",
        "watching": "Aggiunta agli osservati speciali...",
        "unwatching": "Eliminazione dagli osservati speciali...",
        "unblock": "Sblocca utente",
        "blockip": "Blocca {{GENDER:$1|utente}}",
        "blockip-legend": "Blocca l'utente",
-       "blockiptext": "Usare il modulo sottostante per bloccare l'accesso in scrittura a uno specifico indirizzo IP o a un utente registrato.\nIl blocco dev'essere operato per prevenire atti di vandalismo e in stretta osservanza delle [[{{MediaWiki:Policy-url}}|regole di {{SITENAME}}]].\nIndicare il motivo specifico per il quale si procede al blocco (per esempio, citando i titoli di eventuali pagine oggetto di vandalismo).",
+       "blockiptext": "Usa il modulo sottostante per bloccare l'accesso in scrittura a uno specifico indirizzo IP o a un utente registrato.\nIl blocco dev'essere operato per prevenire atti di vandalismo e in stretta osservanza delle [[{{MediaWiki:Policy-url}}|regole di {{SITENAME}}]].\nIndica il motivo specifico per il quale si procede al blocco (per esempio, citando i titoli di eventuali pagine oggetto di vandalismo).\nPuoi bloccare intervalli di IP utilizzando la sintassi [https://it.wikipedia.org/wiki/CIDR CIDR]; l'intervallo più ampio consentito è /$1 per IPv4 e /$2 per IPv6.",
        "ipaddressorusername": "Indirizzo IP o nome utente:",
        "ipbexpiry": "Scadenza del blocco:",
        "ipbreason": "Motivo:",
        "block-log-flags-hiddenname": "nome utente nascosto",
        "range_block_disabled": "La possibilità di bloccare intervalli di indirizzi IP non è attiva al momento.",
        "ipb_expiry_invalid": "Durata o scadenza del blocco non valida.",
+       "ipb_expiry_old": "Scadenza già trascorsa.",
        "ipb_expiry_temp": "I blocchi dei nomi utenti nascosti dovrebbero essere infiniti",
        "ipb_hide_invalid": "Impossibile sopprimere questa utenza; ha più di {{PLURAL:$1|una modifica|$1 modifiche}}.",
        "ipb_already_blocked": "L'utente \"$1\" è già bloccato",
        "lockedbyandtime": "(da $1 il $2 alle $3)",
        "move-page": "Spostamento di $1",
        "move-page-legend": "Spostamento di pagina",
-       "movepagetext": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Puoi aggiornare automaticamente i redirect che puntano al titolo originale. Puoi decidere di non farlo, ma ricordati di verificare che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina '''non''' sarà spostata se ne esiste già una con il nuovo nome, a meno che quest'ultima non sia costituita solo da un redirect alla vecchia e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n'''ATTENZIONE:'''\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
-       "movepagetext-noredirectfixer": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Controlla che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina '''non''' sarà spostata se ne esiste già una con il nuovo nome, a meno che non sia vuota o costituita solo da un redirect alla vecchia e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n'''ATTENZIONE:'''\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
+       "movepagetext": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Puoi aggiornare automaticamente i redirect che puntano al titolo originale. Puoi decidere di non farlo, ma ricordati di verificare che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina <strong>non</strong> sarà spostata se ne esiste già una con il nuovo nome, a meno che quest'ultima non sia costituita solo da un redirect e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n<strong>Nota:</strong>\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
+       "movepagetext-noredirectfixer": "Questo modulo consente di rinominare una pagina, spostando tutta la sua cronologia al nuovo nome. La pagina attuale diverrà automaticamente un redirect al nuovo titolo. Controlla che lo spostamento non abbia creato [[Special:DoubleRedirects|doppi redirect]] o [[Special:BrokenRedirects|redirect errati]]. L'onere di garantire che i collegamenti alla pagina restino corretti spetta a chi la sposta.\n\nSi noti che la pagina <strong>non</strong> sarà spostata se ne esiste già una con il nuovo nome, a meno che non sia costituita solo da un redirect e sia priva di versioni precedenti. In caso di spostamento errato si può quindi tornare subito al vecchio titolo, e non è possibile sovrascrivere per errore una pagina già esistente.\n\n<strong>Nota:</strong>\nUn cambiamento così drastico può creare contrattempi e problemi, soprattutto per le pagine più visitate. Accertarsi di aver valutato le conseguenze dello spostamento prima di procedere.",
        "movepagetalktext": "Se selezioni questa casella, la corrispondente pagina di discussione sarà spostata automaticamente al nuovo titolo, a meno che esiste già una pagina di discussione non vuota.\n\nIn questi casi, dovrai spostare o unire manualmente la pagina, se lo si ritiene opportuno.",
        "moveuserpage-warning": "'''Attenzione:''' Si sta per spostare una pagina utente. Nota che verrà spostata solamente la pagina. L'utente ''non'' sarà rinominato.",
        "movecategorypage-warning": "<strong>Attenzione:</strong> si sta per spostare una categoria. Solo questa pagina verrà spostata: tutte le pagine nella vecchia categoria <em>non</em> saranno inserite nella nuova.",
        "movenosubpage": "Questa pagina non ha sottopagine.",
        "movereason": "Motivo:",
        "revertmove": "ripristina",
-       "delete_and_move_text": "==Cancellazione richiesta==\n\nLa pagina specificata come destinazione \"[[:$1]]\" esiste già. Vuoi cancellarla per proseguire con lo spostamento?",
+       "delete_and_move_text": "La pagina specificata come destinazione \"[[:$1]]\" esiste già. Vuoi cancellarla per proseguire con lo spostamento?",
        "delete_and_move_confirm": "Sì, sovrascrivi la pagina esistente",
        "delete_and_move_reason": "Cancellata per rendere possibile lo spostamento da \"[[$1]]\"",
        "selfmove": "Il titolo di destinazione è uguale a quello di provenienza, non è possibile spostare una pagina su sé stessa.",
        "move-leave-redirect": "Crea un redirect con lo spostamento",
        "protectedpagemovewarning": "'''Attenzione: Questa pagina è stata bloccata in modo che solo gli utenti con privilegi di amministratore possano spostarla.'''\nL'ultimo elemento del registro è riportato di seguito per informazione:",
        "semiprotectedpagemovewarning": "'''Nota:''' Questa pagina è stata bloccata in modo che solo gli utenti registrati possano spostarla.\nL'ultimo elemento del registro è riportato di seguito per informazione:",
-       "move-over-sharedrepo": "== File già esistente ==\n[[:$1]] è già presente in una struttura condivisa. Spostare un file a questo titolo comporterà la sovrascrittura del file condiviso.",
+       "move-over-sharedrepo": "[[:$1]] esiste già in un archivio condiviso. Lo spostamento di un file a questo titolo comporterà la sovrascrittura del file condiviso.",
        "file-exists-sharedrepo": "Il nome che hai scelto per il file è già utilizzato.\nPer favore, scegli un nome diverso.",
        "export": "Esporta pagine",
        "exporttext": "È possibile esportare il testo e la cronologia delle modifiche di una pagina o di un gruppo di pagine in formato XML per importarle in altri siti che utilizzano il software MediaWiki, attraverso la [[Special:Import|pagina delle importazioni]].\n\nPer esportare le pagine indicare i titoli nella casella di testo sottostante, uno per riga, e specificare se si desidera ottenere l'ultima versione e tutte le versioni precedenti, con i dati della cronologia della pagina, oppure soltanto l'ultima versione e i dati corrispondenti all'ultima modifica.\n\nIn quest'ultimo caso si può anche utilizzare un collegamento, ad esempio [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] per esportare \"[[{{MediaWiki:Mainpage}}]]\".",
        "export-download": "Richiedi il salvataggio come file",
        "export-templates": "Includi i template",
        "export-pagelinks": "Includi pagine correlate ad una profondità di:",
+       "export-manual": "Aggiungi pagine manualmente:",
        "allmessages": "Messaggi di sistema",
        "allmessagesname": "Nome",
        "allmessagesdefault": "Testo predefinito",
        "javascripttest-pagetext-frameworks": "Per cortesia, scegli uno dei seguenti framework per i test: $1",
        "javascripttest-pagetext-skins": "Scegli un tema con cui eseguire i test:",
        "javascripttest-qunit-intro": "Vedi su mediawiki.org la [$1 documentazione riguardante i test].",
-       "tooltip-pt-userpage": "La tua pagina utente",
+       "tooltip-pt-userpage": "La {{GENDER:|tua}} pagina utente",
        "tooltip-pt-anonuserpage": "La pagina utente di questo indirizzo IP",
-       "tooltip-pt-mytalk": "La tua pagina di discussione",
+       "tooltip-pt-mytalk": "La {{GENDER:|tua}} pagina di discussione",
        "tooltip-pt-anontalk": "Discussioni sulle modifiche fatte da questo indirizzo IP",
-       "tooltip-pt-preferences": "Le tue preferenze",
+       "tooltip-pt-preferences": "Le {{GENDER:|tue}} preferenze",
        "tooltip-pt-watchlist": "La lista delle pagine che stai tenendo sotto osservazione",
-       "tooltip-pt-mycontris": "La lista dei tuoi contributi",
+       "tooltip-pt-mycontris": "La lista dei {{GENDER:|tuoi}} contributi",
        "tooltip-pt-anoncontribs": "Un elenco delle modifiche fatte da questo indirizzo IP",
        "tooltip-pt-login": "Si consiglia di effettuare l'accesso, anche se non è obbligatorio",
        "tooltip-pt-logout": "Uscita (logout)",
        "tooltip-t-recentchangeslinked": "Elenco delle ultime modifiche alle pagine collegate a questa",
        "tooltip-feed-rss": "Feed RSS per questa pagina",
        "tooltip-feed-atom": "Feed Atom per questa pagina",
-       "tooltip-t-contributions": "Elenco dei contributi di questo utente",
-       "tooltip-t-emailuser": "Invia un messaggio email a questo utente",
+       "tooltip-t-contributions": "Elenco dei contributi di {{GENDER:$1|questo|questa}} utente",
+       "tooltip-t-emailuser": "Invia un messaggio email a {{GENDER:$1|questo|questa}} utente",
        "tooltip-t-info": "Ulteriori informazioni su questa pagina",
        "tooltip-t-upload": "Carica file multimediali",
        "tooltip-t-specialpages": "Elenco di tutte le pagine speciali",
        "pageinfo-category-files": "Numero di file",
        "markaspatrolleddiff": "Segna come verificata",
        "markaspatrolledtext": "Segna questa pagina come verificata",
+       "markaspatrolledtext-file": "Segna questo file come verificato",
        "markedaspatrolled": "Modifica verificata",
        "markedaspatrolledtext": "La modifica di [[:$1]] selezionata è stata segnata come verificata.",
        "rcpatroldisabled": "La verifica delle ultime modifiche è disattivata",
        "newimages-legend": "Filtra",
        "newimages-label": "Nome file (o una parte di esso):",
        "newimages-showbots": "Mostra caricamenti di bot",
+       "newimages-hidepatrolled": "Nascondi caricamenti verificati",
        "noimages": "Non c'è nulla da vedere.",
        "ilsubmit": "Ricerca",
        "bydate": "per data",
        "scarytranscludetoolong": "[Errore: URL troppo lunga]",
        "deletedwhileediting": "'''Attenzione''': questa pagina è stata cancellata dopo che hai cominciato a modificarla!",
        "confirmrecreate": "L'utente [[User:$1|$1]] ([[User talk:$1|discussioni]]) ha cancellato questa pagina dopo che hai iniziato a modificarla, per il seguente motivo: ''$2''\nPer favore, conferma che vuoi veramente ricreare questa pagina.",
-       "confirmrecreate-noreason": "L'utente [[User:$1|$1]] ([[User talk:$1|discussioni]]) ha cancellato questa pagina dopo che hai iniziato a modificarla.\nPer favore, conferma che vuoi veramente ricreare questa pagina.",
+       "confirmrecreate-noreason": "L'utente [[User:$1|$1]] ([[User talk:$1|discussioni]]) {{GENDER:$1|ha cancellato}} questa pagina dopo che hai iniziato a modificarla. Per favore, conferma che vuoi veramente ricreare questa pagina.",
        "recreate": "Ricrea",
        "confirm_purge_button": "Conferma",
        "confirm-purge-top": "Vuoi pulire la cache di questa pagina?",
        "hebrew-calendar-m10": "Tammuz",
        "hebrew-calendar-m10-gen": "Tammuz",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussioni]])",
+       "timezone-local": "Locale",
        "duplicate-defaultsort": "Attenzione: la chiave di ordinamento predefinita \"$2\" sostituisce la precedente \"$1\".",
        "duplicate-displaytitle": "<strong>Attenzione:</strong> il titolo visualizzato \"$2\" sostituisce il precedente titolo \"$1\".",
        "invalid-indicator-name": "<strong>Errore:</strong> attributo <code>name</code> degli indicatori dello stato della pagina non può essere vuoto.",
        "version-libraries-license": "Licenza",
        "version-libraries-description": "Descrizione",
        "version-libraries-authors": "Autori",
-       "redirect": "Reindirizzamento da file, utente, pagina o versione",
+       "redirect": "Reindirizzamento da file, utente, pagina, versione o ID registro",
        "redirect-legend": "Reindirizza a un file o una pagina",
-       "redirect-summary": "Questa pagina speciale reindirizza a un file (specificando il nome del file), a una pagina (specificando un ID di versione o un ID pagina) o a un utente (specificando un ID utente numerico).\nEsempi: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Questa pagina speciale reindirizza a un file (specificando il nome del file), a una pagina (specificando un ID di versione o un ID pagina), a un utente (specificando un ID utente numerico) o ad un elemento del 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": "Vai",
        "redirect-lookup": "Ricerca:",
        "redirect-value": "Valore:",
        "redirect-page": "ID della pagina",
        "redirect-revision": "Versione pagina",
        "redirect-file": "Nome del file",
+       "redirect-logid": "ID registro",
        "redirect-not-exists": "Valore non trovato",
        "fileduplicatesearch": "Ricerca dei file duplicati",
        "fileduplicatesearch-summary": "Ricerca di eventuali duplicati del file in base al valore di ''hash''.",
        "tags-deactivate": "disattiva",
        "tags-hitcount": "$1 {{PLURAL:$1|modifica|modifiche}}",
        "tags-manage-no-permission": "Non hai il permesso di gestire il cambiamento tag.",
+       "tags-manage-blocked": "Non puoi gestire le etichette alle modifiche mentre sei bloccato.",
        "tags-create-heading": "Crea un nuovo tag",
        "tags-create-explanation": "Per impostazione predefinita, i tag appena creati saranno disponibili per l'utilizzo di utenti e bot.",
        "tags-create-tag-name": "Nome del tag:",
        "tags-deactivate-not-allowed": "Non è possibile disattivare il tag \"$1\".",
        "tags-deactivate-submit": "Disattiva",
        "tags-apply-no-permission": "Non disponi dell'autorizzazione per applicare la modifica di tag insieme con le tue modifiche.",
+       "tags-apply-blocked": "Non puoi applicare le etichette alle modifiche mentre sei bloccato.",
        "tags-apply-not-allowed-one": "L'etichetta \"$1\" non può essere applicata manualmente.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|La seguente etichetta non può essere applicata|Le seguenti etichette non possono essere applicate}}  manualmente: $1",
        "tags-update-no-permission": "Non hai il permesso di aggiungere o rimuovere modifiche di tag dalle singole revisioni o voci di registro.",
+       "tags-update-blocked": "Non puoi aggiungere o rimuovere le etichette alle modifiche mentre sei bloccato.",
        "tags-update-add-not-allowed-one": "Il tag \"$1\" non può essere aggiunto manualmente.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Il seguente tag non può essere aggiunto|I seguenti tag non possono essere aggiunti}} manualmente: $1",
        "tags-update-remove-not-allowed-one": "Il tag \"$1\" non può essere rimosso.",
        "htmlform-float-invalid": "Il valore specificato non è un numero.",
        "htmlform-int-toolow": "Il valore specificato è inferiore al minimo di $1",
        "htmlform-int-toohigh": "Il valore specificato è superiore al massimo di $1",
-       "htmlform-required": "Questo valore è necessario",
+       "htmlform-required": "Questo valore è obligatorio.",
        "htmlform-submit": "Invia",
        "htmlform-reset": "Annulla modifiche",
        "htmlform-selectorother-other": "Altro",
        "htmlform-chosen-placeholder": "Seleziona un'opzione",
        "htmlform-cloner-create": "Aggiungi altro",
        "htmlform-cloner-delete": "Rimuovi",
-       "htmlform-cloner-required": "È necessario almeno un valore.",
+       "htmlform-cloner-required": "È obbligatorio almeno un valore.",
        "htmlform-title-badnamespace": "[[:$1]] non si trova nel namespace \"{{ns:$2}}\".",
        "htmlform-title-not-creatable": "\"$1\" è il titolo di una pagina non creabile",
        "htmlform-title-not-exists": "$1 non esiste.",
        "expand_templates_generate_xml": "Mostra albero sintattico XML",
        "expand_templates_generate_rawhtml": "Mostra HTML",
        "expand_templates_preview": "Anteprima",
-       "expand_templates_preview_fail_html": "<em>Dato che {{SITENAME}} ha dell'HTML grezzo attivato e c'è stata una perdita dei dati della sessione, l'anteprima è nascosta per precauzione contro gli attacchi a JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, riprova.</strong> \nSe comunque non dovesse funzionare, prova ad [[Special:UserLogout|uscire]] ed a rientrare.",
+       "expand_templates_preview_fail_html": "<em>Poiché {{SITENAME}} ha dell'HTML grezzo attivato e c'è stata una perdita dei dati della sessione, l'anteprima è nascosta come precauzione contro gli attacchi JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, riprova.</strong> \nSe comunque non dovesse funzionare, prova ad [[Special:UserLogout|uscire]] ed a rientrare.",
        "expand_templates_preview_fail_html_anon": "<em>Poiché {{SITENAME}} ha dell'HTML grezzo attivato e non hai effettuato l'accesso, l'anteprima è nascosta come precauzione contro gli attacchi JavaScript.</em>\n\n<strong>Se si tratta di un normale tentativo d'anteprima, [[Special:UserLogin|entra]] e riprova.</strong>",
+       "expand_templates_input_missing": "Devi inserire del testo come input.",
        "pagelanguage": "Seleziona lingua della pagina",
        "pagelang-name": "Pagina",
        "pagelang-language": "Lingua",
        "pagelang-use-default": "Utilizza la lingua predefinita",
        "pagelang-select-lang": "Seleziona lingua",
+       "pagelang-submit": "Invia",
        "right-pagelang": "Modifica la lingua della pagina",
        "action-pagelang": "modificare la lingua della pagina",
        "log-name-pagelang": "Modifiche lingua",
        "mediastatistics": "Statistiche relative ai file multimediali",
        "mediastatistics-summary": "Statistiche sui tipi di file caricati. Sono incluse solo la versione più recente di un file. Versioni vecchie o cancellate dei file sono escluse.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Dimensione totale dei file per questa sezione: {{PLURAL:$1|$1 byte}} ($2; $3%).",
+       "mediastatistics-allbytes": "Dimensione totale di tutti i file: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "Tipo MIME",
        "mediastatistics-table-extensions": "Possibili estensioni",
        "mediastatistics-table-count": "Numero di file",
        "mediastatistics-header-text": "Testuali",
        "mediastatistics-header-executable": "File eseguibili",
        "mediastatistics-header-archive": "Formati compressi",
+       "mediastatistics-header-total": "Tutti i file",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|virgola finale è stata rimossa|virgole finali sono state rimosse}} dal JSON",
        "json-error-unknown": "Si è verificato un problema con il JSON. Errore: $1",
        "json-error-depth": "La profondità massima dello stack è stata superata",
        "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."
+       "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",
+       "sessionprovider-nocookies": "I cookie possono essere disattivati. Assicurati di avere i cookie abilitati e ha inizia nuovamente.",
+       "randomrootpage": "Pagina radice casuale"
 }
index 24240ff..d30b41b 100644 (file)
        "tog-watchlisthidebots": "ボットによる編集をウォッチリストに表示しない",
        "tog-watchlisthideminor": "細部の編集をウォッチリストに表示しない",
        "tog-watchlisthideliu": "ログイン利用者による編集をウォッチリストに表示しない",
-       "tog-watchlistreloadautomatically": "ã\83\95ã\82£ã\83«ã\82¿ã\81\8cå¤\89æ\9b´ã\81\95ã\82\8cã\82\8bã\81\9fã\81³ã\81«ã\80\81ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\82\92è\87ªå\8b\95ç\9a\84ã\81«å\86\8d読ã\81¿è¾¼ã\81¿ã\81\97ã\81¾ã\81\99(JavaScript が必要)",
+       "tog-watchlistreloadautomatically": "ã\83\95ã\82£ã\83«ã\82¿ã\81\8cå¤\89æ\9b´ã\81\95ã\82\8cã\82\8bã\81\9fã\81³ã\81«ã\80\81ã\82¦ã\82©ã\83\83ã\83\81ã\83ªã\82¹ã\83\88ã\82\92è\87ªå\8b\95ç\9a\84ã\81«å\86\8d読ã\81¿è¾¼ã\81¿ã\81\99ã\82\8b(JavaScript が必要)",
        "tog-watchlisthideanons": "匿名利用者による編集をウォッチリストに表示しない",
        "tog-watchlisthidepatrolled": "巡回済みの編集をウォッチリストに表示しない",
        "tog-watchlisthidecategorization": "ページのカテゴリ化を隠す",
        "october-date": "10月$1日",
        "november-date": "11月$1日",
        "december-date": "12月$1日",
+       "period-am": "午前",
+       "period-pm": "午後",
        "pagecategories": "{{PLURAL:$1|カテゴリ}}",
        "category_header": "カテゴリ「$1」にあるページ",
        "subcategories": "下位カテゴリ",
        "resetpass_submit": "再設定してログイン",
        "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-bad-appid": "ボット「$1」は有効ではありません。",
+       "botpasswords-insert-failed": "ボット「$1」の追加に失敗しました。既に追加されていないか確認してください。",
+       "botpasswords-update-failed": "ボット「$1」の更新に失敗しました。削除されていないか確認してください。",
+       "botpasswords-created-title": "ボットのパスワードが作成されました",
+       "botpasswords-created-body": "ボット「$1」のパスワードが作成されました。",
+       "botpasswords-updated-title": "ボットのパスワードが更新されました",
+       "botpasswords-updated-body": "ボット「$1」のパスワードを更新しました。",
+       "botpasswords-deleted-title": "ボットのパスワードが削除されました",
+       "botpasswords-deleted-body": "ボット「$1」のパスワードを削除しました。",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider が有効ではありません。",
+       "botpasswords-invalid-name": "指定された利用者名には、ボットのパスワードに区切り (「$1」) が含まれていません。",
+       "botpasswords-not-exist": "利用者「$1」はボット「$2」のパスワードを所持していません。",
        "resetpass_forbidden": "パスワードは変更できません",
        "resetpass-no-info": "このページに直接アクセスするためにはログインしている必要があります。",
        "resetpass-submit-loggedin": "パスワードを変更",
        "passwordreset-emailtext-ip": "誰か (おそらくあなた、IP アドレス $1) が {{SITENAME}} ($4)\nでのパスワードを再設定するよう申請しました。\n以下の利用者{{PLURAL:$3|アカウント|アカウント群}}がこのメールアドレスと紐付けられています。\n\n$2\n\n{{PLURAL:$3|この仮パスワード|これらの仮パスワード}}は {{PLURAL:$5|$5 日|$5 日間}}で有効期限が切れます。\nあなたはすぐにログインして新しいパスワードを設定する必要があります。\nこれが他の誰かによる申請である場合、あるいはあなたが自分の元のパスワードを\n覚えていてそれを変更したくない場合には、このメッセージを無視して以前のパスワードを\n使用し続けることができます。",
        "passwordreset-emailtext-user": "{{SITENAME}} の利用者 $1 があなたの {{SITENAME}} ($4)\nでのパスワードを再設定するよう申請しました。\n以下の利用者{{PLURAL:$3|アカウント|アカウント群}}がこのメールアドレスと紐付けられています。\n\n$2\n\n{{PLURAL:$3|この仮パスワード|これらの仮パスワード}}は{{PLURAL:$5|$5日}}で有効期限が切れます。\nあなたは、すぐにログインして新しいパスワードを設定する必要があります。\nこの申請が他の誰かによるものの場合、あるいはあなたが自分の元のパスワードを\n覚えていて、変更したくない場合は、このメッセージを無視して\n以前のパスワードを使い続けることができます。",
        "passwordreset-emailelement": "利用者名: \n$1\n\n仮パスワード: \n$2",
-       "passwordreset-emailsentemail": "ã\82\82ã\81\97ã\80\81ã\81\93ã\82\8cã\81\8cã\81\82ã\81ªã\81\9fã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81®ã\81\9fã\82\81ã\81«ç\99»é\8c²ã\81\95ã\82\8cã\81\9fã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81§ã\81\82ã\82\8bå ´å\90\88ã\80\81ã\81\93ã\82\8cã\81\8bã\82\89パスワードリセットのメールが送信されます。",
-       "passwordreset-emailsentusername": "ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81\8cç\99»é\8c²ã\81\95ã\82\8cã\81¦ã\81\84る場合は、パスワードリセットのメールが送信されます。",
+       "passwordreset-emailsentemail": "ã\81\93ã\81®ã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81\8cã\81\82ã\81ªã\81\9fã\81®ã\82¢ã\82«ã\82¦ã\83³ã\83\88ã\81«é\96¢é\80£ä»\98ã\81\91ã\82\89ã\82\8cã\81¦ã\81\84ã\82\8bå ´å\90\88ã\81¯ã\80\81パスワードリセットのメールが送信されます。",
+       "passwordreset-emailsentusername": "ã\81\93ã\81®å\88©ç\94¨è\80\85å\90\8dã\81«é\96¢é\80£ä»\98ã\81\91ã\82\89ã\82\8cã\81\9fã\83¡ã\83¼ã\83«ã\82¢ã\83\89ã\83¬ã\82¹ã\81\8cã\81\82る場合は、パスワードリセットのメールが送信されます。",
        "passwordreset-emailsent-capture": "下記の内容の、パスワード再設定メールをお送りしました。",
        "passwordreset-emailerror-capture": "以下の内容のパスワード再設定メールを生成しましたが、{{GENDER:$2|利用者}}への送信に失敗しました: $1",
        "changeemail": "メールアドレスを変更または除去",
        "userrights": "利用者権限を管理",
        "userrights-lookup-user": "利用者グループを管理",
        "userrights-user-editname": "利用者名を入力:",
-       "editusergroup": "利用者グループを編集",
+       "editusergroup": "{{GENDER:$1|利用者}}グループを編集",
        "editinguser": "利用者<strong> [[User:$1|$1]]</strong> $2 の権限を変更",
        "userrights-editusergroup": "利用者グループを編集",
-       "saveusergroups": "利用者グループを保存",
+       "saveusergroups": "{{GENDER:$1|利用者}}グループを保存",
        "userrights-groupsmember": "所属グループ:",
        "userrights-groupsmember-auto": "自動的に付与される権限:",
        "userrights-groupsmember-type": "$1",
        "right-blockemail": "利用者のメール送信をブロック",
        "right-hideuser": "利用者名をブロックして公開記録から隠す",
        "right-ipblock-exempt": "IPブロック、自動ブロック、広域ブロックを回避",
-       "right-proxyunbannable": "プロキシの自動ブロックを回避",
        "right-unblockself": "自身に対するブロックを解除",
        "right-protect": "保護レベルを変更し、カスケード保護されたページを編集",
        "right-editprotected": "「{{int:protect-level-sysop}}」の保護を設定されたページを編集",
        "right-managechangetags": "[[Special:Tags|タグ]]のデータベースにおける作成および削除",
        "right-applychangetags": "自分の編集に[[Special:Tags|タグ]]を適用する",
        "right-changetags": "個々の版と記録項目の任意の[[Special:Tags|タグ]]の追加と削除",
+       "grant-group-email": "メールの送信",
+       "grant-group-customization": "カスタマイズと個人設定",
+       "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-editprotected": "保護されたページを編集",
+       "grant-oversight": "利用者名および版を秘匿",
+       "grant-patrol": "ページへの変更の巡回",
+       "grant-protect": "ページを保護および保護解除",
+       "grant-rollback": "ページヘの変更の巻き戻し",
+       "grant-sendemail": "他の利用者へのメールの送信",
+       "grant-uploadeditmovefile": "ファイルをアップロード/置き換え/移動",
+       "grant-uploadfile": "新しいファイルをアップロード",
+       "grant-viewdeleted": "削除されたファイルとページを閲覧",
+       "grant-viewmywatchlist": "自身のウォッチリストを閲覧",
        "newuserlogpage": "アカウント作成記録",
        "newuserlogpagetext": "以下はアカウント作成の記録です。",
        "rightslog": "利用者権限の変更記録",
        "upload-form-label-select-file": "ファイル選択",
        "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": "ファイル名",
        "foreign-structured-upload-form-label-own-work": "これはあなた自身による作業です",
        "foreign-structured-upload-form-label-not-own-work-local-default": "このファイルはその方針の下でそこにアップロードすることができれば、また、 [[Special:Upload|the upload page on {{SITENAME}}]]を使用してみてください",
        "foreign-structured-upload-form-label-own-work-message-shared": "私は、このファイルの著作権を所有していることを宣誓し、取消し不能な形で  [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] ライセンスのもとでウィキメディア・コモンズに、このファイルを解放することに同意します。そして私は、  [https://wikimediafoundation.org/wiki/Terms_of_Use Terms of Use] に同意します。",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "このファイルの著作権を所有していない場合、または別のライセンスの下でそれをリリースしたい場合には、 [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard] を使用することを検討してください。",
-       "foreign-structured-upload-form-label-not-own-work-local-shared": "ã\82\82ã\81\97ã\82µã\82¤ã\83\88ã\81\8cã\80\81ã\81\9dã\82\8cã\82\89ã\81®æ\96¹é\87\9dã\81®ä¸\8bã\81§ã\80\81ã\81\93ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\82\92許å\8f¯ã\81\99ã\82\8bå ´å\90\88ã\81¯ã\80\81You may also want to try using [[Special:Upload|{{SITENAME}}ä¸\8aã\81§ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\83\9aã\83¼ã\82¸]]ã\82\92使ç\94¨ã\81\99ã\82\8bã\81\93ã\81¨ã\82\82試ã\81\97ã\81¦ã\81\8fã\81 ã\81\95ã\81\84。",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "ã\82\82ã\81\97ã\82µã\82¤ã\83\88ã\81\8cã\80\81ã\81\9dã\82\8cã\82\89ã\81®æ\96¹é\87\9dã\81®ä¸\8bã\81«ã\81¦ã\80\81ã\81\93ã\81®ã\83\95ã\82¡ã\82¤ã\83«ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\82\92許å\8f¯ã\81\97ã\81¦ã\81\84ã\82\8bå ´å\90\88ã\81¯ã\80\81[[Special:Upload|{{SITENAME}}ä¸\8aã\81§ã\81®ã\82¢ã\83\83ã\83\97ã\83­ã\83¼ã\83\89ã\83\9aã\83¼ã\82¸]]ã\81®å\88©ç\94¨ã\82\82æ¤\9cè¨\8eã\81§ã\81\8dã\81¾ã\81\99。",
        "foreign-structured-upload-form-3-label-yes": "はい",
        "foreign-structured-upload-form-3-label-no": "いいえ",
        "backend-fail-stream": "ファイル $1 をストリームできませんでした。",
        "log-title-wildcard": "この文字列で始まるページ名を検索",
        "showhideselectedlogentries": "選択した記録項目を表示/非表示",
        "log-edit-tags": "選択した記録項目のタグを編集",
+       "checkbox-select": "選択: $1",
+       "checkbox-all": "すべて",
+       "checkbox-none": "なし",
+       "checkbox-invert": "反転",
        "allpages": "全ページ",
        "nextpage": "次のページ ($1)",
        "prevpage": "前のページ ($1)",
        "listgrouprights-namespaceprotection-header": "名前空間ごとの制限",
        "listgrouprights-namespaceprotection-namespace": "名前空間",
        "listgrouprights-namespaceprotection-restrictedto": "編集を可能にする権限",
+       "listgrants-rights": "権限",
        "trackingcategories": "追跡用カテゴリ",
        "trackingcategories-summary": "このページでは、MediaWiki ソフトウェアが自動的に追加した追跡用カテゴリを列挙します。これらの名前は、{{ns:8}} 名前空間内の対応するシステム メッセージを修正することで変更できます。",
        "trackingcategories-msg": "追跡用カテゴリ",
        "wlshowhideanons": "IP利用者",
        "wlshowhidepatr": "巡回された編集",
        "wlshowhidemine": "自分の編集",
+       "wlshowhidecategorization": "ページのカテゴリ化",
        "watchlist-options": "ウォッチリストのオプション",
        "watching": "ウォッチリストに追加中...",
        "unwatching": "ウォッチリストから除去中...",
        "lockedbyandtime": "($1 が $2 $3 から)",
        "move-page": "「$1」の移動",
        "move-page-legend": "ページの移動",
-       "movepagetext": "下のフォームを使用すると、ページ名を変更でき、そのページの履歴も変更先に移動できます。\n移動元のページは移動先への転送ページになります。\n移動元のページへの転送ページを自動的に修正できます。\n[[Special:DoubleRedirects|二重転送]]や[[Special:BrokenRedirects|迷子のリダイレクト]]を確認する必要があります。\nリンクを正しく維持するのは移動した人の責任です。\n\n移動先のページが既に存在する場合は、その移動先が転送ページであり、かつ過去の版を持たない場合以外は移動<strong>できません</strong>。\nつまり、間違えてページ名を変更した場合には元に戻せます。また移動によって既存のページを上書きしてしまうことはありません。\n\n<strong>注意!</strong>\nよく閲覧されるページや、他の多くのページからリンクされているページを移動すると予期しない結果が起こるかもしれません。\nページの移動に伴う影響をよく考えてから踏み切るようにしてください。",
-       "movepagetext-noredirectfixer": "下のフォームを使用すると、ページ名を変更でき、そのページの履歴も変更先に移動できます。\n移動元のページは移動先への転送ページになります。\n自動的な修正を選択しない場合は、[[Special:DoubleRedirects|二重転送]]や[[Special:BrokenRedirects|迷子のリダイレクト]]を確認する必要があります。\nつながるべき場所にリンクがつながるよう維持するのは移動した人の責任です。\n\n移動先が既に存在する場合は、そのページが転送ページであり、かつ過去の版を持たない場合を除いて移動<strong>できません</strong>。\nつまり、間違えてページ名を変更した場合には元に戻せます。また移動によって既存のページを上書きしてしまうことはありません。\n\n<strong>警告!</strong>\n多く閲覧されるページや多くリンクされているページを移動すると、予期しない大きな変化が起こるかもしれないことにご注意ください。\nページの移動に伴う影響をよく考えてから移動してください。",
+       "movepagetext": "下のフォームを使用すると、ページ名を変更でき、そのページの履歴も変更先に移動できます。\n移動元のページは移動先への転送ページになります。\n移動元のページへの転送ページを自動的に修正できます。\n[[Special:DoubleRedirects|二重転送]]や[[Special:BrokenRedirects|迷子のリダイレクト]]を確認する必要があります。\nリンクを正しく維持するのは移動した人の責任です。\n\n移動先のページが既に存在する場合は、その移動先が転送ページであり、かつ過去の版を持たない場合以外は移動<strong>できません</strong>。\nつまり、間違えてページ名を変更した場合には元に戻せます。また移動によって既存のページを上書きしてしまうことはありません。\n\n<strong>注意</strong>\nよく閲覧されるページや、他の多くのページからリンクされているページを移動すると予期しない結果が起こるかもしれません。\nページの移動に伴う影響をよく考えてから踏み切るようにしてください。",
+       "movepagetext-noredirectfixer": "下のフォームを使用すると、ページ名を変更でき、そのページの履歴も変更先に移動できます。\n移動元のページは移動先への転送ページになります。\n自動的な修正を選択しない場合は、[[Special:DoubleRedirects|二重転送]]や[[Special:BrokenRedirects|迷子のリダイレクト]]を確認する必要があります。\nつながるべき場所にリンクがつながるよう維持するのは移動した人の責任です。\n\n移動先が既に存在する場合は、そのページが転送ページであり、かつ過去の版を持たない場合を除いて移動<strong>できません</strong>。\nつまり、間違えてページ名を変更した場合には元に戻せます。また移動によって既存のページを上書きしてしまうことはありません。\n\n<strong>注意</strong>\n多く閲覧されるページや多くリンクされているページを移動すると、予期しない大きな変化が起こるかもしれないことにご注意ください。\nページの移動に伴う影響をよく考えてから移動してください。",
        "movepagetalktext": "ここにチェックを付けると、関連付けられたトークページも一緒に、自動的に新しいページ名に移動されます。ただし、移動先に空ではないトークページが既に存在する場合を除きます。\n\nこの場合、手動でトークページを移動または統合する必要があります。",
        "moveuserpage-warning": "<strong>警告:</strong> 利用者ページを移動しようとしています。この操作ではページのみが移動され、利用者名は<em>変更されない</em>点に注意してください。",
        "movecategorypage-warning": "<strong>警告:</strong> カテゴリのページを移動させようとしています。カテゴリのページのみが移動するため、元のカテゴリに属していたどのページも新しいカテゴリには移動 <em>しない</em> ことにご注意ください。",
        "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": "<strong>警告:</strong> このページは保護されているため、管理者権限を持つ利用者のみが移動できます。\n参考として以下に最後の記録を表示します:",
        "semiprotectedpagemovewarning": "<strong>注意:</strong> このページは保護されているため、登録利用者のみが移動できます。\n参考として以下に最後の記録を表示します:",
-       "move-over-sharedrepo": "== ファイルが存在します ==\n[[:$1]]は共有リポジトリ上に存在します。ファイルをこの名前に移動すると共有ファイルを上書きします。",
+       "move-over-sharedrepo": "[[:$1]]は共有リポジトリ上に存在します。ファイルをこの名前に移動すると共有ファイルを上書きします。",
        "file-exists-sharedrepo": "選ばれたファイル名は既に共有リポジトリ上で使用されています。\n別の名前を選んでください。",
        "export": "ページの書き出し",
        "exporttext": "ここでは単独あるいは複数のページの本文と編集履歴を、XMLの形で書き出しができます。\nこのXMLは、他のMediaWikiを使用しているウィキで[[Special:Import|取り込みページ]]を使用して取り込みができます。\n\nページを書き出すには、下の入力ボックスに一行に一つずつ書き出したいページの名前を記入してください。また、編集履歴とともにすべての過去版を含めて書き出すのか、最新版のみを書き出すのか選択してください。\n\n後者の場合ではリンクの形で使うこともできます。例えば、[[{{#Special:Export}}/{{MediaWiki:Mainpage}}]]はページ「[[{{MediaWiki:Mainpage}}]]」が対象になります。",
        "javascripttest-pagetext-frameworks": "以下のテストフレームワークから1つ選択してください: $1",
        "javascripttest-pagetext-skins": "テストを実行する外装を選択してください:",
        "javascripttest-qunit-intro": "mediawiki.org上の[$1 テストのドキュメント]を参照してください。",
-       "tooltip-pt-userpage": "自分の利用者ページ",
+       "tooltip-pt-userpage": "{{GENDER:|自分の利用者}}ページ",
        "tooltip-pt-anonuserpage": "自分が編集しているIPアドレスの利用者ページ",
        "tooltip-pt-mytalk": "自分のトークページ",
        "tooltip-pt-anontalk": "このIPアドレスからなされた編集についての議論",
        "tooltip-t-recentchangeslinked": "このページからリンクしているページの最近の更新",
        "tooltip-feed-rss": "このページのRSSフィード",
        "tooltip-feed-atom": "このページのAtomフィード",
-       "tooltip-t-contributions": "この利用者の投稿の一覧",
-       "tooltip-t-emailuser": "この利用者にメールを送信する",
+       "tooltip-t-contributions": "{{GENDER:$1|この利用者}}による投稿の一覧",
+       "tooltip-t-emailuser": "{{GENDER:$1|この利用者}}にメールを送信します",
        "tooltip-t-info": "このページについての詳細情報",
        "tooltip-t-upload": "ファイルをアップロードする",
        "tooltip-t-specialpages": "特別ページの一覧",
        "scarytranscludefailed-httpstatus": "[$1に対してテンプレートの取得に失敗しました: HTTP $2]",
        "scarytranscludetoolong": "[URLが長すぎます]",
        "deletedwhileediting": "<strong>警告:</strong> このページが、編集開始後に削除されました!",
-       "confirmrecreate": "あなたが編集を開始した後、[[User:$1|$1]] ([[User talk:$1|トーク]]) がこのページを以下の理由で削除しました:\n: <em>$2</em>\nこのままこのページを本当に再作成していいか確認してください。",
-       "confirmrecreate-noreason": "あなたが編集を開始した後、[[User:$1|$1]] ([[User talk:$1|トーク]]) がこのページを削除しました。このページを本当に再作成していいかご確認ください。",
+       "confirmrecreate": "あなたが編集を開始した後、[[User:$1|$1]] ([[User talk:$1|トーク]]) がこのページを以下の理由で{{GENDER:$1|削除しました}}:\n: <em>$2</em>\nこのままこのページを本当に再作成していいか確認してください。",
+       "confirmrecreate-noreason": "あなたが編集を開始した後、[[User:$1|$1]] ([[User talk:$1|トーク]]) がこのページを{{GENDER:$1|削除しました}}。このページを本当に再作成していいかご確認ください。",
        "recreate": "再作成する",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "このページのキャッシュを破棄しますか?",
        "hebrew-calendar-m11-gen": "アブ",
        "hebrew-calendar-m12-gen": "エルール",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|トーク]])",
+       "timezone-local": "ローカル",
        "duplicate-defaultsort": "<strong>警告:</strong> 既定のソートキー「$2」が、その前に書かれている既定のソートキー「$1」を上書きしています。",
        "duplicate-displaytitle": "<strong>警告:</strong> DISPLAYTITLE「$2」が、その前に書かれているDISPLAYTITLE「$1」を上書きしています。",
        "invalid-indicator-name": "<strong>エラー:</strong> ページ・ステータス・インディケーターの <code>name</code> 属性は空であってはいけません。",
        "version-libraries-license": "ライセンス",
        "version-libraries-description": "説明",
        "version-libraries-authors": "作者",
-       "redirect": "ファイル名、利用者ID、ページID、版IDでの転送",
+       "redirect": "ã\83\95ã\82¡ã\82¤ã\83«å\90\8dã\80\81å\88©ç\94¨è\80\85IDã\80\81ã\83\9aã\83¼ã\82¸IDã\80\81ç\89\88IDã\80\81è¨\98é\8c²IDã\81§ã\81®è»¢é\80\81",
        "redirect-legend": "ファイルまたはページヘの転送",
-       "redirect-summary": "ã\81\93ã\81®ç\89¹å\88¥ã\83\9aã\83¼ã\82¸ã\81¯ã\80\81ã\83\95ã\82¡ã\82¤ã\83« (ã\83\95ã\82¡ã\82¤ã\83«å\90\8dã\82\92æ\8c\87å®\9a\80\81ã\83\9aã\83¼ã\82¸ (ç\89\88 ID ã\81¾ã\81\9fã\81¯ã\83\9aã\83¼ã\82¸ ID ã\82\92æ\8c\87å®\9a\80\81å\88©ç\94¨è\80\85ã\83\9aã\83¼ã\82¸ (å\88©ç\94¨è\80\85 ID ã\82\92æ\95´æ\95°ã\81§æ\8c\87å®\9a) ã\81«è»¢é\80\81ã\81\95ã\82\8cã\81¾ã\81\99ã\80\82使ç\94¨ä¾\8b: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]]",
+       "redirect-summary": "ã\81\93ã\81®ç\89¹å\88¥ã\83\9aã\83¼ã\82¸ã\81¯ã\80\81ã\83\95ã\82¡ã\82¤ã\83« (ã\83\95ã\82¡ã\82¤ã\83«å\90\8dã\82\92æ\8c\87å®\9a\80\81ã\83\9aã\83¼ã\82¸ (ç\89\88 ID ã\81¾ã\81\9fã\81¯ã\83\9aã\83¼ã\82¸ ID ã\82\92æ\8c\87å®\9a\80\81å\88©ç\94¨è\80\85ã\83\9aã\83¼ã\82¸ (å\88©ç\94¨è\80\85 ID ã\82\92æ\95´æ\95°ã\81§æ\8c\87å®\9a) ã\80\81è¨\98é\8c²é \85ç\9b® (è¨\98é\8c² ID ã\82\92æ\8c\87å®\9a) ã\81«è»¢é\80\81ã\81\95ã\82\8cã\81¾ã\81\99ã\80\82使ç\94¨ä¾\8b: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], [[{{#Special:Redirect}}/logid/186]]",
        "redirect-submit": "実行",
        "redirect-lookup": "検索の種類:",
        "redirect-value": "値:",
        "redirect-page": "ページ ID",
        "redirect-revision": "ページの版 ID",
        "redirect-file": "ファイル名",
+       "redirect-logid": "記録 ID",
        "redirect-not-exists": "値が見つかりません",
        "fileduplicatesearch": "重複ファイルの検索",
        "fileduplicatesearch-summary": "重複ファイルをハッシュ値に基づいて検索します。",
        "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 バイト}} ($2; $3%)",
+       "mediastatistics-bytespertype": "このセクションの総ファイルサイズは {{PLURAL:$1|$1 バイト}} ($2、$3%) です。",
+       "mediastatistics-allbytes": "全ファイルの総ファイルサイズは {{PLURAL:$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": "JSON の末尾の{{PLURAL:$1|カンマ $1 個}}を除去しました",
        "json-error-unknown": "JSON に問題点がありました。エラー: $1",
        "json-error-depth": "スタックの深さが上限を超えました",
        "mw-widgets-dateinput-no-date": "選択されたデータ無し",
        "mw-widgets-titleinput-description-new-page": "ページは存在しません",
        "mw-widgets-titleinput-description-redirect": "$1 へのリダイレクト",
-       "api-error-blacklisted": "他の、説明的なタイトルをお選びください。"
+       "api-error-blacklisted": "他の、説明的なタイトルをお選びください。",
+       "randomrootpage": "おまかせルートページ"
 }
index 5571ae5..230697a 100644 (file)
        "content-model-css": "CSS",
        "content-json-empty-object": "ცარიელი ობიექტი",
        "content-json-empty-array": "ცარიელი ტაბლო",
+       "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>.",
        "expensive-parserfunction-warning": "ყურადღება. მოცემული გვერდი შეიცავს ძალიან ბევრ მძიმე ფუნქციას.\n\nგამოძახებათა რაოდენობა შეზღუდულია $2 დონეზე.ამ შემთხვევაში უნდა გაკეთდეს  $1 გამოძახება.",
        "right-blockemail": "ელ ფოსტის გაგზავნის აკრძალვა",
        "right-hideuser": "მომხმარებლის სახელის დაბლოკვა და მისი დამალვა საზოგადოებისგან",
        "right-ipblock-exempt": "IP ბლოკის, ავტობლოკის და დიაპაზონთა ბლოკის გასვლა",
-       "right-proxyunbannable": "პროქსის ავტობლოკის გადასვლა",
        "right-unblockself": "საკუთარი თავის განბლოკვა",
        "right-protect": "გვერდების დაცვის დონის შეცვლა და კასკადურად დაცული გვერდების რედაქტირება",
        "right-editprotected": "გვერდების რედაქტირება რომლებიც დაცულია როგორც „{{int:protect-level-sysop}}“",
        "right-managechangetags": "[[Special:Tags|tags]] შექმნა და წაშლა მონაცემთა ბაზიდან",
        "right-applychangetags": "[[Special:Tags|tags]] მიღება თქვენ ცვლილებებთან ერთად",
        "right-changetags": "თვითნებური [[Special:Tags|tags]] დამატება ან წაშლა ცალკეულ ცვლილებებსა და ჟურნალის ჩანაწერებში",
+       "grant-group-email": "ელ.ფოსტის გაგზავნა",
+       "grant-editmyoptions": "თქვენი საკუთარი კონფიგურაციის რედაქტირება",
+       "grant-editmywatchlist": "თქვენი კონტროლის სიის რედაქტირება",
        "newuserlogpage": "მომხმარებლის რეგისტრაციის ჟურნალი",
        "newuserlogpagetext": "ბოლო დროს დარეგისტრირებულ მომხმარებელთა სია",
        "rightslog": "მომხმარებლის უფლებების ჟურნალი",
        "cachedspecial-viewing-cached-ts": "თქვენ ნახულობთ ამ გვერდის ქეშირებულ ვერსიას, რომელიც შესაძლოა მნიშვნელოვნად განსხვავდებოდეს მიმდინარე ვერსისაგან.",
        "cachedspecial-refresh-now": "ბოლო ვერსიის ხილვა.",
        "categories": "კატეგორიები",
+       "categories-submit": "ჩვენება",
        "categoriespagetext": "შემდეგი {{PLURAL:$1|კატეგორია შეიცავს|კატეგორია შეიცავს}} გვერდს ან მედიას.\n[[Special:UnusedCategories|გამოუყენებელი კატეგორიები]] აქ ნაჩვენები არ არის.\nიხ. ასევე [[Special:WantedCategories|მოთხოვნილი კატეგორიები]].",
        "categoriesfrom": "აჩვენეთ კატეგორიები, რომლებიც იწყება:",
        "special-categories-sort-count": "დაალაგეთ რაოდენობის მიხედვით",
        "delete-confirm": "„$1“-ის წაშლა",
        "delete-legend": "წაშლა",
        "historywarning": "'''ყურადღება:''' გვერდს, რომლის წაშლასაც აპირებთ, აქვს დიდი ისტორია: ($1)",
+       "historyaction-submit": "ჩვენება",
        "confirmdeletetext": "თქვენ მოითხოვეთ გვერდისა (ან ფაილისა) და მონაცემთა ბაზიდან მისი ისტორიის წაშლა.\nგთხოვთ დაადასტუროთ, რომ მართლაც აპირებთ ამის გაკეთებას და გესმით თქვენი ქმედებების ფასი.\nასევე გადაამოწმეთ, თუ ასრულებთ ამას [[{{MediaWiki:Policy-url}}|წესებიდან გამომდინარე]].",
        "actioncomplete": "მოქმედება შესრულებულია",
        "actionfailed": "მოქმედება ვერ განხორციელდა",
        "mw-widgets-dateinput-placeholder-month": "წწწწ-თთ",
        "mw-widgets-titleinput-description-new-page": "გვერდი ჯერ არ არსებობს",
        "mw-widgets-titleinput-description-redirect": "გადამისამართება $1-ზე",
-       "api-error-blacklisted": "გთხოვთ, აირჩიეთ სხვა, აღწერილობითი სათაური."
+       "api-error-blacklisted": "გთხოვთ, აირჩიეთ სხვა, აღწერილობითი სათაური.",
+       "randomrootpage": "შემთხვევითი ძირეული გვერდი"
 }
index 4b97c43..0c988e8 100644 (file)
@@ -2,7 +2,8 @@
        "@metadata": {
                "authors": [
                        "Rachitrali",
-                       "아라"
+                       "아라",
+                       "Obaid Raza"
                ]
        },
        "tog-underline": "ربطو خط کشیدگی",
        "post-expand-template-argument-category": "ھش صفحات کہ ھتیرا بوغینو بیرو سانچان یعنی(ٹمپلیٹان) لو شینی۔",
        "viewpagelogs": "ھیہ صفحہو بچے نوشتہ جاتن لوڑے",
        "currentrev-asof": "حالیہ نظرثانی بمطابق $1",
-       "revisionasof": "تـجدید بـمطابق $1",
+       "revisionasof": "تجدید بمطابق $1",
        "revision-info": "$2 $1 ھموݰ نیویشیتائے",
        "previousrevision": "←پرانو تدوین",
        "nextrevision": "→پروشٹیو اعادہ",
index 75bc2c8..64bc6c5 100644 (file)
        "session_fail_preview_html": "<strong>Кешіріңіз! Сессия деректері жоғалуы салдарынан өңдемеңізді бітіре алмаймыз.</strong>\n\n<em>Сондықтан {{SITENAME}} жобасында қам HTML қосылған, JavaScript шабуылдардан қорғану үшін алдын ала қарап шығу жасырылған.</em>\n\n<strong>Егер бұл өңдеме адал ниетті әрекет болса қайта байқап көріңіз.</strong> \nЕгер бұл әлі істемесе [[Special:UserLogout|шығуды]] және қайта кіруді байқап көріңіз.",
        "token_suffix_mismatch": "<strong>Өңдемеңіз тайдырылды, себебі тұтынғышыңыз өңдеме деректер бумасындағы тыныс белгілерін бүлдіртті.\nБет мәтіні бүлінбеу үшін өңдемеңіз тайдырылады.</strong>\nБұл кей уақытта қатесі толған веб-негізінде тіркелуі жоқ прокси-серверді пайдаланған болуы мүмкін.",
        "edit_form_incomplete": "<strong>Өңдеу пішінінің кейбір бөліктері серверге жетпеді; өңдемелеріңіздің бұзылмағандығына екі рет бақылау жүргізіңіз және қайта байқап көріңіз.</strong>",
-       "editing": "$1 бетін өңдеп жатырсыз",
+       "editing": "Өңдеп жатырсыз: $1",
        "creating": "Жаңадан бастау: $1",
-       "editingsection": "$1 бетінің бөлімін өңдеп жатырсыз",
-       "editingcomment": "$1 бетін өңдеп жатырсыз (жаңа бөлім)",
+       "editingsection": "Өңдеп жатырсыз: $1 бетінің бөлімі",
+       "editingcomment": "Өңдеп жатырсыз: $1 (жаңа бөлім)",
        "editconflict": "Өңдемелер қақтығысы: $1",
        "explainconflict": "Осы бетті сіз өңдей бастағанда басқа біреу бетті өзгерткен.\nЖоғарғы мәтін аумағында қазіргі уақытта бар бет мәтінінен тұрады.\nТөменгі мәтін аумағында сіздің өзгертулеріңіз көрсетіледі.\nӨзгертуіңізді бар мәтінге біріктіруге тура келеді.\n«{{int:savearticle}}» батырмасын басқанда </strong>тек</strong> жоғарғы мәтін аумағы сақталады.",
        "yourtext": "Мәтініңіз",
        "right-blockemail": "Қатысушының хат жөнелтуін бұғаттау",
        "right-hideuser": "Баршадан жасырып, қатысушы атын бұғаттау",
        "right-ipblock-exempt": "IP бұғаттауларды, өзбұғаттауларды және ауқым бұғаттауларды орағыту",
-       "right-proxyunbannable": "Прокси серверлердің өзбұғаттауларын орағыту",
        "right-unblockself": "Өзін бұғатынан шығару",
        "right-protect": "Қорғау деңгейлерін өзгерту және баулы-қорғаулы беттерді өңдеу",
        "right-editprotected": "Қорғалған беттерді өңдеу \"{{int:protect-level-sysop}}\"",
        "foreign-structured-upload-form-label-own-work": "Бұл менің өз туындым",
        "foreign-structured-upload-form-label-infoform-categories": "Санаттар",
        "foreign-structured-upload-form-label-infoform-date": "Ай-күні",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Мен осы файлдың авторы екенінімді растаймын және бұл файлды Wikimedia Commons ортаққорына [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] лицензиясы аясында қайтымсыз жариялауға келісемін, сонымен бірге [https://wikimediafoundation.org/wiki/Terms_of_Use Қолдану шарттарына] да келісемін.",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Егер сіз осы файлдың авторы өзіңіз болмасаңыз немесе оны сіз басқа лицензия аясында жариялағыңыз келсе [https://commons.wikimedia.org/wiki/Special:UploadWizard Ортаққор Жүктеу шеберін] қолданыңыз.",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Егер сайт бұл файлды жүктеуге өзінің ережелері аясында рұқсат беретін болса, сіз сондай-ақ [[Special:Upload|{{SITENAME}} жобасындағы жүктеу бетін]] қолданып көргіңіз келетін шығар.",
        "foreign-structured-upload-form-3-label-yes": "Иә",
        "foreign-structured-upload-form-3-label-no": "Жоқ",
        "backend-fail-stream": "«$1» файлы ақпады.",
        "listusers-editsonly": "Тек қатысушы өңдемелерін көрсету",
        "listusers-creationsort": "Басталған уақытына қарай іріктеу",
        "listusers-desc": "Кемуі бойынша ретке келтіру",
-       "usereditcount": "$1 {{PLURAL:$1|өңдеме|өңдемелер}}",
+       "usereditcount": "$1 {{PLURAL:$1|өңдеме|өңдеме}}",
        "usercreated": "$1 $2-та {{GENDER:$3|басталған}}",
        "newpages": "Ең жаңа беттер",
        "newpages-submit": "Көрсету",
        "hebrew-calendar-m11-gen": "абтың",
        "hebrew-calendar-m12-gen": "айлолдың",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|талқылауы]])",
+       "timezone-local": "Жергілікті",
        "duplicate-defaultsort": "<strong>Ескерту:</strong> «$2» әдепкі сұрыптау кілтін бұрыңғы «$1» сұрыптау кілтінің үстіне жазылады.",
        "duplicate-displaytitle": "<strong>Ескерту:</strong> «$2» көрсетілетін атауы бұрынғы «$1» көрсетілетін атауының үстінен жазады.",
        "version": "Нұсқа",
        "fileduplicatesearch-result-n": "«$1» файлына тең $2 телнұсқасы бар.",
        "fileduplicatesearch-noresults": "\"$1\" атауымен файл табылмады.",
        "specialpages": "Арнайы беттер",
-       "specialpages-note-top": "Ð\90Ò£Ñ\8bз",
-       "specialpages-note": "* Қалыпты арнайы беттер. \n* <span class==\"mw-specialpagerestricted\">Шектелген арнайы беттер.</span>",
+       "specialpages-note-top": "ШаÑ\80Ñ\82Ñ\82Ñ\8b Ð±ÐµÐ»Ð³Ñ\96леÑ\80",
+       "specialpages-note": "* Қалыпты арнайы беттер. \n* <span class=\"mw-specialpagerestricted\">Шектелген арнайы беттер.</span>",
        "specialpages-group-maintenance": "Техникалық талқылау есептері",
        "specialpages-group-other": "Тағы басқа арнайы беттер",
        "specialpages-group-login": "Кіру / тіркелу",
        "logentry-newusers-byemail": "$1 $3 деген аккаунт {{GENDER:$2|тіркеді}} және құпия сөзі е-пошта арқылы жіберілді",
        "logentry-newusers-autocreate": "$1 қатысушы аккаунтын автоматты түрде {{GENDER:$2|тіркеді}}",
        "logentry-protect-move_prot": "$1 protection settings from $4 дегеннен $3 дегенге қорғалу баптауларын {{GENDER:$2|жылжытты}}",
+       "logentry-protect-unprotect": "$1 $3 бетінің қорғанысын {{GENDER:$2|алыпсады}}",
        "logentry-protect-protect": "$1 $3 бетін {{GENDER:$2|қорғады}}  $4",
+       "logentry-protect-modify-cascade": "$1 $3 бетінің қорғалу деңгейін $4 мерзіміне {{GENDER:$2|өзгертті}} [баулы]",
        "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|деңгейі көтерілген}}",
        "pagelang-language": "Тіл",
        "pagelang-use-default": "Әдепкі тілді қолдану",
        "pagelang-select-lang": "Тілді таңдау",
+       "pagelang-submit": "Жөнелту",
        "right-pagelang": "Бет тілін аудару",
        "action-pagelang": "бет тілін аудару",
        "log-name-pagelang": "Тіл журналын өзгерту",
        "mediastatistics-header-text": "Мәтіндік",
        "mediastatistics-header-executable": "Атқарушы файл",
        "mediastatistics-header-archive": "Сығылған форматтар",
+       "mediastatistics-header-total": "Барлық файлдар",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|соңына қойылған үтір|commas were}} JSON-нан аластанған.",
        "json-error-unknown": "JSON-мен мәселе болды. Қате:$1",
        "json-error-depth": "Максимум стек тереңдігінен асып кеткен",
index be67aa4..98c3c68 100644 (file)
@@ -29,7 +29,7 @@
                ]
        },
        "tog-underline": "ಕೊಂಡಿಗಳ ಕೆಳಗೆ ಗೆರೆ ತೋರಿಸಿ",
-       "tog-hideminor": "à²\9aಿà²\95à³\8dà²\95ಪà³\81à²\9fà³\8dà²\9f à²¬à²¦à²²à²¾à²µà²£ೆಗಳನ್ನು ಅಡಗಿಸಿ",
+       "tog-hideminor": "à²\87ತà³\8dತà³\80à²\9aಿನ à²¬à²¦à²²à²¾à²µà²£à³\86à²\97ಳಲà³\8dಲಿ à²\9aಿà²\95à³\8dà²\95ಪà³\81à²\9fà³\8dà²\9f à²¸à²\82ಪಾದನೆಗಳನ್ನು ಅಡಗಿಸಿ",
        "tog-hidepatrolled": "ಪಹರೆಯಲ್ಲಿ ಆದ ಸಂಪಾದನೆಗಳನ್ನು ಇತ್ತೀಚೆಗಿನ ಬದಲಾವಣೆಗಳಲ್ಲಿ ಅಡಗಿಸು",
        "tog-newpageshidepatrolled": "ಪಹರೆಯಲ್ಲಿ ಆದ ಪುಟಗಳನ್ನು ಹೊಸ ಪುಟಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ಅಡಗಿಸು",
        "tog-extendwatchlist": "ಕೇವಲ ಇತ್ತೀಚೆಗಿನ ಬದಲಾವಣೆಗಳಲ್ಲದೆ, ಸಂಬಂಧಿತ ಎಲ್ಲಾ ಬದಲಾವಣೆಗಳನ್ನು ತೋರುವಂತೆ ಪಟ್ಟಿಯನ್ನು ವಿಸ್ತರಿಸಿ",
        "morenotlisted": "ಈ ಪಟ್ಟಿ ಪೂರ ಇಲ್ಲ.",
        "mypage": "ಪುಟ",
        "mytalk": "ಚರ್ಚೆ",
-       "anontalk": "à²\88 à²\90.ಪಿ à²\97à³\86 à²®à²¾à²¤à²¨à²¾à²¡à²¿",
+       "anontalk": "à²\9aರà³\8dà²\9aà³\86",
        "navigation": "ಸಂಚರಣೆ",
        "and": "&#32;ಮತ್ತು",
        "qbfind": "ಹುಡುಕು",
        "nstab-image": "ಚಿತ್ರ",
        "nstab-mediawiki": "ಸಂದೇಶ",
        "nstab-template": "ಟೆಂಪ್ಲೇಟು",
-       "nstab-help": "ಸಹಾಯ",
+       "nstab-help": "ಸಹಾಯ ಪುಟ",
        "nstab-category": "ವರ್ಗ",
        "mainpage-nstab": "ಮುಖ್ಯ ಪುಟ",
        "nosuchaction": "ಆ ರೀತಿಯ ಕೃತ್ಯ ಯಾವುದೂ ಇಲ್ಲ",
        "missingcommenttext": "ಕೆಳಗೆ ಒಂದು ಟಿಪ್ಪಣಿ ನಮೂದಿಸಿ",
        "missingcommentheader": "'''ಗಮನಿಸಿ:''' ಈ ವ್ಯಾಖ್ಯಾನಕ್ಕೆ ವಿಷಯ ಅಥವ ತಲೆಬರಹ ನೀವು ಸೂಚಿಸಿಲ್ಲ. ನೀವು \"{{int:savearticle}}\"\nಮತ್ತೊಮೆ ಒತ್ತಿದರೆ ನಿಮ್ಮ ಸಂಪಾದನೆಯನ್ನು ಹಾಗೆಯೇ ಉಳಿಸಲಾಗುವುದು.",
        "summary-preview": "ತಾತ್ಪರ್ಯ ಮುನ್ನೋಟ:",
-       "subject-preview": "ವಿಷಯದ/ತಲೆಬರಹದ ಮುನ್ನೋಟ:",
+       "subject-preview": "ವಿಷಯದ ಮುನ್ನೋಟ:",
        "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": "$1 ಅವರಿಂದ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟ ಇನ್ನೊಬ್ಬ ಬಳಕೆದಾರ ನಿಮ್ಮ ಐಪಿ ವಿಳಾಸವನ್ನು ಉಪಯೋಗಿಸುತ್ತಿದ್ದರಿಂದ ಅ ವಿಳಾಸವನ್ನು ಯಾಂತ್ರಿಕವಾಗಿ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟಿದೆ.\nತಡೆಗೆ ನೀಡಿರುವ ಕಾರಣ:\n\n:''$2''\n\n* ತಡೆಯ ಪ್ರಾರಂಭ: $8\n* ತಡೆಯ ಅಂತ್ಯ: $6\n* ಉದ್ದೇಶಿತ ತಡೆ: $7\n\nನೀವು $1 ಅವರನ್ನು ಅಥವ ಇತರ [[{{MediaWiki:Grouppage-sysop}}|ನಿರ್ವಾಹಕರನ್ನು]] ಈ ತಡೆಯನ್ನು ಚರ್ಚಿಸಲು ಸಂಪರ್ಕಿಸಬಹುದು.\nಗಮನಿಸಿ: ನಿಮ್ಮ [[Special:Preferences|ಬಳಕೆದಾರ ಪ್ರಾಶಸ್ತ್ಯಗಳಲ್ಲಿ]] ಧೃಡೀಕೃತ ಇ-ಅಂಚೆ ವಿಳಾಸ ನೀಡಿದ್ದಲ್ಲಿ ಮತ್ತು ಅದನ್ನು ಉಪಯೋಗಿಸದಂತೆ ತಡೆಹಿಡಿಯಲ್ಪಟ್ಟಿಲ್ಲದಿದ್ದಲ್ಲಿ ಮಾತ್ರ \"ಸದಸ್ಯರಿಗೆ ಇ-ಅಂಚೆ ಕಳಿಸಿ\" ಸೌಲಭ್ಯವನ್ನು ಉಪಯೋಗಿಸಬಹುದು.\n\nನಿಮ್ಮ ಪ್ರಸ್ತಕ ಐಪಿ ವಿಳಾಸ $3, ಮತ್ತು ಈ ತಡೆಯ ಸಂಖ್ಯೆ $5.\nನೀವು ಸಂಪರ್ಕಿಸಿದಾಗ ದಯವಿಟ್ಟು ಈ ವಿವರಣೆಗಳನ್ನು ಸೇರಿಸಿ.",
        "group-user-member": "ಬಳಕೆದಾರ",
        "group-autoconfirmed-member": "ಸ್ವಧೃಡೀಕೃತ ಬಳಕೆದಾರ",
        "group-bot-member": "{{ಲಿಂಗ:$1|ಬೋಟ್}}",
-       "group-sysop-member": "{{ಲಿಂಗ:$1|ಮಾಜಿ ಆಡಳಿತಗಾರ}}",
-       "group-bureaucrat-member": "ಮೇಲ್ವಿಚಾರಕ",
+       "group-sysop-member": "{{GENDER:$1|ನಿರ್ವಾಹಕ}}",
+       "group-bureaucrat-member": "{{GENDER:$1|ಮೇಲ್ವಿಚಾರಕ}}",
        "group-suppress-member": "ನಿಗ ಇಡುವವ",
        "grouppage-user": "{{ns:project}}:ಬಳಕೆದಾರರು",
        "grouppage-autoconfirmed": "{{ns:project}}:ಸ್ವಧೃಡೀಕೃತ ಬಳಕೆದಾರರು",
        "movepagetalktext": "ಜೊತೆಗಿನ ಚರ್ಚೆ ಪುಟವೂ ಸ್ಥಳಾಂತರಿಸಲಾಗುವುದು. ಈ ಸ್ಥಳಾಂತರ '''ಆಗದಿರುವ''' ಪ್ರಸಂಗಗಳು:\n*ಸ್ಥಳಾಂತರಿಕೆಯ ಹೆಸರಿನಲ್ಲಿ ಆಗಲೇ ಖಾಲಿಯಲ್ಲದ ಒಂದು ಪುಟವು ಇದ್ದಲ್ಲಿ, ಅಥವ\n*ಕೆಳಗಿನ ಚೌಕದಲ್ಲಿರುವ tick mark ಅನ್ನು ನೀವು ತಗೆದಲ್ಲಿ.\n\nಈ ಪ್ರಸಂಗಗಳಲ್ಲಿ ನೀವು ಸ್ವತಃ ಚರ್ಚೆ ಪುಟವನ್ನು ಸ್ಥಳಾಂತರಿಸಬೇಕು ಅಥವ ಒಂದುಗೂಡಿಸಬೇಕು.",
        "movenologintext": "ಪುಟವನ್ನು ಸ್ಥಳಾಂತರಿಸಲು ನೀವು ನೋಂದಾಯಿತ ಸದಸ್ಯರಾಗಿದ್ದು [[Special:UserLogin|ಲಾಗಿನ್]] ಆಗಿರಬೇಕು.",
        "movenotallowed": "ನಿಮಗೆ {{SITENAME}} ಅಲ್ಲಿ ಪುಟಗಳನ್ನು ಸ್ಥಳಾಂತರಿಸುವ ಅನುಮತಿ ಇಲ್ಲ.",
-       "newtitle": "à²\88 à²¹à³\8aಸ à²¶à³\80ರà³\8dಷಿà²\95à³\86à²\97ೆ:",
+       "newtitle": "ಹà³\8aಸ à²¶à³\80ರà³\8dಷಿà²\95ೆ:",
        "move-watch": "ಈ ಪುಟವನ್ನು ವೀಕ್ಷಿಸು",
        "movepagebtn": "ಪುಟವನ್ನು ಸ್ಥಳಾಂತರಿಸಿ",
        "pagemovedsub": "ಸ್ಥಳಾಂತರಿಸುವಿಕೆ ಯಶಸ್ವಿಯಾಯಿತು",
index 57aa194..78ec4c8 100644 (file)
        "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-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-emailsentemail": "당신의 계정과 연결된 이메일 주소가 있다면, 비밀번호 재설정 메일이 전해질 것입니다.",
+       "passwordreset-emailsentusername": "이 사용자 이름과 연결된 이메일 주소가 있다면 비밀번호 초기화 이메일이 전송됩니다.",
        "passwordreset-emailsent-capture": "비밀번호 재설정 이메일이 발송되었으며, 아래에 나타나 있습니다.",
        "passwordreset-emailerror-capture": "비밀번호 재설정 이메일이 생성되어 아래에 보여져 있지만, {{GENDER:$2|사용자}}에게 발송하는 데에는 실패했습니다: $1",
        "changeemail": "이메일 주소를 바꾸거나 제거하기",
        "logdelete-text": "삭제된 로그 내용은 로그에 보여지겠지만, 내용의 일부는 다른 사람들이 접근할 수 없게 됩니다.",
        "revdelete-text-others": "다른 관리자는 여전히 숨겨진 내용에 접근할 수 있고 추가 제한이 설정되어 있지 않으면, 다시 되살릴 수 있습니다.",
        "revdelete-confirm": "이 작업을 수행하는 것의 결과를 알고 있으며, [[{{MediaWiki:Policy-url}}|정책]]에 맞는 행동인지 확인해주세요.",
-       "revdelete-suppress-text": "숨기기는 '''다음 경우에만''' 사용되어야 합니다:\n* 잠재적인 비방 정보\n* 부적절한 개인 정보\n*: 집 주소, 전화번호, 주민등록번호 등",
+       "revdelete-suppress-text": "숨기기는 <strong>다음 경우에만</strong> 사용되어야 합니다:\n* 잠재적인 비방 정보\n* 부적절한 개인 정보\n*: 집 주소, 전화번호, 주민등록번호 등",
        "revdelete-legend": "보이기 제한을 설정",
        "revdelete-hide-text": "판 문자열",
        "revdelete-hide-image": "파일을 숨기기",
        "revdelete-no-change": "<strong>경고:</strong> $1 $2에 해당하는 항목은 이미 요청한 보이기 설정이 설정되어 있습니다.",
        "revdelete-concurrent-change": "$1 $2에 수정된 항목을 새로 고치면서 오류 발생: 이런 현상은 당신이 문서를 편집하고 있을 때 다른 사람이 문서를 편집했기 때문에 발생합니다.\n관련 기록을 확인해 보세요.",
        "revdelete-only-restricted": "$1 $2 버전 숨기기 오류: 다른 숨기기 설정을 같이 설정하지 않고 관리자가 보지 못하도록 특정 판을 숨길 수 없습니다.",
-       "revdelete-reason-dropdown": "*일반적인 삭제 이유\n** 저작권 침해\n** 부적절한 의견과 개인 정보\n** 부적절한 이름\n** 잠재적인 비방 정보",
+       "revdelete-reason-dropdown": "*ì\9d¼ë°\98ì \81ì\9d¸ ì\82­ì \9c ì\9d´ì\9c \n** ì \80ì\9e\91ê¶\8c ì¹¨í\95´\n** ë¶\80ì \81ì \88í\95\9c ì\9d\98견과 ê°\9cì\9d¸ ì \95ë³´\n** ë¶\80ì \81ì \88í\95\9c ì\82¬ì\9a©ì\9e\90 ì\9d´ë¦\84\n** ì\9e ì\9e¬ì \81ì\9d¸ ë¹\84ë°© ì \95ë³´",
        "revdelete-otherreason": "다른 이유/부가적인 이유",
        "revdelete-reasonotherlist": "다른 이유",
        "revdelete-edit-reasonlist": "삭제 이유 편집",
        "recentchangesdays-max": "최대 $1{{PLURAL:$1|일}}",
        "recentchangescount": "기본으로 보여줄 편집 수:",
        "prefs-help-recentchangescount": "이 설정은 최근 바뀜, 문서 역사와 기록에 적용됩니다.",
-       "prefs-help-watchlist-token2": "ë\82´ ì£¼ì\8b\9c문ì\84\9c ëª©ë¡\9dì\9d\98 ì\9b¹ í\94¼ë\93\9cì\9d\98 ë¹\84ë°\80 í\82¤ì\9e\85ë\8b\88ë\8b¤.\në¹\84ë°\80 í\82¤ë¥¼ ì\95\8cê³  ì\9e\88ë\8a\94 ì\82¬ë\9e\8cì\9d\80 ë\82´ ì£¼ì\8b\9c문ì\84\9c ëª©ë¡\9dì\9d\84 ì\9d½ì\9d\85 ì\88\98 ì\9e\88ì\9c¼ë\8b\88 ë¹\84ë°\80 í\82¤ë¥¼ ì\95\8c리ì§\80 ë§\88ì\84¸ì\9a\94.\n[[Special:ResetTokens|ë¹\84ë°\80 í\82¤ë¥¼ ì\9e¬ì\84¤ì \95í\95´ì\95¼ í\95\9cë\8b¤ë©´ ì\97¬ê¸°ë¥¼ í\81´ë¦­í\95\98ì\84¸ì\9a\94]].",
+       "prefs-help-watchlist-token2": "ë\82´ ì£¼ì\8b\9c문ì\84\9c ëª©ë¡\9dì\9d\98 ì\9b¹ í\94¼ë\93\9cì\9d\98 ë¹\84ë°\80 í\82¤ì\9e\85ë\8b\88ë\8b¤.\në¹\84ë°\80 í\82¤ë¥¼ ì\95\8cê³  ì\9e\88ë\8a\94 ì\82¬ë\9e\8cì\9d\80 ë\82´ ì£¼ì\8b\9c문ì\84\9c ëª©ë¡\9dì\9d\84 ì\9d½ì\9d\84 ì\88\98 ì\9e\88ì\9c¼ë\8b\88 ë¹\84ë°\80 í\82¤ë¥¼ ì\95\8c리ì§\80 ë§\88ì\84¸ì\9a\94.\ní\95\84ì\9a\94í\95\98ë\8b¤ë©´ [[Special:ResetTokens|ë¹\84ë°\80 í\82¤ë¥¼ ì\9e¬ì\84¤ì \95í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤]].",
        "savedprefs": "설정을 저장했습니다.",
        "savedrights": "$1의 사용자 권한이 저장되었습니다.",
        "timezonelegend": "시간대:",
        "right-blockemail": "다른 사용자가 이메일을 보내지 못하도록 차단",
        "right-hideuser": "사용자 이름을 차단하고 숨김",
        "right-ipblock-exempt": "IP 차단, 자동 차단, 광역 차단을 무시",
-       "right-proxyunbannable": "프록시 자동 차단을 적용하지 않음",
        "right-unblockself": "자신을 차단 해제하기",
        "right-protect": "보호 수준 바꾸기 및 연쇄 보호된 문서 편집",
        "right-editprotected": "\"{{int:protect-level-sysop}}\"로 보호된 문서 편집",
        "right-managechangetags": "데이터베이스에서 [[Special:Tags|태그]]를 만들거나 지우기",
        "right-applychangetags": "자신이 편집할 때 [[Special:Tags|태그]]를 적용하기",
        "right-changetags": "문서의 특정 판과 특정 기록 항목에 임의의 [[Special:Tags|태그]]를 추가하거나 제거하기",
+       "grant-generic": "\"$1\" 권한 번들",
+       "grant-group-page-interaction": "문서로 상호 작용",
+       "grant-group-file-interaction": "미디어로 상호 작용",
+       "grant-group-watchlist-interaction": "당신의 주시문서로 상호작용",
+       "grant-group-email": "이메일 보내기",
+       "grant-group-high-volume": "대량의 작업을 수행",
+       "grant-group-customization": "사용자 최적화 및 환경 설정",
+       "grant-group-administration": "관리 기능 수행",
+       "grant-group-other": "기타 활동",
+       "grant-blockusers": "사용자 차단 또는 차단 해제",
+       "grant-createaccount": "계정 만들기",
+       "grant-createeditmovepage": "문서 만들기, 편집 및 이동",
+       "grant-delete": "문서, 판 및 기록 항목 삭제",
+       "grant-editinterface": "미디어위키 이름공간과 사용자 CSS/JS 편집",
+       "grant-editmycssjs": "자신의 사용자 CSS/자바스크립트 편집하기",
+       "grant-editmyoptions": "사용자 환경 설정 편집하기",
+       "grant-editmywatchlist": "내 주시문서 목록 편집하기",
+       "grant-editpage": "기존 문서 편집하기",
+       "grant-editprotected": "보호된 문서 편집하기",
+       "grant-highvolume": "대용량 편집",
+       "grant-oversight": "사용자 숨기기와 판 억제",
+       "grant-patrol": "페이지 검토",
+       "grant-protect": "문서 보호 및 보호 해제",
+       "grant-rollback": "문서의 바뀜을 되돌리기",
+       "grant-sendemail": "다른 사용자에게 이메일 보내기",
+       "grant-uploadeditmovefile": "파일 올리기, 바꾸기, 이동",
+       "grant-uploadfile": "새 파일 올리기",
+       "grant-viewdeleted": "삭제된 파일과 문서 보기",
+       "grant-viewmywatchlist": "내 주시문서 목록 보기",
        "newuserlogpage": "사용자 만들기 기록",
        "newuserlogpagetext": "사용자가 만들어진 기록입니다.",
        "rightslog": "사용자 권한 기록",
        "mostrevisions": "가장 많이 편집된 문서 목록",
        "prefixindex": "접두어에 따른 문서 목록",
        "prefixindex-namespace": "접두어가 있는 모든 문서 ($1 이름공간)",
+       "prefixindex-submit": "보이기",
        "prefixindex-strip": "목록에서 접두어 생략",
        "shortpages": "짧은 문서 목록",
        "longpages": "긴 문서 목록",
        "protectedpages-performer": "보호한 사용자",
        "protectedpages-params": "보호 변수",
        "protectedpages-reason": "이유",
+       "protectedpages-submit": "문서 보이기",
        "protectedpages-unknown-timestamp": "알 수 없음",
        "protectedpages-unknown-performer": "알 수 없는 사용자",
        "protectedtitles": "만들기 보호된 표제어 목록",
        "protectedtitles-summary": "이 페이지는 현재 만들기 보호가 설정되어 있는 문서 제목을 나열합니다. 보호된 기존 문서들의 목록을 보려면 [[{{#special:ProtectedPages}}|{{int:protectedpages}}]]을 보세요.",
        "protectedtitlesempty": "해당 조건에 맞는 만들기 금지 표제어가 없습니다.",
+       "protectedtitles-submit": "제목 보이기",
        "listusers": "사용자 목록",
        "listusers-editsonly": "기여가 있는 사용자만 보기",
        "listusers-creationsort": "계정을 만든 날짜순으로 정렬",
        "usereditcount": "{{PLURAL:$1|편집}} $1회",
        "usercreated": "$1 $2에 계정이 {{GENDER:$3|만들어짐}}",
        "newpages": "새 문서 목록",
+       "newpages-submit": "보이기",
        "newpages-username": "사용자 이름:",
        "ancientpages": "오래된 문서 목록",
        "move": "이동",
        "cachedspecial-viewing-cached-ts": "현재 이 문서는 캐시 처리된 버전으로 현재 문서 상태를 반영하지 않을 수도 있습니다.",
        "cachedspecial-refresh-now": "최신 버전 보기.",
        "categories": "분류 목록",
+       "categories-submit": "보이기",
        "categoriespagetext": "문서나 자료를 {{PLURAL:$1|포함하고 있는 분류}} 목록입니다.\n[[Special:UnusedCategories|사용되지 않는 분류]]는 여기에 보이지 않습니다.\n[[Special:WantedCategories|필요한 분류]]도 참조하세요.",
        "categoriesfrom": "다음으로 시작하는 분류를 보여주기:",
        "special-categories-sort-count": "갯수 순으로 정렬",
        "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-summary": "다음은 사용자 권한에 관련된 접근 권한을 통해 부여된 OAuth 부여 목록입니다. 사용자는 자신의 계정에 대해 권한을 부여 할 수 있지만, 사용자가 응용 프로그램에 부여한 권한 설정에 따라 제한이 있습니다. 사용자를 대신하여 동작하는 응용 프로그램은 사용자가 갖고 있지 않은 권한은 사용할 수 없습니다. \n각각의 권한에 대한 [[{{MediaWiki:Listgrouprights-helppage}}|추가 정보]]가 있습니다.",
+       "listgrants-grant": "부여",
+       "listgrants-rights": "권한",
        "trackingcategories": "추적용 분류",
        "trackingcategories-summary": "이 페이지는 미디어위키 소프트웨어에 의해 자동으로 만들어지는 추적용 분류를 나열합니다. 그들의 이름은 {{ns:8}} 이름공간에 관련된 시스템 메시지를 바꾸어서 바꿀 수 있습니다.",
        "trackingcategories-msg": "추적용 분류",
        "wlshowhideanons": "익명 사용자",
        "wlshowhidepatr": "순찰한 편집",
        "wlshowhidemine": "내 편집",
+       "wlshowhidecategorization": "문서 분류",
        "watchlist-options": "주시문서 목록 설정",
        "watching": "주시 추가 중…",
        "unwatching": "주시 해제 중…",
        "delete-confirm": "\"$1\" 삭제",
        "delete-legend": "삭제",
        "historywarning": "<strong>경고:</strong> 삭제하려고 하는 문서에 {{PLURAL:$1|판}} $1개의 역사가 있습니다:",
+       "historyaction-submit": "보이기",
        "confirmdeletetext": "문서와 문서 역사를 삭제하려고 합니다.\n삭제하려는 문서가 맞는지, 이 문서를 삭제하는 것이 [[{{MediaWiki:Policy-url}}|정책]]에 맞는 행동인지를 확인해 주세요.",
        "actioncomplete": "동작 완료",
        "actionfailed": "명령 실패",
        "whatlinkshere-hidelinks": "링크를 $1",
        "whatlinkshere-hideimages": "파일 링크를 $1",
        "whatlinkshere-filters": "필터",
+       "whatlinkshere-submit": "계속",
        "autoblockid": "자동 차단 #$1",
        "block": "사용자 차단",
        "unblock": "사용자 차단 해제",
        "movepage-page-moved": "\"$1\" 문서를 \"$2\" 문서로 이동했습니다.",
        "movepage-page-unmoved": "$1 문서를 $2 문서로 옮길 수 없습니다.",
        "movepage-max-pages": "{{PLURAL:$1|문서}}를 최대 $1개 이동했으며 나머지 문서는 자동으로 이동하지 않습니다.",
-       "movelogpage": "ì\98®ê¸°ê¸° 기록",
+       "movelogpage": "ì\9d´ë\8f\99 기록",
        "movelogpagetext": "아래는 옮겨진 모든 문서의 목록입니다.",
        "movesubpage": "{{PLURAL:$1|하위 문서}}",
        "movesubpagetext": "이 문서에는 다음 {{PLURAL:$1|하위 문서}} $1개가 있습니다.",
        "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 바이트}} ($2; $3%)",
+       "mediastatistics-bytespertype": "이 문단의 총 파일 크기:$1 바이트",
+       "mediastatistics-allbytes": "모든 파일의 총 파일 크기:$1 바이트",
        "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": "뒤 {{PLURAL:$1|쉼표}} $1개가 JSON에서 제거되었습니다",
        "json-error-unknown": "JSON에 문제가 있었습니다. 오류: $1",
        "json-error-depth": "최대 스택 깊이를 초과했습니다",
        "mw-widgets-dateinput-no-date": "선택된 날짜 없음",
        "mw-widgets-titleinput-description-new-page": "문서가 존재하지 않습니다",
        "mw-widgets-titleinput-description-redirect": "$1 문서로 넘겨주기",
-       "api-error-blacklisted": "이 파일을 설명할 수 있는 다른 제목을 선택하세요."
+       "api-error-blacklisted": "이 파일을 설명할 수 있는 다른 제목을 선택하세요.",
+       "randomrootpage": "임의 루트 페이지"
 }
index 682f51d..f7f31a6 100644 (file)
        "createaccountreason": "Чурум:",
        "createacct-reason": "Чурум",
        "createacct-reason-ph": "Башха тергеу джазыуну нек къураусыз",
-       "createacct-captcha": "Къоркъуусузлукъну тинтиу",
-       "createacct-imgcaptcha-ph": "Башыракъдагъы текстни джаз",
        "createacct-submit": "Тергеу джазыуну къура",
        "createacct-another-submit": "Энтда бир аккаунт къурау",
        "createacct-benefit-heading": "{{SITENAME}} сизнича адамла бла къуралгъанды.",
        "passwordreset-email": "Электрон почтаны адреси:",
        "passwordreset-emailtitle": "{{SITENAME}} сайтдагъы тергеу джазыуну юсюнден билгиле",
        "passwordreset-emailelement": "Къошулуучуну аты: \n$1\n\nБолджаллы пароль: \n$2",
-       "passwordreset-emailsent": "Пароль бла e-mail ийилди.",
+       "passwordreset-emailsentemail": "Пароль бла e-mail ийилди.",
        "passwordreset-emailsent-capture": "Ийилген пароль эсгертиу e-mail тюбюрекде берилибди.",
        "passwordreset-emailerror-capture": "Пароль эсгертиу e-mail генерация этилди (тюбюрекде берилибди), аны {{GENDER:$2|къошулуучугъа}} ашырыу джетишимсиз болду, чурум: $1",
        "changeemail": "Электрон почтаны адресин ауушдур",
-       "changeemail-text": "Сизни e-mail адресигизни тюрлендирир ючюн бу форманы толтуругъуз. Тюрлениуню бегитир ючюн паролну джазаргъа керек боллукъду.",
+       "changeemail-header": "Электрон почтаны адресин ауушдуруу",
        "changeemail-no-info": "Бу бетни кёрюр ючюн сиз системагъа тергеу джазыуугъуз (аккаунтугъуз) бла кирирге керексиз.",
        "changeemail-oldemail": "Почтаны бусагъатдагъы адреси:",
        "changeemail-newemail": "Джангы email адрес:",
        "prefs-displaywatchlist": "Кёрюнюуню джарашдырыулары",
        "prefs-diffs": "Версияланы башхалыкълары",
        "prefs-help-prefershttps": "Бу джарашдырыу эндиги авторизацияны ётгенден сора сингдирилликди.",
-       "email-address-validity-valid": "E-mail адрес тюзге ушайды",
-       "email-address-validity-invalid": "Тюз e-mail адрес джазыгъыз!",
        "userrights": "Къошулуучуну хакъларына оноу этиу",
        "userrights-lookup-user": "Къошулуучуланы къауумуна оноу эт",
        "userrights-user-editname": "Къошулуучуну атын джазыгъыз:",
        "right-blockemail": "Къошулуучуну электрон почтаны джибериуюн тый",
        "right-hideuser": "Къошулуучуну атын тый эмда аны джашыр",
        "right-ipblock-exempt": "IP тыйылуаны, автомат тыйыуланы эм диапозонланы тыйыуланы ётюдюр",
-       "right-proxyunbannable": "Проксилени автомат тыйыуларыны ётдюр",
        "right-unblockself": "кеслерини блокларын алыу",
        "right-protect": "Къорууну дараджасын тюрлендир эмда къорууланнган бетледе тюрлениуле эт",
        "right-editprotected": "Къорууланнган бетледе тюрлениу эт (секиртмесиз джакъсыз)",
        "right-override-export-depth": "Бетлени, теренлиги 5-ге дери байламлы бетле бла бирге экспорт эт",
        "right-sendemail": "Башха къошулуучулагъа электрон почта джиберирге",
        "right-passwordreset": "пароль тюрлениуле бла e-mail'леге къарау",
+       "grant-group-email": "E-mail джибер",
+       "grant-createaccount": "Аккаунтла къурау",
        "newuserlogpage": "Къошулуучуланы регистрацияларыны журналы",
        "newuserlogpagetext": "Кёб болмай регистрация этген къошулуучуланы тизмеси.",
        "rightslog": "Къошулуучуну хакъларыны журналы",
        "listgrouprights-removegroup-self-all": "Кесини тергеу джазыуундан бютеу къауумланы къораталлыкъды",
        "listgrouprights-namespaceprotection-header": "Ат аламны чеклеулери",
        "listgrouprights-namespaceprotection-namespace": "Атла алам",
+       "listgrants-rights": "Онгла",
        "trackingcategories": "Марлаб туруучу категорияла",
        "trackingcategories-msg": "Марлаб туруучу категория",
        "trackingcategories-name": "Билдириуню аты",
        "wlheader-showupdated": "Ахыр кириуюгюзден сора бетни тюрлениулери '''къалын''' джазыу бла кёргюзюлгенди.",
        "wlnote": "Тюбюндеди кёргюзюлгенди: ахыр '''$2''' сагъатха этилген ахыр '''$1''' тюрлениу, $3 $4 заманнга дери.",
        "wlshowlast": "Арт $1 сагъат $2 кюннге  кёргюз",
+       "watchlistall2": "бютеу",
        "watchlist-options": "Кёзде тургъан тизмени джарашдырыулары",
        "watching": "Кёзде тургъан тизмеге къошуу...",
        "unwatching": "Кёзде тургъан тизмеден кетериу...",
        "movenosubpage": "Бу бетни тюб бети джокъду.",
        "movereason": "Чурум:",
        "revertmove": "ызына къайтыу",
-       "delete_and_move": "Кетер эмда атын тюрлендир",
        "delete_and_move_text": "== Кетериу керекди ==\n\"[[:$1]]\" атлы бет алайсызда джокъду. О бетни кетериб, атны тюрлендириуню андан ары бардырыргъа излеймисиз?",
        "delete_and_move_confirm": "Хоу, бетни кетер",
        "delete_and_move_reason": "«[[$1]]» бетни атын тюрлендирир ючюн кетерилди",
index c34ecd3..a54b02a 100644 (file)
        "right-blockemail": "Metmaacher för et E-Mail Verschecke sperre",
        "right-hideuser": "Ene Metmaacher sperre un em singe Name versteiche",
        "right-ipblock-exempt": "Es ußjenomme vun automatesche Sperre, vun Sperre fun IP-Adresse, un vun Sperre vun Bereiche vun IP-Adresse",
-       "right-proxyunbannable": "Es ußjenomme fun automatische Sperre fun Proxy-Servere",
        "right-unblockself": "Retuhr nämme, wam_mer sellver jesperrt woode es",
        "right-protect": "Sigge schöze, jeschözde Sigge änndere, un der iere Schoz widder ophevve",
        "right-editprotected": "Sigge ändere, di met „{{int:protect-level-sysop}}“ jezöz sin",
        "right-managechangetags": "[[Special:Tags|Kännzeijsche]] en de Dahtebangk aanlähje udder fottschmiiße",
        "right-applychangetags": "[[Special:Tags|Makehronge]] met de eije Änderonge zersamme verjävve",
        "right-changetags": "[[Special:Tags|Makehronge]] vun Väsjohne un Enndrähsche em Logbohch fott nämme un zohföhje",
+       "grant-group-page-interaction": "Met Sigge ömjonn",
+       "grant-group-file-interaction": "Met Mehdeje_Datteije ömjonn",
+       "grant-group-watchlist-interaction": "Met de eije Oppaßless ömjonn",
+       "grant-group-email": "<i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> scheke",
+       "grant-group-high-volume": "Vill en koote Zigg donn",
+       "grant-group-customization": "Enschtällonge",
+       "grant-group-administration": "Verwallde",
+       "grant-group-other": "Söns jät donn",
+       "grant-blockusers": "Metmaacher schpaääre un esu en Schpärre ophävvve",
+       "grant-createaccount": "Zohjäng aanjähje",
+       "grant-createeditmovepage": "Sigge aanläje, änndere, un ömnänne",
+       "grant-delete": "Sigge, Väsjohne vun Sigge, un Endrähsch uss em Lohbohch fottschmiiße",
+       "grant-editinterface": "Em Appachtemang {{ns:MediaWiki}} un aan de Metmaacher iere <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Cascading Style Sheet\">CSS</i>e un JavaSkrepte jäd änndere.",
+       "grant-editmycssjs": "Aan de eije <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Cascading Style Sheet\">CSS</i>e un JavaSkrepte jäd änndere.",
+       "grant-editmyoptions": "Aan de eije Enschtälonge jäd änndere.",
+       "grant-editmywatchlist": "De eije Oppaßleß änndere.",
+       "grant-editpage": "Sigge ändere, di et ald jitt",
+       "grant-editprotected": "Jeschöz Sigge ändere",
+       "grant-highvolume": "Vill en einem Rötsch ändere",
+       "grant-oversight": "Metmaacher verschteische un Väsohne ongerdröke",
+       "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-viewdeleted": "Fottjeschmeße Dahte un Sigge belohre",
+       "grant-viewmywatchlist": "De eije Oppaßleß ze belooere",
        "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",
        "listgrouprights-namespaceprotection-header": "Beschrängkonge för Appachtemangs",
        "listgrouprights-namespaceprotection-namespace": "Appachtemang",
        "listgrouprights-namespaceprotection-restrictedto": "Rääsch(de) zom Verändere",
+       "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.",
        "emailccme": "Scheck mer en Kopie vun dä E-Mail.",
        "emailccsubject": "En Kopie vun Dinger E-Mail aan $1: $2",
        "emailsent": "De <i lang=\"en\">e-mail</i> es ongerwähs",
-       "emailsenttext": "Ding E-Mail es jetz lossjescheck woode.",
+       "emailsenttext": "Ding <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> es jäz loßßjeschek woode.",
        "emailuserfooter": "Heh di <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"„de eläktrohnesche Poß“\">e-mail</i> hät {{GENDER:$1|dä|et|dä Metmaacher|di Metmaacherėn|dät}} „$1“ an {{GENDER:$2|dä|et|dä Metmaacher|di Metmaacherėn|dät}} „$2“ jescheck, un doför {{GRAMMAR:en dative|{{SITENAME}}}} dat „{{int:emailuser}}“ jebruch.",
        "usermessage-summary": "En Nohreesch vum Wiki afjelivvert.",
        "usermessage-editor": "Name vum Metmaacher för de Täxte un Nohreshte vum Wiki ze beärbeide",
        "export-download": "Als en <i lang=\"en\" xml:lang=\"en\" dir=\"ltr\" title=\"Extensible Markup Language\">XML</i>-Dattei affschpeischere",
        "export-templates": "De Schablohne met expochtehre, di de Sigge bruche",
        "export-pagelinks": "Donn de Sigge metnämme, wo vun heh Lengks drop jon, un vun do wigger, bes esu vill Schrette:",
+       "export-manual": "Donn Sigge vun Hand derbei.",
        "allmessages": "Aanzeije-Baustein, Täxte, un Nohreeschte vum Wiki-System",
        "allmessagesname": "Nahme",
        "allmessagesdefault": "Dä standaadmäßije Tex",
        "thumbnail_error_remote": "Ene Fähler es em $1 opjevalle:\n$2",
        "djvu_page_error": "De DjVu-Sgg es ußerhallef",
        "djvu_no_xml": "De XML-Date för di DjVu-Datei kunnte mer nit afrofe",
-       "thumbnail-temp-create": "Mer kunnte kein Zweschedattei für Minnibeldscher aanlääje.",
+       "thumbnail-temp-create": "Mer kunnte kein Zweschedattei für Minnibeldscher aanlähje.",
        "thumbnail-dest-create": "Mer kunnte kein Minnibeldscher faßhallde, woh se hen sulle.",
        "thumbnail_invalid_params": "Ene Parameter för et Breefmarke-Belldsche (<i lang=\"en\">thumbnail</i>) Maache wohr nit en Odenung",
        "thumbnail_toobigimagearea": "Datteij met mih wi $1",
        "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_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",
        "pagelang-name": "Sigg",
        "pagelang-language": "De Schprohch",
        "mediastatistics-header-text": "Täx",
        "mediastatistics-header-executable": "Projramme",
        "mediastatistics-header-archive": "Kumpremeerte Dahtefommahte",
+       "mediastatistics-header-total": "Alle Datteije",
        "json-warn-trailing-comma": "{{PLURAL:$1|0=Kei Komma wood|1=Ei Komma woodt|$1 Kommas woodte}} aam Ängk vum <i lang=\"en\" xml:lang=\"en\">JSON</i> fott jenumme.",
        "json-error-unknown": "Mem <i lang=\"en\" xml:lang=\"en\">JSON</i> es jät scheif jeloufe: $1",
        "json-error-depth": "Der ẞtägg eß övverjeloufe",
        "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."
+       "api-error-blacklisted": "Söhk Der ene anndere Nahme uß, dä mih drövver säht.",
+       "randomrootpage": "Zofällige Aanfangs-Sigg"
 }
index 8c3ab77..bc6c06b 100644 (file)
        "morenotlisted": "Ev lîste nehatiye temamkirin.",
        "mypage": "Rûpela min",
        "mytalk": "Gotûbêja min",
-       "anontalk": "Gotûbêj ji bo vê IP'ê",
+       "anontalk": "Gotûbêj",
        "navigation": "Navîgasyon",
        "and": "&#32;û",
        "qbfind": "Bibîne",
        "viewtalkpage": "Li gotûbêjê binêre",
        "otherlanguages": "Bi zimanên din",
        "redirectedfrom": "(Ji $1 hate beralîkirin)",
-       "redirectpagesub": "Rûpela beralî bike",
+       "redirectpagesub": "Rûpelê beralî bike",
        "redirectto": "Beraliyê vir bike:",
        "lastmodifiedat": "Ev rûpel cara dawî $1, seet li $2an de hate guherandin.",
        "viewcount": "Ev rûpel {{PLURAL:$1|carekê|caran}} tê xwestin.",
        "userlogin-remembermypassword": "Min têketî bihêle",
        "userlogin-signwithsecure": "Girêdana parastî bikarbîne",
        "yourdomainname": "Domainê te",
+       "password-change-forbidden": "Tu nikarî şîfreyan li ser vê wîkiyê biguherînî.",
        "externaldberror": "Çewtiyeke bingeha daneyan heye, an jî destûra te ya rojanekirina hesabê xweyê navxweyî nîne.",
        "login": "Têkeve",
        "nav-login-createaccount": "Têkeve / hesabekî nû çêke",
        "template-protected": "(tê parastin)",
        "template-semiprotected": "(nîv-parastî)",
        "hiddencategories": "Ev rûpel endamê {{PLURAL:$1|1 kategoriya veşartî|$1 kategoriyên veşartî}} ye:",
+       "nocreate-loggedin": "Destûra te tune ye ku tu rûpelên nu biafirînî.",
        "sectioneditnotsupported-title": "Guhertina beşê nayê piştgirîkirin",
        "sectioneditnotsupported-text": "Guhertina beşê di vê rûpelê de nayê piştgirîkirin.",
        "permissionserrors": "Çewtiyê destûrê",
        "revertmerge": "Veqetîne",
        "history-title": "Dîroka guhertoyên \"$1\"",
        "difference-title": "Cudahiya di navbera guhertoyên \"$1\" de",
+       "difference-title-multipage": "Cudahî di navbera rûpela \"$1\" û \"$2\" de",
        "difference-multipage": "(Cudahî di navbera rûpelan de)",
        "lineno": "Rêz $1:",
        "compareselectedversions": "Guhertoyan bide ber hev",
        "rcshowhidemine": "Guherandinên min $1",
        "rcshowhidemine-show": "nîşan bide",
        "rcshowhidemine-hide": "veşêre",
+       "rcshowhidecategorization-show": "Nîşan bide",
+       "rcshowhidecategorization-hide": "Veşêre",
        "rclinks": "$1 guherandinên di $2 rojên dawî de nîşan bide<br />$3",
        "diff": "cudahî",
        "hist": "dîrok",
        "recentchangeslinked-title": "Guherandinên têkildarî \"$1\"",
        "recentchangeslinked-summary": "Ev lîste, ji rûpela destnîşankirî (an jî endamên destnîşankirî) re rûpelê lîsteya guherandinên dawî ji rûpelên lînkkirî nîşandide. Ew rûpel yê di [[Special:Watchlist|lîsteya te ya şopandinê]] da bi nivîsa <strong>estûr<strong> tên nîşandan.",
        "recentchangeslinked-page": "Navê rûpelê:",
+       "recentchanges-page-added-to-category": "[[:$1]] li kategoriyê hate zêdekirin",
+       "recentchanges-page-removed-from-category": "[[:$1]] ji kategoriyê hate jêbirin",
        "autochange-username": "otomatîk guherandin a MediaWikiyê",
        "upload": "Wêneyekî bar bike",
        "uploadbtn": "Dosyeyek bar bike",
        "reuploaddesc": "Barkirinê biskîne û dîsa here rûpela barkirinê.",
+       "upload-tryagain": "Danasîna dosyeya guherandinî tomar bike",
        "uploadnologin": "Xwe tomar nekir",
        "uploadnologintext": "Ji kerema xwe re ji bo barkirina dosyeyan $1 dake.",
        "uploaderror": "Çewtiya barkirinê",
        "upload-failure-subj": "Pirsgirêka barkirinê",
        "upload-warning-subj": "Hişyariya barkirinê",
        "upload-file-error": "Çewtiya navxweyî",
+       "upload-dialog-button-cancel": "Betal bike",
+       "upload-dialog-button-done": "Çêbû",
+       "upload-dialog-button-save": "Tomar bike",
+       "upload-dialog-button-upload": "Bar bike",
+       "upload-form-label-select-file": "Dosyeyê hilbijêre",
+       "upload-form-label-infoform-title": "Detay",
+       "upload-form-label-infoform-name": "Nav",
+       "upload-form-label-infoform-description": "Danasîn",
+       "upload-form-label-usage-title": "Bikaranîn",
+       "upload-form-label-usage-filename": "Navê dosyeyê",
        "foreign-structured-upload-form-label-own-work": "Min ev xebat bi xwe çêkiriye",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorî",
+       "foreign-structured-upload-form-label-infoform-date": "Dîrok",
        "backend-fail-notexists": "Dosye $1 tune ye.",
        "backend-fail-delete": "Dosyeya \"$1\" nikaribû bê jêbirin.",
        "backend-fail-store": "Dosyeya \"$1\" di bin \"$2\" nikaribû bê tomarkirin.",
        "protectedpagesempty": "Niha ti rûpelên ku bi vê parametreyê parastî ne, tine ne.",
        "protectedpages-page": "Rûpel",
        "protectedpages-reason": "Sedem",
+       "protectedpages-submit": "Rûpelan nîşan bide",
        "protectedpages-unknown-timestamp": "Nenas",
        "protectedpages-unknown-performer": "Bikarhênera nenas",
        "protectedtitles": "Sernavên parastî",
+       "protectedtitles-submit": "Sernavan nîşan bide",
        "listusers": "Lîsteya bikarhêneran",
        "listusers-editsonly": "Tenê bikarhênerên bi guherrandinan nîşan bide",
        "usercreated": "di $1 de, li $2 hate çêkirin",
        "wlnote": "Niha {{PLURAL:$1|xeyrandinê|'''$1''' xeyrandinên}} dawî yê {{PLURAL:$2|seetê|'''$2''' seetên}} dawî {{PLURAL:$1|tê|tên}} dîtin.",
        "wlshowlast": "Guhertinên berî $1 saetan, $2 rojan, ya  nîşan bide",
        "watchlistall2": "hemû",
+       "watchlist-hide": "Veşêre",
+       "watchlist-submit": "Nîşan bide",
+       "wlshowhideliu": "bikarhênerên tomarkirî",
+       "wlshowhidecategorization": "kategorîzekirina rûpelan",
        "watchlist-options": "Vebijarkên lîsteya şopandinê",
        "watching": "Tê şopandin...",
        "unwatching": "Nay şopandin…",
        "delete-confirm": "Jêbirina \"$1\"",
        "delete-legend": "Jê bibe",
        "historywarning": "'''Hişyarî''': Dîrokeke vê rûpela tu dixwazî jê bibî heye:",
+       "historyaction-submit": "Nîşan bide",
        "confirmdeletetext": "Tu kê niha rûpelekê bi tev dîroka wê jêbibê. Xêra xwe zanibe tu kê niha çi bikê û zanibe, çi di wîkîyê da yê bibe. Hên jî seke, ku ev jêbirina bi [[{{MediaWiki:Policy-url}}|mafên wîkîyê]] ra dimeşin ya na.",
        "actioncomplete": "Çalakî pêk hat",
        "actionfailed": "Çalakî têkçû",
        "editcomment": "Kurtenivîsê guherandinê ev bû: \"''$1''\".",
        "revertpage": "Guherandina $2 hat betal kirin, vegerand guhartoya dawî ya $1",
        "rollback-success": "Guherandina $1 şondakir; dîsa guharte verzyona $2.",
+       "changecontentmodel-title-label": "Sernavê rûpelê",
+       "changecontentmodel-reason-label": "Sedem:",
        "protectlogpage": "Têketina parastinê",
        "protectedarticle": "parastî [[$1]]",
        "modifiedarticleprotection": "parastina \"[[$1]]\" guherand",
        "thumbnail-more": "Mezin bike",
        "filemissing": "Rûpel tune",
        "import": "Rûpelan wîne (import)",
+       "import-interwiki-sourcepage": "Çavkaniya rûpelê:",
        "import-interwiki-submit": "Tevlî bike",
        "import-upload-filename": "Navê pelê:",
        "import-comment": "Şîrove:",
        "importfailed": "Împort nebû: $1",
        "importbadinterwiki": "Interwiki-lînkekî xerab",
        "importsuccess": "Împort çêbû!",
+       "import-upload": "Daneyên XMLê bar bike",
        "importlogpage": "Têketina tevlîkirinê",
        "javascripttest": "JavaScript tê testkirin",
        "tooltip-pt-userpage": "Rûpela min",
        "pageinfo-header-edits": "Dîrokê biguherîne",
        "pageinfo-header-restrictions": "Parastina rûpelê",
        "pageinfo-header-properties": "Taybetmendiyên rûpelê",
+       "pageinfo-display-title": "Sernavê nîşan bide",
        "pageinfo-language": "Zimanê naveroka rûpelê",
        "pageinfo-watchers": "Hejmara kesên dişopînin",
        "pageinfo-redirects-name": "Hejmara beralîkirinên ber bi vê rûpelê ve",
        "expand_templates_preview": "Pêşdîtin",
        "pagelang-language": "Ziman",
        "pagelang-select-lang": "Zimanekî hilbijêre",
+       "pagelang-submit": "Tomar bike",
        "right-pagelang": "Zimanê rûpelê biguherîne",
        "action-pagelang": "zimanê rûpelê biguherîne",
        "log-name-pagelang": "Têketina ziman biguherîne",
+       "mediastatistics-header-total": "Hemû dosye",
        "special-characters-group-latin": "Latînî",
        "special-characters-group-latinextended": "Latînî berfirehkirî",
        "special-characters-group-ipa": "IPA",
index 0d6ed54..253d1ed 100644 (file)
                ]
        },
        "tog-underline": "Versores linea denotandi:",
-       "tog-hideminor": "Celare recensiones minores in indice nuper mutatorum",
+       "tog-hideminor": "Recensiones minores in indice nuper mutatorum supprimere",
        "tog-hidepatrolled": "Redactiones censae inter nuper mutatas celandae",
        "tog-newpageshidepatrolled": "Paginae censae inter nouissime creatas celandae",
-       "tog-extendwatchlist": "Extendere indicem paginarum observatarum ut omnes emendationes monstrentur, non solum emendationes recentissimae",
-       "tog-usenewrc": "Indice nuper mutatarum excelsa uti",
+       "tog-extendwatchlist": "In indice paginarum observandarum non solum recentissimas, verum omnes mutationes ostendere",
+       "tog-usenewrc": "Indices per paginas redigere",
        "tog-numberheadings": "Subtituli numeris adornandi",
        "tog-showtoolbar": "Affigere trabem redigentem",
        "tog-editondblclick": "Percussus duplex redactionem hortetur",
        "tog-editsectiononrightclick": "Paginarum segmenta dextero percussu in titulis redigenda",
-       "tog-watchcreations": "Paginas quas creo et fasciculos quos impono in paginarum observatarum indicem addere",
-       "tog-watchdefault": "Paginas et fasciculos quos recenseo in paginarum observatarum indicem addere",
-       "tog-watchmoves": "Paginas et fasciculos quos moveo in paginarum observatarum indicem addere",
-       "tog-watchdeletion": "Paginas et fasciculos quos deleo in paginarum observatarum indicem addere",
+       "tog-watchcreations": "Paginas, quas creavero, et fasciculos, quos imposuero, observare",
+       "tog-watchdefault": "Paginas et fasciculos, quos recensuero, observare",
+       "tog-watchmoves": "Paginas et fasciculos, quos movero, observare",
+       "tog-watchdeletion": "Paginas et fasciculos, quos delevero, paginarum observandarum indici addere",
        "tog-minordefault": "Notare omnes recensiones quasi minores",
-       "tog-previewontop": "Monstrare praevisum ante capsam recensiti, non post ipsam",
-       "tog-previewonfirst": "Praevisum monstrare recensione incipiente",
-       "tog-enotifwatchlistpages": "Mittere mihi litteras electronicas si pagina a me observata vel fasciculus a me observatus mutatur",
-       "tog-enotifusertalkpages": "Mittere mihi litteras electronicas si mea disputatio mutatur",
-       "tog-enotifminoredits": "Mittere mihi litteras electronicas etiam pro recensionibus minoribus",
-       "tog-enotifrevealaddr": "Monstrare inscriptio mea electronica in nuntiis notificantibus",
+       "tog-previewontop": "Prospectum supra capsam recensoriam ostendere",
+       "tog-previewonfirst": "Prospectum novae paginae perhibere",
+       "tog-enotifwatchlistpages": "Mutata vel pagina vel fasciculo observando certior fiam",
+       "tog-enotifusertalkpages": "De mutata disputationis pagina mea certior fiam",
+       "tog-enotifminoredits": "Etiam de minoribus recensionibus certior fiam",
+       "tog-enotifrevealaddr": "Ostendatur inscriptio mea electronica in nuntiis notificantibus",
        "tog-shownumberswatching": "Numerum usorum observantium monstrare",
-       "tog-oldsig": "Subscriptio, qua nunc uteris",
+       "tog-oldsig": "Subscriptio, qua nunc uteris:",
        "tog-fancysig": "Subscriptio vicitext (sine nexu automatico)",
-       "tog-uselivepreview": "Praevisum viventem adhibere (experimentalis)",
+       "tog-uselivepreview": "Prospectum viventem perhibere",
        "tog-forceeditsummary": "Si recensionem non summatim descripsero, me roga si continuare velim",
-       "tog-watchlisthideown": "Celare recensiones meas in paginarum observatarum indice",
-       "tog-watchlisthidebots": "Celare recensiones automatarias in paginarum observatarum indice",
-       "tog-watchlisthideminor": "Celare recensiones minores in paginarum observatarum indice",
-       "tog-watchlisthideliu": "Celare recensiones usorum notorum in paginarum observatarum indice",
-       "tog-watchlisthideanons": "Celare recensiones usorum ignotorum in paginarum observatarum indice",
-       "tog-watchlisthidepatrolled": "Recensiones vigilatae paginas custoditas celare",
-       "tog-ccmeonemails": "Mitte mihi transcriptiones litterarum quas ad alios usores mitto",
+       "tog-watchlisthideown": "Recensiones meas in paginarum observandarum indice supprimere",
+       "tog-watchlisthidebots": "Recensiones per automaton factas in paginarum observandarum indice supprimere",
+       "tog-watchlisthideminor": "Minores recensiones in paginarum observandarum indice supprimere",
+       "tog-watchlisthideliu": "Recensiones ab usoribus notis factas in paginarum observandarum indice supprimere",
+       "tog-watchlistreloadautomatically": "Quamprimum aliquis selectus mutatus erit, indicem paginarum observandarum reficere (JavaScript necesse est)",
+       "tog-watchlisthideanons": "Recensiones ab usoribus ignotis factas in paginarum observandarum indice supprimere",
+       "tog-watchlisthidepatrolled": "Recensiones custoditas supprimere",
+       "tog-ccmeonemails": "Transcriptiones earum, quas ad alios usores misero litteras, mihi ipsi mittantur",
        "tog-diffonly": "Nihil nisi differentiam in pagina factam ostendatur",
        "tog-showhiddencats": "Categorias celatas monstrare",
        "tog-norollbackdiff": "Post reversionem paginae differentia neglegatur",
        "disclaimers": "Repudiationes",
        "disclaimerpage": "Project:Repudiationes",
        "edithelp": "Opes recensendi",
+       "helppage-top-gethelp": "Auxilium",
        "mainpage": "Pagina prima",
        "mainpage-description": "Pagina prima",
        "policy-url": "Project:Consilium",
        "noemail": "Nulla inscriptio electronica invenitur per usorem \"$1\".",
        "mailerror": "Error in litteras electronicas inmittendas: $1",
        "acct_creation_throttle_hit": "His superioribus 24 horis ex isto loco IP iam {{PLURAL:$1|nomen impositum est|$1 nomina imposita sunt}}.\nNon autem licet plura sibi imponere. Igitur hodie ex hoc loco IP cetera nomina tibi non imponi possunt.",
-       "emailauthenticated": "Tua inscriptio electronica recognita est $3, $2.",
+       "emailauthenticated": "Inscriptio tua electronica recognita est $3, $2.",
        "emailconfirmlink": "Inscriptionem tuam electronicam adfirmare",
        "emaildisabled": "Huic paginae litteras electronicas mittere non licet.",
        "accountcreated": "Nomen impositum",
        "summary": "Summarium:",
        "subject": "Res/titulus:",
        "minoredit": "Haec est recensio minor",
-       "watchthis": "Observare hanc paginam",
-       "savearticle": "Servare hanc rem",
+       "watchthis": "Hanc paginam observare",
+       "savearticle": "Hanc redactionem servare",
        "preview": "Praevidere",
-       "showpreview": "Monstrare praevisum",
-       "showdiff": "Mutata ostendere",
+       "showpreview": "Prospectum ostendere",
+       "showdiff": "Mutationes ostendere",
        "anoneditwarning": "<strong>Monitio:</strong> Nomen tuum non dedisti. In recensendo locus IP tuus in historia huius paginae notabitur. Quodsi <strong>[$1 nomen tuum dederis]</strong> vel <strong>[$2 nomen tibi imposueris]</strong>, quaecumque recensebis, isti nomini attribuentur.",
        "anonpreviewwarning": "''Nomen tuum non dedisti. Quodsi servas, locus IP tuus in historia huius paginae notabitur.''",
        "missingcommenttext": "Sententiam subter inscribe.",
-       "summary-preview": "Praevisum summarii:",
-       "subject-preview": "Praevisum rei/tituli:",
+       "summary-preview": "Prospectus summarii:",
+       "subject-preview": "Prospectus rei/tituli:",
        "blockedtitle": "Usor obstructus est",
        "blockedtext": "'''Nomen usoris aut locus IP tuus obstructus est''' a magistratu $1.\n\nRatio data est: ''$2''.\n\n* Initium obstructionis: $8\n* Finis obstructionis: $6\n* Obstructus destinatus: $7\n\nPotes ad $1 aut [[{{MediaWiki:Grouppage-sysop}}|magistratum]] alium nuntium mittere ad impedimentum disputandum.\nNota bene te non posse proprietate \"Litteras electronicas usori mittere\" uti, nisi tibi est inscriptio electronica confirmata apud [[Special:Preferences|praeferentias usoris tuas]] vel si tibi etiam litterae electronicae obstructi sunt.\nLocus IP tuus temporarius est $3, et numerus obstructionis est #$5. Quaesumus te eos scripturum si quaestiones ullas roges.",
        "autoblockedtext": "Locus IP tuus automatice obstructus est quia usor alius, qui a magistratu $1 obstructus est, eum adhiberat.\nRatio data est:\n\n:''$2''\n\n* Initium obstructionis: $8\n* Finis obstructionis: $6\n* Obstructus destinatus: $7\n\nPotes ad $1 aut [[{{MediaWiki:Grouppage-sysop}}|magistratum]] alium nuntium mittere ad impedimentum disputandum.\n\nNota bene te non posse proprietate \"Litteras electronicas usori mittere\" uti, nisi tibi est inscriptio electronica confirmata apud [[Special:Preferences|praeferentias usoris tuas]].\n\nLocus IP tuus temporarius $3 est et numerus obstructionis tuus est #$5. Quaesumus te eos scripturum si quaestiones ullas roges.",
        "userpage-userdoesnotexist": "Usor \"<nowiki>$1</nowiki>\" non est. Visne re vera hanc paginam creare vel recensere?",
        "updated": "(Novata)",
        "note": "'''Nota:'''",
-       "previewnote": "'''Memento hanc paginam solum praevisam esse, neque iam servatam!'''",
+       "previewnote": "<strong>Memento istud nihil esse nisi prospectum!</strong>\nMutationes tuae nondum servatae sunt!",
        "editing": "Recensio paginae \"$1\"",
        "creating": "Creans $1",
        "editingsection": "Recensens $1 (partem)",
        "copyrightwarning2": "Nota bene omnia contributa apud {{grammar:accusative|{{SITENAME}}}} ab aliis recenseri, mutari vel removi posse.\nNisi vis verba tua crudelissime recenseri, noli ea submittere.<br />\nNobis etiam spondes te esse ipsum horum verborum scriptorem primum, aut ex opere in \"dominio publico\" vel ex libere fonte simili exscripsisse (vide singula apud $1).\n'''NOLI OPERIBUS SUB IURE DIVULGANDI UTI SINE POTESTATE!'''",
        "protectedpagewarning": "'''CAVE: Haec pagina protecta est ut magistratus soli eam recenseant.'''",
        "templatesused": "{{PLURAL:$1|Formula hac in pagina adhibita:|Formulae hac in pagina adhibitae:}}",
-       "templatesusedpreview": "{{PLURAL:$1|Formula hoc in praeviso adhibita:|Formulae hoc in praeviso adhibitae:}}",
+       "templatesusedpreview": "{{PLURAL:$1|Formula hoc in prospectu adhibita:|Formulae hoc in prospectu adhibitae:}}",
        "templatesusedsection": "{{PLURAL:$1|Formula hac in parte adhibita:|Formulae hac in parte adhibitae:}}",
        "template-protected": "(protecta)",
        "template-semiprotected": "(semi-protecta)",
        "last": "prox",
        "page_first": "prim",
        "page_last": "ult",
-       "histlegend": "Selige pro dissimilitudine: indica emendationes in botones radiales et \"intrare\" in claviatura vel \"comparatio\" imprime ut conferas.<br />\nTitulus: '''({{int:cur}})''' = dissimilis ab emendatione novissima,\n'''({{int:last}})''' = dissimilis ab emendatione proxima, '''{{int:minoreditletter}}''' = recensio minor.",
+       "histlegend": "Ad seligendas differentias nota diversarum redactionum bullas et agi iube!<br />\nLegenda: '''({{int:cur}})''' = differentiam monstrabit inter hanc et novissimam redactionem,\n'''({{int:last}})''' = differentiam monstrabit inter hanc et superiorem redactionem,\n'''({{int:minoreditletter}})''' = recensio minor.",
        "history-fieldset-title": "Quaerere in paginae historia",
        "history-show-deleted": "Solum recensiones deletas monstrare",
        "histfirst": "veterrima",
        "mergehistory-reason": "Causa:",
        "mergelog": "Acta confundendi",
        "revertmerge": "Inconfundere",
-       "history-title": "$1: Historia paginae",
+       "history-title": "Historia paginae \"$1\"",
        "lineno": "Linea $1:",
-       "compareselectedversions": "Conferre emendationes selectas",
+       "compareselectedversions": "Redactiones selectas conferre",
        "showhideselectedversions": "Monstrare/celare emendationes selectas",
        "editundo": "abrogare",
        "diff-empty": "(eadem)",
        "shown-title": "Monstrare $1 {{PLURAL:$1|eventum|eventus}} per paginam",
        "viewprevnext": "Videre ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''Iam est pagina \"[[:$1]]\"'''",
-       "searchmenu-new": "'''Creare paginam \"[[:$1]]\"'''",
+       "searchmenu-new": "<strong>Si vis, paginam \"[[:$1]]\" crea!<strong> {{PLURAL:$2|0=|Conferatur etiam pagina sequens, ubi quaesitum quodam modo continetur.|Conferantur etiam paginae $2 sequentes, in quibus quaesitum quodam modo continetur.}}",
        "searchprofile-articles": "Paginae contentorum",
        "searchprofile-images": "Multimedia",
        "searchprofile-everything": "Omnia",
        "search-redirect": "(redirectio $1)",
        "search-section": "(pars $1)",
        "search-suggest": "Nonne dicere voluisti: $1",
+       "search-rewritten": "Ostenduntur, quae per \"$1\" inveniuntur. Profecto scrutinari, ubi \"$2\" contineatur!",
        "search-interwiki-caption": "Alia incepta",
        "search-interwiki-default": "$1 eventus:",
        "search-interwiki-more": "(plus)",
        "mypreferences": "Praeferentiae",
        "prefs-edits": "Numerus recensionum:",
        "prefs-skin": "Aspectum",
-       "skin-preview": "Praevisum",
+       "skin-preview": "Prospectus",
        "datedefault": "Nullum praeferentiae",
        "prefs-user-pages": "Paginae usoris",
-       "prefs-personal": "Minutiae rationis",
+       "prefs-personal": "Proprietates",
        "prefs-rc": "Nuper mutata",
-       "prefs-watchlist": "Paginae observatae",
-       "prefs-watchlist-days": "Numerus dierum displicandus in paginis tuis observatis:",
+       "prefs-watchlist": "Paginae observandae",
+       "prefs-watchlist-days": "Per quot dies mutationes porrigendae sint:",
        "prefs-watchlist-days-max": "Numerus maximus: $1 {{PLURAL:$1|dies|dies}}",
-       "prefs-watchlist-edits": "Numerus recensionum displicandus in paginis tuis observatis extensis:",
+       "prefs-watchlist-edits": "Quot mutationes maxime porrigendae sint:",
        "prefs-watchlist-edits-max": "Numerus maximus: 1000",
        "prefs-misc": "Misc",
        "prefs-resetpass": "Tesseram mutare",
-       "prefs-email": "Optiones inscriptionis electronicae",
+       "prefs-email": "Modi de inscriptione electronica servandi",
        "prefs-rendering": "Conspectus",
-       "saveprefs": "Servare praeferentias",
+       "saveprefs": "Hos modos servare",
        "prefs-editing": "Mensura capsae verbi",
        "rows": "Lineae:",
        "columns": "Columnae:",
        "searchresultshead": "Figuratio eventorum investigationis",
-       "recentchangesdays": "Quot dies in nuper mutatis monstrandi:",
-       "recentchangescount": "Quantum rerum in nuper mutatis, historiis et actis:",
+       "recentchangesdays": "Per quot dies mutationes porrigendae sint:",
+       "recentchangescount": "Quot mutationes porrigendae sint:",
        "savedprefs": "Praeferentiae tuae servatae sunt.",
        "timezonelegend": "Zona temporis:",
        "localtime": "Hora indigena:",
        "timezoneregion-europe": "Europa",
        "timezoneregion-indian": "Oceanus Indicus",
        "timezoneregion-pacific": "Oceanus Pacificus",
-       "allowemail": "Sinere litteras electronicas inscriptioni electronicae meae mittere",
+       "allowemail": "Aliis usoribus concedere, ut litteras electronicas mittant",
        "prefs-searchoptions": "Quaerere",
        "prefs-namespaces": "Spatia nominalia",
        "default": "praedeterminatum",
        "prefs-emailconfirm-label": "Adfirmatio inscriptionis electronicae:",
        "youremail": "Inscriptio electronica:",
        "username": "Nomen usoris:",
+       "prefs-memberingroups": "{{PLURAL:$1|Categoria, cui|Categoriae, quibus}} $2 attribuitur:",
        "prefs-registration": "Tempus, quo nomen impositum est:",
        "yourrealname": "Nomen verum:",
        "yourlanguage": "Lingua:",
        "yourvariant": "Differentia linguae contentorum:",
        "yournick": "Subscriptio nova:",
-       "prefs-help-signature": "Cum in paginis disputationum scribas, \"<nowiki>~~~~</nowiki>\" conscribe, quod in subscriptionem tuam et indicationem temporis convertetur.",
+       "prefs-help-signature": "Disputationibus auctis ne subscripseris nisi quater undulam (<nowiki>~</nowiki>) ponens! Quae quatuor undulae (<nowiki>~~~~</nowiki>) automatice nomen tuum et diem adscribent.",
        "badsig": "Subscriptio cruda non est valida; scrutina affixa HTML.",
        "badsiglength": "Subscriptio tua nimis longa est.\n{{PLURAL:$1|Una littera est|$1 litterae sunt}} longitudo maxima.",
        "yourgender": "Sexus:",
        "gender-unknown": "Indefinitus",
        "gender-male": "masculinus",
        "gender-female": "femininus",
+       "prefs-help-gender": "Liber vel libera es istum delectum habere.\nQuodsi feceris, programma eo utetur ad te rite iuxta genus tuum aut appellandum aut appellandam.\nQuod datum ab omnibus videbitur.",
        "email": "Litterae electronicae",
        "prefs-help-realname": "Nomen verum non necesse est.\nSi vis id dare, opera tua tibi ascribentur.",
-       "prefs-help-email": "Non necesse est inscriptionem electronicam dare. Qua tamen data licebit tibi tesseram novam tribuere, si eius oblitus eris.",
-       "prefs-help-email-others": "Si vis, sinit etiam aliis tecum loqui per tuam paginam usoris vel disputationis, nisi te reveles.",
+       "prefs-help-email": "Non necesse est inscriptionem electronicam dare. Qua tamen data tessera tibi tribui poterit nova, si prioris oblitus eris.",
+       "prefs-help-email-others": "Praeterea, si libeat, aliis concedas tibi nuntia per nexum in pagina vel disputatione tua positum mittere electronicas.\nInscriptio tua conlatoribus, qui tibi scribeant, latebit.",
        "prefs-help-email-required": "Inscriptio electronica necesse est.",
        "prefs-info": "Generalia",
        "prefs-i18n": "Sermonis delectus",
        "prefs-signature": "Subscriptio",
-       "prefs-preview": "Praevisum",
-       "prefs-advancedwatchlist": "Praeferentiae monstrare",
+       "prefs-preview": "Prospectus",
+       "prefs-advancedrc": "Modi speciales",
+       "prefs-advancedwatchlist": "Indicis modi speciales",
        "prefs-displayrc": "Praeferentiae vultus",
        "prefs-displaywatchlist": "Praeferentiae vultus",
        "prefs-diffs": "Differentiae",
        "userrights-unchangeable-col": "Greges quos tibi non oportet mutare",
        "group": "Grex:",
        "group-user": "Usores",
-       "group-autoconfirmed": "Usores adfirmati automaticale",
+       "group-autoconfirmed": "Usores automatice confirmati",
        "group-bot": "Automata",
        "group-sysop": "Magistratus",
        "group-bureaucrat": "Grapheocrates",
        "group-suppress": "Censurae",
        "group-all": "(omnes)",
        "group-user-member": "{{GENDER:$1|Usor}}",
-       "group-autoconfirmed-member": "{{GENDER:$1|Usor adfirmatus automaticale}}",
+       "group-autoconfirmed-member": "{{GENDER:$1|Usor automatice confirmatus}}",
        "group-bot-member": "{{GENDER:$1|Automaton}}",
        "group-sysop-member": "{{GENDER:$1|Magistratus}}",
        "group-bureaucrat-member": "{{GENDER:$1|Grapheocrates}}",
        "group-suppress-member": "{{GENDER:$1|Censura}}",
        "grouppage-user": "{{ns:project}}:Usores",
-       "grouppage-autoconfirmed": "{{ns:project}}:Usores adfirmati automaticale",
+       "grouppage-autoconfirmed": "{{ns:project}}:Usores automatice confirmati",
        "grouppage-bot": "{{ns:project}}:Automata",
        "grouppage-sysop": "{{ns:project}}:Magistratus",
        "grouppage-bureaucrat": "{{ns:project}}:Grapheocrates",
        "right-rollback": "Cito reverti recensiones proximas usoris cuiuslibet paginae",
        "right-import": "Paginas ex vicis aliis importare",
        "right-importupload": "Paginas ex fasciculo imponendo importare",
-       "right-unwatchedpages": "Indicem paginarum non observatarum inspicere",
+       "right-unwatchedpages": "Indicem paginarum non observandarum inspicere",
        "right-mergehistory": "Historias paginarum confundere",
        "right-userrights": "Omnes potestates usorum recensere",
        "right-userrights-interwiki": "Potestates usorum aliis in vicis recensere",
        "action-protect": "protectionem huius paginae mutare",
        "action-import": "paginas ex vico alio importare",
        "action-importupload": "paginas ex fasciculo imponendo importare",
-       "action-unwatchedpages": "indicem paginarum non observatarum inspicere",
+       "action-unwatchedpages": "indicem paginarum non observandarum inspicere",
        "action-mergehistory": "historiam huius paginae confundere",
        "action-userrights": "omnes potestates usorum recensere",
        "action-userrights-interwiki": "potestates usorum aliis in vicis recensere",
        "action-siteadmin": "basem datorum obstruere vel deobstruere",
-       "action-editmywatchlist": "indicem tuum paginarum observatarum recensere",
-       "action-viewmywatchlist": "indicem tuum paginarum observatarum inspicere",
+       "action-editmywatchlist": "indicem tuum paginarum observandarum recensere",
+       "action-viewmywatchlist": "indicem tuum paginarum observandarum inspicere",
        "nchanges": "$1 {{PLURAL:$1|mutatio|mutationes}}",
        "enhancedrc-history": "Historia",
        "recentchanges": "Nuper mutata",
-       "recentchanges-legend": "Indicis paginarum nuper mutatarum praeferentiae",
+       "recentchanges-legend": "Huius indicis modi",
        "recentchanges-summary": "Ecce mutationes recentes.",
        "recentchanges-feed-description": "Nuper mutata Viciae hoc in fluxu observare.",
        "recentchanges-label-newpage": "Pagina nova creata est",
        "recentchangeslinked-feed": "Nuper mutata annexorum",
        "recentchangeslinked-toolbox": "Nuper mutata annexorum",
        "recentchangeslinked-title": "Nuper mutata in paginis quibus pagina \"$1\" nectit",
-       "recentchangeslinked-summary": "Ecce mutationes recentissimas commentationum quae aut paginae cuidam adnectuntur, aut in categoria quadam includuntur. Paginae a [[Special:Watchlist|te observatae]] '''litteris pinguibus''' monstrantur.",
+       "recentchangeslinked-summary": "Ecce mutationes recentissimas commentationum quae aut paginae cuidam adnectuntur, aut in categoria quadam includuntur. Paginae [[Special:Watchlist|tibi observandae]] '''typis pinguioribus''' ostenduntur.",
        "recentchangeslinked-page": "Titulus paginae:",
        "recentchangeslinked-to": "Mutationes commentationum quae huic paginae adnectuntur monstrare",
        "upload": "Fasciculum imponere",
        "license": "Typus permissionis:",
        "license-header": "Potestas usoris",
        "nolicense": "Nulla selecta",
-       "license-nopreview": "(Praevisum monstrari non potest)",
+       "license-nopreview": "(Prospectus non fieri potest)",
        "imgfile": "fasciculus",
        "listfiles": "Fasciculorum index",
        "listfiles_thumb": "Minutio",
        "movethispage": "Movere hanc paginam",
        "notargettitle": "Nullus scopus",
        "notargettext": "Paginam aut usorem non notavisti.",
-       "pager-newer-n": "{{PLURAL:$1|novior 1|noviores $1}}",
-       "pager-older-n": "{{PLURAL:$1|senior 1|seniores $1}}",
+       "pager-newer-n": "{{PLURAL:$1|recentiorem 1|recentiores $1}}",
+       "pager-older-n": "{{PLURAL:$1|superiorem 1|superiores $1}}",
        "suppress": "Censura",
        "booksources": "Librorum fontes",
        "booksources-search-legend": "Fontes impressas quaerere",
        "emailsenttext": "Nuntium tuum missum est.",
        "emailuserfooter": "Has litteras electronicas $1 ad $2 misit per \"Litteras electronicas usori mittere\" in {{grammar:ablative|{{SITENAME}}}}.",
        "usermessage-editor": "Nuntius systematis",
-       "watchlist": "Paginae observatae",
-       "mywatchlist": "Paginae observatae",
-       "watchlistfor2": "ab usore \"$1\"",
-       "nowatchlist": "Sunt nullas paginas in indice tuo paginarum observatarum.",
-       "watchlistanontext": "Ad inspiciendum vel recensendum indicem paginarum observatarum necesse est nomen dare.",
+       "watchlist": "Paginae observandae",
+       "mywatchlist": "Paginae observandae",
+       "watchlistfor2": "ab usore \"$1\" $2",
+       "nowatchlist": "Nihil est in isto indice paginarum observandarum.",
+       "watchlistanontext": "Ad inspiciendum vel recensendum indicem paginarum observandarum necesse est nomen dare.",
        "watchnologin": "Nomen datum non est",
-       "addedwatchtext": "Pagina \"[[:$1]]\" in [[Special:Watchlist|paginas tuas observatas]] addita est.\nMutationes posthac huic paginae et paginae disputationis ibi notabuntur.",
-       "removedwatchtext": "Pagina \"[[:$1]]\" ex [[Special:Watchlist|indice paginarum observatarum]] remota est.",
+       "addedwatchtext": "Pagina \"[[:$1]]\" necnon disputatio pertinens abhinc [[Special:Watchlist|observabitur]].",
+       "removedwatchtext": "Pagina \"[[:$1]]\" necnon disputatio eius ex [[Special:Watchlist|indice paginarum observandarum]] remota est.",
        "watch": "Observare",
        "watchthispage": "Observare hanc paginam",
        "unwatch": "Non iam observare",
        "notvisiblerev": "Emendatio deleta est",
        "watchlist-details": "{{PLURAL:$1|$1 paginam|$1 paginas}} observas.",
        "wlheader-enotif": "Mutationes si quae factae erunt, electronice tibi nuntiabuntur.",
-       "wlheader-showupdated": "Paginae nondum a te inspectae typis <strong>crassioribus</strong> ostenduntur.",
+       "wlheader-showupdated": "Paginae nondum a te inspectae <strong>typis crassioribus</strong> ostenduntur.",
        "wlnote": "{{PLURAL:$1|Indicatur mutatio novissima|Indicantur '''$1''' mutationes novissimae}} abhinc {{PLURAL:$2|superiorem horam|superiores '''$2''' horas}} (ab $3, $4) factae.",
        "wlshowlast": "Monstrare proximas $1 horas $2 dies",
        "watchlistall2": "omnes",
+       "watchlist-hide": "Supprimere recensiones",
        "watchlist-submit": "Porrige",
        "wlshowtime": "Temporis spatium porrigendum:",
+       "wlshowhideminor": "minores",
+       "wlshowhidebots": "automatice factas",
+       "wlshowhideliu": "a conlatoribus notis factas",
+       "wlshowhideanons": "sine nomine factas",
+       "wlshowhidemine": "meas",
        "watchlist-options": "Huius indicis modi",
        "watching": "Custodiens...",
        "unwatching": "Decustodiens...",
        "contributions-title": "Conlationes usoris $1",
        "mycontris": "Conlationes",
        "anoncontribs": "Conlationes",
-       "contribsub2": "Pro $1 ($2)",
+       "contribsub2": "factae ab usore \"$1\" ($2)",
        "nocontribs": "Nullae mutationes inventae sunt ex his indiciis.",
        "uctop": "(vertex)",
        "month": "Ab mense (et prior):",
        "year": "Ab anno (et prior):",
-       "sp-contributions-newbies": "Monstrare solum conlationes rationum novarum",
-       "sp-contributions-newbies-sub": "Conlationes rationum novarum",
-       "sp-contributions-newbies-title": "Conlationes rationum novarum",
+       "sp-contributions-newbies": "Nullas conlationes nisi a conlatoribus novis factis ostendere",
+       "sp-contributions-newbies-sub": "a conlatoribus novis factae",
+       "sp-contributions-newbies-title": "Conlationes a conlatoribus novis factae",
        "sp-contributions-blocklog": "acta obstructionum",
        "sp-contributions-deleted": "conlationes usoris deletae",
        "sp-contributions-uploads": "Fasciculi impositi",
        "tooltip-ca-edit": "Hanc paginam recensere",
        "tooltip-ca-addsection": "Novam partem creare",
        "tooltip-ca-viewsource": "Haec pagina protecta est. Inspicere quidem fontem licet.",
-       "tooltip-ca-history": "Emendationes veteres huius paginae",
+       "tooltip-ca-history": "Superiores huius paginae versiones",
        "tooltip-ca-protect": "Protegere hanc paginam",
        "tooltip-ca-delete": "Delere hanc paginam",
        "tooltip-ca-undelete": "Restituere emendationes huic paginae conlatas antequam haec pagina deleta esset",
        "tooltip-ca-move": "Movere hanc paginam",
-       "tooltip-ca-watch": "Addere hanc paginam tuis paginis observatis",
-       "tooltip-ca-unwatch": "Removere hanc paginam ex tuis paginis observatis",
+       "tooltip-ca-watch": "Hanc paginam observandam habere",
+       "tooltip-ca-unwatch": "Hanc paginam non iam observandam habere",
        "tooltip-search": "Quaerere aliquid in {{grammar:ablative|{{SITENAME}}}}",
        "tooltip-search-go": "I ad paginam cum hoc titulo exacto, si est",
        "tooltip-search-fulltext": "Hunc textum in paginis quaerere",
        "tooltip-ca-nstab-category": "Videre paginam categoriae",
        "tooltip-minoredit": "Indicare hanc recensionem minorem",
        "tooltip-save": "Servare mutationes tuas",
-       "tooltip-preview": "Praevidere mutationes tuas, quaesumus hoc utere antequam servas!",
-       "tooltip-diff": "Monstrare mutationes textui tuas",
-       "tooltip-compareselectedversions": "Inspicere, quantum pagina inter versiones selectas distet",
-       "tooltip-watch": "Addere hanc paginam tuis paginis observatis",
+       "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",
+       "tooltip-watch": "Hanc paginam paginis observandis adscribere",
        "tooltip-recreate": "Recreare hanc paginam etiamsi deleta est",
        "tooltip-upload": "Incipere imponere",
        "tooltip-rollback": "\"Revertere\" omnes ultimi editoris in hac pagina recensiones statim revertit",
        "tooltip-summary": "Summarium breve addere",
        "anonymous": "{{PLURAL:$1|Usor ignotus|Usores ignoti}} {{grammar:genitive|{{SITENAME}}}}",
        "siteuser": "{{SITENAME}} usor $1",
-       "lastmodifiedatby": "Pagina novissime die $2 hora $1 ab usore $3 mutatata est.",
+       "lastmodifiedatby": "Pagina novissime die $2 hora $1 ab usore $3 mutata est.",
        "others": "alii",
        "siteusers": "{{PLURAL:$2|usor|usores}} {{grammar:genitive|{{SITENAME}}}} $1",
        "creditspage": "Auctores paginae",
        "size-kilobytes": "$1 chiliocteti",
        "size-megabytes": "$1 megaocteti",
        "size-gigabytes": "$1 gigaocteti",
-       "watchlistedit-normal-title": "Indicem paginarum observatarum recensere",
+       "watchlistedit-normal-title": "Indicem paginarum observandarum recensere",
        "watchlistedit-normal-submit": "Removere titulos",
-       "watchlistedit-raw-title": "Indicem paginarum observatarum quasi textum recensere",
-       "watchlistedit-raw-legend": "Indicem paginarum observatarum quasi textum recensere",
+       "watchlistedit-raw-title": "Indicem paginarum observandarum textualiter recensere",
+       "watchlistedit-raw-legend": "Indicem paginarum observandarum textualiter recensere",
        "watchlistedit-raw-titles": "Tituli:",
        "watchlisttools-clear": "Nullas iam paginas observare",
        "watchlisttools-view": "Mutationes inspicere",
        "logentry-newusers-create": "Usori $1 nomen impositum est",
        "logentry-newusers-create2": "Usori $3 nomen ab usore $1 impositum est",
        "logentry-newusers-autocreate": "Usori $1 nomen automatice impositum est",
+       "logentry-protect-protect": "$1 {{GENDER:$2|protegit}} $3 $4",
        "rightsnone": "(nullus)",
        "revdelete-summary": "summarium recensionis",
        "feedback-cancel": "Dimittere",
index 07357ea..5757a46 100644 (file)
        "october-date": "$1. Oktober",
        "november-date": "$1. November",
        "december-date": "$1. Dezember",
+       "period-am": "moies",
+       "period-pm": "nomëttes",
        "pagecategories": "{{PLURAL:$1|Kategorie|Kategorien}}",
        "category_header": "Säiten an der Kategorie \"$1\"",
        "subcategories": "Ënnerkategorien",
        "laggedslavemode": "'''Opgepasst:''' Dës Säit ass net onbedéngt um neiste Stand.",
        "readonly": "D'Datebank ass gespaart",
        "enterlockreason": "Gitt w.e.g. e Grond u firwat d'Datebank gespaart ass, a wéi laang dës Spär ongeféier bestoe soll.",
-       "readonlytext": "D'Datebank ass elo fir all Ännerunge gespaart, wahrscheinlech wéinst Maintenance vun der Datebank, duerno ass erëm alles beim alen.\n\nDen Administrateur huet dës Erklärung uginn: $1",
+       "readonlytext": "D'Datebank ass elo fir all Ännerunge gespaart, wahrscheinlech wéinst Maintenance vun der Datebank, duerno ass erëm alles beim Alen.\n\nDe System-Administrateur, deen se gespaart huet, huet dës Erklärung uginn: $1",
        "missing-article": "Den Text „$1“ $2 gouf net an der Datebank fonnt.\n\nDat geschitt normalerweis duerch e Link op eng Säit déi geläscht oder geréckelt gouf.\n\nWann dat net de Fall ass, hutt Dir eventuell e Feeler an der Software fonnt.\nMellt dëst w.e.g. bei engem [[Special:ListUsers/sysop|Administrateur]] a vergiesst net d'URL unzeginn.",
        "missingarticle-rev": "(Versiounsnummer: $1)",
        "missingarticle-diff": "(Ënnerscheed tëscht Versiounen: $1, $2)",
        "mypreferencesprotected": "Dir hutt net d'Recht fir Är Astellungen z'änneren.",
        "ns-specialprotected": "Spezialsäite kënnen net verännert ginn.",
        "titleprotected": "Eng Säit mat dësem Numm kann net ugeluecht ginn. Dës Spär gouf vum [[User:$1|$1]] gemaach deen als Grond ''$2'' uginn huet.",
-       "filereadonlyerror": "De Fichier \"$1\" konnt net geännert ginn well de Repertoire vun de Fichieren \"$2\" nëmme geliest däerf ginn.\n\nDeAdministrateur den d'Schreiwe gespaart huet, huet dës Erklärung uginn: \"$3\"",
+       "filereadonlyerror": "De Fichier \"$1\" konnt net geännert ginn well de Repertoire vun de Fichieren \"$2\" nëmme geliest däerf ginn.\n\nDe System-Administrateur den d'Schreiwe gespaart huet, huet dës Erklärung uginn: \"$3\"",
        "invalidtitle-knownnamespace": "Net valabelen Titel mam Nummraum \"$2\" a mam Text \"$3\"",
        "invalidtitle-unknownnamespace": "Net valabelen Titel mat der onbekannter Nummraum-Zuel $1 a mam Text \"$2\"",
        "exception-nologin": "Net ageloggt",
        "virus-scanfailed": "De Scan huet net funktionéiert (Code $1)",
        "virus-unknownscanner": "onbekannten Antivirus:",
        "logouttext": "'''Dir sidd elo ausgeloggt.'''\n\nOpgepasst: Op verschiddene Säite kann et nach sou aus gesinn, wéi wann Dir nach ageloggt wiert, bis Dir Ärem Browser säin Tëschespäicher (cache) eidel maacht.",
+       "cannotlogoutnow-title": "Ausloggen ass elo net méiglech",
+       "cannotlogoutnow-text": "Ausloggen ass net méiglech wann dir $1 benotzt.",
        "welcomeuser": "Wëllkomm $1!",
        "welcomecreation-msg": "Äre Benotzerkont gouf ugeluecht.\nVergiesst net fir Är [[Special:Preferences|{{SITENAME}} Astellungen]] z'änneren",
        "yourname": "Benotzernumm:",
        "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",
+       "cannotloginnow-title": "Aloggen ass elo net méiglech",
+       "cannotloginnow-text": "Aloggen ass net méiglech wann dir $1 benotzt.",
        "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.",
        "wrongpasswordempty": "D'Passwuert dat Dir aginn hutt war eidel.\nProbéiert w.e.g. nach eng Kéier.",
        "passwordtooshort": "Passwierder musse mindestens {{PLURAL:$1|1 Zeeche|$1 Zeeche}} laang sinn.",
        "passwordtoolong": "Passwierder kënnen net méi laang wéi {{PLURAL:$1|1 Zeeche|$1 Zeeche}} sinn.",
+       "passwordtoopopular": "Dacks gewielt Passwierder kënnen net benotzt ginn. Sicht Iech w.e.g. e méi e spezifescht Passwuert.",
        "password-name-match": "Äert Passwuert muss verschidde vun Ärem Benotzernumm sinn.",
        "password-login-forbidden": "D'Benotze vun dësem Benotzernumm a Passwuert gouf verbueden.",
        "mailmypassword": "Passwuert zrécksetzen",
        "resetpass_submit": "Passwuert aginn an aloggen",
        "changepassword-success": "Äert Passwuert gouf geännert!",
        "changepassword-throttled": "Dir hutt rezent zevill dacks versicht Iech anzeloggen.\nWaart w.e.g. $1 ier Dir et nach eng Kéier probéiert.",
+       "botpasswords": "Botpasswierder",
+       "botpasswords-disabled": "Botpasswierder sinn desaktivéiert.",
+       "botpasswords-no-central-id": "Fir Botpasswierder ze benotze musst Dir mat engem zentraliséierte Benotzerkont ageloggt sidd.",
+       "botpasswords-existing": "Aktuell Botpasswierder.",
+       "botpasswords-editexisting": "E Botpasswuert änneren",
+       "botpasswords-label-appid": "Numm vum Bot:",
+       "botpasswords-label-create": "Uleeën",
+       "botpasswords-label-update": "Aktualiséieren",
+       "botpasswords-label-cancel": "Ofbriechen",
+       "botpasswords-label-delete": "Läschen",
+       "botpasswords-label-resetpassword": "D'Passwuert zrécksetzen",
+       "botpasswords-label-grants-column": "Accordéiert",
+       "botpasswords-bad-appid": "Den Numm vum Bot \"$1\" ass net valabel.",
+       "botpasswords-updated-title": "Botpasswuert aktualiséiert",
+       "botpasswords-updated-body": "D'Botpasswuert \"$1\" gouf aktualiséiert.",
+       "botpasswords-deleted-title": "Botpasswuert geläscht",
+       "botpasswords-deleted-body": "D'Botpasswuert \"$1\" gouf geläscht.",
+       "botpasswords-not-exist": "De Benotzer \"$1\" huet kee Botpasswuert mam Numm \"$2\".",
        "resetpass_forbidden": "Passwierder kënnen net geännert ginn.",
        "resetpass-no-info": "Dir musst ageloggt sinn, fir direkt op dës Säit ze kommen.",
        "resetpass-submit-loggedin": "Passwuert änneren",
        "passwordreset-emailtext-ip": "Iergendee mat der IP-Adress $1, wahrscheinlech Dir selwer, huet d'Zrécksetze vun Ärem Passwuert op {{SITENAME}} gefrot ($4). {{PLURAL:$3|De Benotzerkont ass|D'Benutzerkonte si}} mat dëser E-Mail-Adress verbonn:\n\n$2\n\n{{PLURAL:$3|Dëst temporärt Passwuert leeft|Dës temporär Passwierder lafe}} bannent {{PLURAL:$5|engem Dag|$5 Deeg}} of.\nDir sollt Iech aloggen an een neit Passwuert festleeën. Wann een Aneren déi Ufro gemaach huet oder Dir Iech erëm un Äert Passwuert erënnere kënnt an et net ännere wëllt, kënnt Dir dës Noriicht ignoréieren an Äert aalt Passwuert weider benotzen.",
        "passwordreset-emailtext-user": "De Benotzer $1 vu(n) {{SITENAME}} huet d'Zrécksetze vun Ärem Passwuert op {{SITENAME}} gefrot ($4). {{PLURAL:$3|De Benotzerkont|D'Benutzerkonte}} \n\n$2\n\n{{PLURAL:$3|ass|si}} mat dëser E-Mail-Adress verbonn.\n\n{{PLURAL:$3|Dëst temporärt Passwuert leeft|Dës temporär Passwierder lafe}} bannent {{PLURAL:$5|engem Dag|$5 Deeg}} of.\nDir sollt Iech aloggen an een neit Passwuert festleeën. Wann een Aneren déi Ufro gemaach huet oder Dir Iech erëm un Äert Passwuert erënnere kënnt an et net ännere wëllt, kënnt Dir dës Noriicht ignoréieren an Äert aalt Passwuert weider benotzen.",
        "passwordreset-emailelement": "Benotzernumm: \n$1\n\nTemporärt Passwuert: \n$2",
-       "passwordreset-emailsentemail": "Wann dëst eng registréiert E-Mailadress vun Ärem benotzerkont ass da gëtt Eng E-Mail fir d'Passwuert zréckzesetze geschéckt.",
+       "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",
        "changeemail": "E-Mail-Adress änneren oder ewechhuelen",
        "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": "'''Är Ännerung konnt net gespäichert gi well d'Date vun Ärer Sessioun verluergaange sinn.\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": "<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_html": "<strong>Är Ännerung konnt net gespäichert gi well d'Date vun Ärer Sessioun verluergaange sinn.</strong>\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.\nWann de Problem dann ëmmer nach bestoe sollt, versicht Iech [[Special:UserLogout|auszeloggen]] an dann erëm anzeloggen.</strong>",
        "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.'''",
        "userrights": "Benotzerrechterverwaltung",
        "userrights-lookup-user": "Benotzergruppe verwalten",
        "userrights-user-editname": "Benotzernumm uginn:",
-       "editusergroup": "Benotzergruppen änneren",
+       "editusergroup": "{{GENDER:$1|Benotzer}}gruppen änneren",
        "editinguser": "Ännere vun de Rechter vum  {{GENDER:$1|Benotzer}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Benotzergruppen änneren",
-       "saveusergroups": "Benotzergruppe späicheren",
+       "saveusergroups": "{{GENDER:$1|Benotzer}}gruppe späicheren",
        "userrights-groupsmember": "Member vun:",
        "userrights-groupsmember-auto": "Implizit Member vun:",
        "userrights-groups-help": "Dir kënnt d'Gruppen zu deenen dëse Benotzer gehéiert änneren.\n* Een ugekräizt Haische bedeit, datt de Benotzer Member vun dëser Grupp ass.\n* Een net ugekräizt Haische bedeit, datt de Benotzer net Member vun dëser Grupp ass.\n* E Stäerchen (*) bedeit datt Dir d'Grupp net méi ewechhuele kënnt wann e bis eemol derbäigesat ass oder gouf.",
        "right-blockemail": "E Benotzer späre sou datt hie keng Maile verschécke kann",
        "right-hideuser": "E Benotzernumm spären, an deem e virun der Ëffentlechkeet verstoppt gëtt",
        "right-ipblock-exempt": "Ausname vun IP-Spären, automatesche Spären a vu Späre vu Plage vun IPen",
-       "right-proxyunbannable": "Automatesche Proxyspären ëmgoen",
        "right-unblockself": "Seng eege Spär ophiewen",
        "right-protect": "Protektiounsniveauen änneren a kaskadegespaart Säiten änneren",
        "right-editprotected": "Protegéiert Säiten als \"{{int:protect-level-sysop}}\" änneren",
        "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",
        "right-passwordreset": "Maile vum Zrécksetze vum Passwuert weisen",
+       "grant-group-page-interaction": "Mat Säiten interagéieren",
+       "grant-group-watchlist-interaction": "Mat Ärer Iwwerwaachungslëscht interagéieren",
+       "grant-group-email": "E-Mail schécken",
+       "grant-group-high-volume": "Massenaktivitéiten ausféieren",
+       "grant-group-customization": "Upassungen an Astellungen",
+       "grant-group-administration": "Administrativ Aktioune maachen",
+       "grant-group-other": "Verschidden Aktivitéiten",
+       "grant-blockusers": "Benotzer spären an hir Spär ophiewen",
+       "grant-createaccount": "Benotzerkonten opmaachen",
+       "grant-createeditmovepage": "Säiten uleeën, änneren a réckelen",
+       "grant-delete": "Säiten, Versiounen a Rubriken a Logbicher läschen",
+       "grant-editinterface": "MediaWiki-Nummraum a Benotzer CSS/JS änneren",
+       "grant-editmycssjs": "Ären eegene Benotzer CSS/JavaScript änneren",
+       "grant-editmyoptions": "Ännert Är Benotzerastellungen",
+       "grant-editmywatchlist": "Ännert Är Iwwerwaachungslëscht",
+       "grant-editpage": "Säiten déi et gëtt änneren",
+       "grant-editprotected": "Gespaart Säiten änneren",
+       "grant-oversight": "Benotzer verstoppen a Versioune läschen",
+       "grant-patrol": "Ännerungen op Säiten iwwerwaachen",
+       "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",
+       "grant-uploadeditmovefile": "Fichieren eroplueden, ersetzen a réckelen",
+       "grant-uploadfile": "Nei Fichieren eroplueden",
+       "grant-basic": "Basisrechter",
+       "grant-viewdeleted": "Geläscht Fichieren a Säite weisen",
+       "grant-viewmywatchlist": "Kuckt Är Iwwerwaachungslëscht",
        "newuserlogpage": "Logbuch vun den neien Umeldungen",
        "newuserlogpagetext": "Dëst ass d'Lescht vun de Benotzernimm déi ugeluecht goufen.",
        "rightslog": "Logbuch vun de Benotzerrechter",
        "foreign-structured-upload-form-label-own-work": "Dëst ass mäin eegent Wierk",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorien",
        "foreign-structured-upload-form-label-infoform-date": "Datum",
+       "foreign-structured-upload-form-3-label-question-website": "Hutt Dir dëst Bild vun engem Internetsite erofgelueden, oder beim Sichen no engem Bild fonnt?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Hutt Dir dëst Bild (Foto, Zeechnung, asw.) selwer gemaacht?",
        "foreign-structured-upload-form-3-label-yes": "Jo",
        "foreign-structured-upload-form-3-label-no": "Neen",
        "backend-fail-stream": "De Fichier $1 konnt net iwwerdroe ginn.",
        "logempty": "Näischt fonnt.",
        "log-title-wildcard": "Titel fänkt mat dësem Text un",
        "showhideselectedlogentries": "Déi erausgesichte Entréeën am Logbuch weisen/verstoppen",
+       "checkbox-select": "Eraussichen:$1",
+       "checkbox-all": "All",
+       "checkbox-none": "Keen",
+       "checkbox-invert": "Ëmdréinen",
        "allpages": "All Säiten",
        "nextpage": "Nächst Säit ($1)",
        "prevpage": "Säit viru(n) ($1)",
        "listgrouprights-namespaceprotection-header": "Limitatioune vum Nummraum",
        "listgrouprights-namespaceprotection-namespace": "Nummraum",
        "listgrouprights-namespaceprotection-restrictedto": "Recht(er), déi dem Benotzer d'Änneren erlaben",
+       "listgrants-rights": "Rechter",
        "trackingcategories": "Tracking-Kategorien",
        "trackingcategories-msg": "Tracking-Kategorie",
        "trackingcategories-name": "Numm vum Message",
        "wlshowhideanons": "anonym Benotzer",
        "wlshowhidepatr": "iwwerwaacht Ännerungen",
        "wlshowhidemine": "meng Ännerungen",
+       "wlshowhidecategorization": "Kategorisatioun vu Säiten",
        "watchlist-options": "Optioune vun der Iwwerwaachungslëscht",
        "watching": "Iwwerwaachen …",
        "unwatching": "Net méi iwwerwaachen …",
        "unblock": "D'Spär vum Benotzer ophiewen",
        "blockip": "{{GENDER:$1|Benotzer}} spären",
        "blockip-legend": "Benotzer spären",
-       "blockiptext": "Benotzt dëse Formulaire fir eng spezifesch IP-Adress oder e Benotzernumm ze spären. Dëst soll nëmmen am Fall vu Vandalismus gemaach ginn, en accordance mat den [[{{MediaWiki:Policy-url}}|interne Richlinen]]. Gitt e spezifesche Grond un (zum Beispill Säite wou Vandalismus virgefall ass).",
+       "blockiptext": "Benotzt dëse Formulaire fir eng spezifesch IP-Adress oder e Benotzernumm ze spären.\nDëst soll nëmmen am Fall vu Vandalismus gemaach ginn, en accordance mat den [[{{MediaWiki:Policy-url}}|interne Richlinen]].\nGitt e spezifesche Grond un (zum Beispill Säite wou Vandalismus virgefall ass).\nDir kënnt IP-Beräicher spären an deem Dir d' [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] Syntax benotzt; de gréissten erlaabte Beräich as  /$1 fir IPv4 an /$2 fir IPv6",
        "ipaddressorusername": "IP-Adress oder Benotzernumm:",
        "ipbexpiry": "Gültegkeet:",
        "ipbreason": "Grond:",
        "export-download": "Als XML-Datei späicheren",
        "export-templates": "Inklusiv Schablounen",
        "export-pagelinks": "Verlinkte Säiten mat exportéieren, bis zu enger Déift vun:",
+       "export-manual": "Säite manuell derbäisetzen:",
        "allmessages": "All Systemmessagen",
        "allmessagesname": "Numm",
        "allmessagesdefault": "Standardtext",
        "javascripttest-pagetext-frameworks": "Sicht w.e.g. eng vun dësen Test-Strukturen eraus: $1",
        "javascripttest-pagetext-skins": "Sicht en Interface (skin) eraus fir d'Tester ze maachen:",
        "javascripttest-qunit-intro": "Kuckt d'[$1 Dokumentatioun vun den Tester] op mediawiki.org",
-       "tooltip-pt-userpage": "Är Benotzersäit",
+       "tooltip-pt-userpage": "{{GENDER:|Är Benotzer}}säit",
        "tooltip-pt-anonuserpage": "Benotzersäit vun der IP-Adress vun där aus Dir den Ament Ännerunge maachtt",
-       "tooltip-pt-mytalk": "Är Diskussiounssäit",
+       "tooltip-pt-mytalk": "{{GENDER:|Är}} Diskussiounssäit",
        "tooltip-pt-anontalk": "Diskussioun iwwer d'Ännerungen déi vun dëser IP-Adress aus gemaach gi sinn",
-       "tooltip-pt-preferences": "Meng Astellungen",
+       "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 Äre Kontributiounen",
+       "tooltip-pt-mycontris": "Lëscht vun {{GENDER:|Äre}} Kontributiounen",
        "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",
        "tooltip-t-recentchangeslinked": "Rezent Ännerungen op Säiten, déi von hei verlinkt sinn",
        "tooltip-feed-rss": "RSS-Feed fir dës Säit",
        "tooltip-feed-atom": "Atom-Feed fir dës Säit",
-       "tooltip-t-contributions": "Lëscht vun de Kontributioune vun dësem Benotzer",
-       "tooltip-t-emailuser": "Dësem Benotzer eng E-Mail schécken",
+       "tooltip-t-contributions": "Lëscht vun de Kontributioune vun dësem {{GENDER:$1|Benotzer}}",
+       "tooltip-t-emailuser": "{{GENDER:$1|Dësem Benotzer}} eng E-Mail schécken",
        "tooltip-t-info": "Méi Informatiounen iwwer dës Säit",
        "tooltip-t-upload": "Biller oder Mediefichieren eroplueden",
        "tooltip-t-specialpages": "Lëscht vun alle Spezialsäiten",
        "pageinfo-category-files": "Zuel vun de Fichieren",
        "markaspatrolleddiff": "Als nogekuckt markéieren",
        "markaspatrolledtext": "Dës Säit als nogekuckt markéieren",
+       "markaspatrolledtext-file": "Dës Versioun vum Fichier als nogekuckt markéieren",
        "markedaspatrolled": "ass als nogekuckt markéiert",
        "markedaspatrolledtext": "Déi gewielt Versioun vu(n) [[:$1]] gouf als nogekuckt markéiert.",
        "rcpatroldisabled": "Rezent Ännerungskontroll ausgeschalt.",
        "exif-compression-1": "Onkompriméiert",
        "exif-copyrighted-true": "Duerch Copyright geschützt",
        "exif-copyrighted-false": "Copyright status net agestallt",
+       "exif-photometricinterpretation-1": "Schwaarz a wäiss (Schwaarz ass 0)",
        "exif-unknowndate": "Onbekannten Datum",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Horizontal gedréit",
        "scarytranscludefailed-httpstatus": "[D'Opruffe vun der Schabloun $1: HTTP $2 huet net funktionéiert]",
        "scarytranscludetoolong": "[D'URL ass ze laang]",
        "deletedwhileediting": "<strong>Opgepasst:</strong>Dës Säit gouf geläscht nodeem datt Dir ugefaang hutt se z'änneren!",
-       "confirmrecreate": "De Benotzer [[User:$1|$1]] ([[User talk:$1|Diskussioun]]) huet dës Säit geläscht, nodeem s datt där ugefaangen hutt drun ze schaffen. D'Begrënnung war: ''$2'' Bestätegt w.e.g., datt Dir dës Säit wierklech erëm nei opmaache wëllt.",
-       "confirmrecreate-noreason": "De Benotzer [[User:$1|$1]] ([[User talk:$1|Diskussioun]]) huet dës Säit geläscht, nodeem s datt Dir ugefaangen hutt drun ze schaffen. Confirméiert w.e.g., datt Dir dës Säit wierklech erëm nei opmaache wëllt.",
+       "confirmrecreate": "De Benotzer [[User:$1|$1]] ([[User talk:$1|Diskussioun]]) huet dës Säit {{GENDER:$1|geläscht}}, nodeems datt Dir ugefaangen hutt drun ze schaffen. D'Begrënnung war: <em>$2</em>\nBestätegt w.e.g., datt Dir dës Säit wierklech erëm nei opmaache wëllt.",
+       "confirmrecreate-noreason": "De Benotzer [[User:$1|$1]] ([[User talk:$1|Diskussioun]]) huet dës Säit {{GENDER:$1|geläscht}}, nodeems datt Dir ugefaangen hutt drun ze schaffen. Confirméiert w.e.g., datt Dir dës Säit wierklech erëm nei opmaache wëllt.",
        "recreate": "Nees uleeën",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Dës Säit aus dem Server-Cache läschen?",
        "watchlisttools-edit": "Iwwerwaachungslëscht weisen an änneren",
        "watchlisttools-raw": "Net-formatéiert Iwwerwaachungslëscht änneren",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Diskussioun]])",
+       "timezone-local": "Lokal",
        "duplicate-defaultsort": "'''Opgepasst:''' Den Zortéierschlëssel \"$2\" iwwerschreift de virege Standard-Zortéierschlëssel \"$1\".",
        "duplicate-displaytitle": "<strong>Opgepasst:</strong> Den Titel dee gewise gëtt \"$2\" iwwerschreift deen Titel dee virdru gewise gouf \"$1\".",
        "version": "Versioun",
        "version-libraries-license": "Lizenz",
        "version-libraries-description": "Beschreiwung",
        "version-libraries-authors": "Auteuren",
-       "redirect": "Viruleedung duerch e Fichier, e Benotzer, eng Säit oder eng Versiouns-ID",
+       "redirect": "Viruleedung duerch e Fichier, e Benotzer, eng Säit, eng Versiouns-ID oder eng Logbuch-ID",
        "redirect-legend": "Viruleedung op ee Fichier oder eng Säit",
-       "redirect-summary": "Dës Spezialsäit ass eng Viruleedung op e Fichier (Fichiersnumm uginn), eng Säit (Versiounsnummer uginn) oder eng Benotzersäit (numeresch Benotzeridentifikatioun uginn).\nGebrauch: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], oder [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Dës Spezialsäit ass eng Viruleedung op e Fichier (Fichiersnumm uginn), eng Säit (Versiounsnummer uginn), eng Benotzersäit (numeresch Benotzeridentifikatiounsnummer uginn) oder eng Entrée an engem Logbuch (vum Logbuch mat der ID).\nGebrauch:\n[[{{#Special:Redirect}}/file/Example.jpg]],\n[[{{#Special:Redirect}}/page/64308]],\n[[{{#Special:Redirect}}/revision/328429]],\n[[{{#Special:Redirect}}/user/101]], oder\n[[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Lass",
        "redirect-lookup": "Nosichen:",
        "redirect-value": "Wäert:",
        "expand_templates_generate_xml": "Weis d'Struktur vum XML",
        "expand_templates_generate_rawhtml": "HTML-Format weisen",
        "expand_templates_preview": "Kucken ouni ofzespäicheren",
+       "expand_templates_input_missing": "Dir musst mindestens een Text aginn.",
        "pagelanguage": "Eraussiche vun der Sprooch vun der Säit",
        "pagelang-name": "Säit",
        "pagelang-language": "Sprooch",
        "pagelang-use-default": "Standard-Sprooch benotzen",
        "pagelang-select-lang": "Sprooch eraussichen",
+       "pagelang-submit": "Späicheren",
        "right-pagelang": "Sprooch vun der Säit änneren",
        "action-pagelang": "d'Sprooch vun der Säit änneren",
        "log-name-pagelang": "Log vum Ännere vun der Sprooch",
        "mediastatistics": "Statistike vun de Medien",
        "mediastatistics-summary": "Statistike vun den Type vun den eropgeluedene Fichieren. Dobäi gëtt nëmmen déi lescht Versioun vun engem Fichier gezielt, al oder geläscht Versioune vu Fichiere sinn ausgeschloss.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 Byte|$1 Byten}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Gesamtgréisst vun de Fichiere vun dësem Abschnitt:  {{PLURAL:$1|$1 Byte|$1 Bytes}} ($2; $3%).",
        "mediastatistics-table-mimetype": "MIME-Typ",
        "mediastatistics-table-extensions": "Méiglech Erweiderungen",
        "mediastatistics-table-count": "Zuel vun de Fichieren",
        "mediastatistics-header-office": "Office",
        "mediastatistics-header-text": "Textuell",
        "mediastatistics-header-archive": "Kompriméiert Formater",
+       "mediastatistics-header-total": "All Fichieren",
        "json-error-unknown": "Et gouf e Problem mam JSON. Feeler: $1",
        "json-error-state-mismatch": "JSON den net valabel ass oder Feeler huet",
        "json-error-syntax": "Syntaxfeeler",
        "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."
+       "api-error-blacklisted": "Sicht w.e.g. en aneren Titel, dee méi iwwer de Sujet ausseet.",
+       "sessionprovider-generic": "$1-Sessiounen",
+       "randomrootpage": "Zoufalls-Stammsäit"
 }
index ee7dfd2..c029ed4 100644 (file)
        "october-date": "$1 otobre",
        "november-date": "{{PLURAL:$1|1°|$1}} novembre",
        "december-date": "$1 dexenbre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categorîa|Categorîe}}",
        "category_header": "Pàgine inta categorîa \"$1\"",
        "subcategories": "Sottocategorîe",
        "yournick": "Nomiagio:",
        "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}}.",
+       "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.",
        "email": "Posta elettronega",
-       "prefs-help-realname": "* Nomme vëo (opsionâ): se o se scellie de scrivilo, o sajà dêuviòu pe ascrivighe a paternitæ di contegnûi inviæ.",
+       "prefs-help-realname": "O veo nomme o l'è facortativo.\nSe fornio, o poeu ese deuviou pe attribuite a paternitæ do to travaggio.",
        "prefs-help-email": "L'email a no l'é obligatöia, ma a te permette de reçéive a paròlla segrétta se ti l'ascòrdi.",
        "prefs-help-email-others": "Ti ti peu scélie ascì de lasciâ che i âtri te contattan via e-mail co-in ingancio da-a to pàgina utente ò de discuscion sénsa rivelâ a to e-mail quande i atri utenti te contattan.",
        "prefs-help-email-required": "L'addresso email o ghe veu.",
        "prefs-tokenwatchlist": "Token",
        "prefs-diffs": "Differençe",
        "prefs-help-prefershttps": "Questa preferença a l'aviâ effetto da-o proscimo accesso.",
+       "prefswarning-warning": "T'hæ fæto de modiffiche a-e teu preferense che no son ancon stæte sarvæ.\nSe ti sciorti da sta paggina sensa sciaccâ \"$1\" e preferense no saian agiornæ.",
+       "prefs-tabs-navigation-hint": "Suggeimento: ti peu deuviâ i pomelli co-a freccia scinistra e drita pe navegâ tra e schede inta lista de schede.",
+       "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",
+       "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",
+       "userrights-groupsmember": "Membro de:",
+       "userrights-groupsmember-auto": "Membro impliçito de:",
        "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.",
        "grouppage-sysop": "{{ns:project}}:Amministratoî",
        "grouppage-bureaucrat": "{{ns:project}}:Buroccrati",
        "grouppage-suppress": "{{ns:project}}:Soppressoî",
+       "right-move": "Mescia e paggine",
        "right-writeapi": "Deuvia l'API in scrittua",
        "newuserlogpage": "Nêuvi utenti",
        "rightslog": "Diritti d'ûtente",
        "minoreditletter": "m",
        "newpageletter": "N",
        "boteditletter": "b",
-       "rc_categories_any": "Quarsevêuggia",
+       "rc_categories_any": "Quâ-se-sæ fra quelle indicæ",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} doppo a modiffica",
-       "rc-enhanced-expand": "Fanni védde detaggi (serve JavaScript)",
+       "newsectionsummary": "/* $1 */ neuva seçion",
+       "rc-enhanced-expand": "Fanni vedde i detaggi",
        "rc-enhanced-hide": "Ascondi detaggi",
+       "rc-old-title": "originaiamente creâ comme \"$1\"",
        "recentchangeslinked": "Cangiamenti correlæ",
        "recentchangeslinked-feed": "Cangiamenti correlæ",
        "recentchangeslinked-toolbox": "Cangiaménti corelæ",
        "recentchangeslinked-summary": "Sta pàgina a fa védde i cangiaménti ciù reçenti a-e pàgine colegæ a questa.\nE pàgine che t'æ in oservaçion inti [[Special:Watchlist|oservæ speciâli]] son in '''grascetto'''.",
        "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 {{PLURAL:$2|una paggina a l'è azonta|$2 paggine son azonte}} a-a categoria",
+       "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",
+       "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",
        "uploadlogpage": "Log di file caregæ",
        "filename": "Nomme do file",
        "filedesc": "Detaggi",
index 9ef193b..362e176 100644 (file)
@@ -8,7 +8,7 @@
                ]
        },
        "tog-underline": ":خط کیشائن ئۀ ژئر پئؤن",
-       "tog-hideminor": "تغÛ\8cÛ\8cرات Ú¯Ø¤Ø¬Ø± Ø¦Ù\87 Ø±Ø²Ú¯-Ù\81ئرست ØªØºÛ\8cرات Ø§Ø®Û\8cر Ø¦Ø¢Ø´Ø§Ø±Û\8cا Ø¨Ù\88Ù\88",
+       "tog-hideminor": "آشاردÙ\86 Ø¯Û\95سکارÛ\8cÛ\95Ù\84 Ú¯Ø¤Ø¬Û\95ر  Ø¥Ú\98 Ú¯Ø¤Û\95Ú\95Û\8cاÙ\84(تغÛ\8cÛ\8cرات) Ø§Û\8cسÛ\95(اخÛ\8cر)",
        "tog-hidepatrolled": "دسکاریۀل گه دیار بینۀ ئژ فئرست-رزگ تغییرات اخیر بشارا",
        "tog-newpageshidepatrolled": "وڵگۀل گه دیار بینۀ ئژ فئرست-رزگ ولگۀل تازۀ بشارا",
        "tog-hidecategorization": "Hide categorization of pages",
@@ -31,7 +31,7 @@
        "tog-enotifminoredits": "ئۀر تغییرۀل-آڵؤنۀل(آلشتۀل)گؤجۀریجی ئۀر وڵگۀل ؤ پرؤندۀلم کریا نامه ئۀرا مه کِل کۀ",
        "tog-enotifrevealaddr": "نیشانی ایمیل مه ئۀر ایمیل‌ل حاوواڵ رۀسن نیشؤن دۀ",
        "tog-shownumberswatching": "گلۀ شؤماری-شؤمار کاربۀل پی‌گیر نیشان دۀ",
-       "tog-oldsig": ":امضائ گه ایسۀ درینئ",
+       "tog-oldsig": ":امضاێ گإ ایسگە درینێ",
        "tog-fancysig": "(امضا چؤی ویکی‌متن بوو(بدون پئؤن خودکار نیائن",
        "tog-uselivepreview": "استفاده از پیش‌نمایش زنده",
        "tog-forceeditsummary": "هۀنئ گه-وختئ که خؤلاصۀ دسکاریم نَنیؤیسائۀ خۀؤۀ رم کۀ",
        "tog-watchlisthidepatrolled": "دسکاریۀل گشت خورده-سئرکریا ئژ فئرست سئرکردن بشآرا",
        "tog-watchlisthidecategorization": "نهفتن رده‌بندی صفحه‌ها",
        "tog-ccmeonemails": "رؤی نؤیسیائ  ئژ نامۀلئگه کِلۀ مِه هۀم کاربرۀل-بهرۀگرۀل ئۀرا ووژم کِل که",
-       "tog-diffonly": "Ù\85حتÙ\88ا Ù\88ةڵگةØ\8c Ø¦Ø© Ú\98ئر ØªÙ\81اÙ\88ت Ø¯Û\8cار Ù\86اÙ\88Ù\88\86Ù\85اÛ\8cØ´ Ù\86دÙ\87د",
+       "tog-diffonly": "Ù\86Û\86Ù\85 Ø¬Ù\90Ú©(Ù\85حتÙ\88ا)Ù\88Û\95ÚµÚ¯Û\95Ø\8c Ø£ Ú\98Û\8eر ØªÙ\81اÙ\88ت Ø¯Û\8cار Ù\86اÙ\88Ù\88\86Ù\85اÛ\8cØ´ Ù\86دÙ\87د)",
        "tog-showhiddencats": "دسۀل-رزگۀل آشاریآ نیشؤن دۀ",
        "tog-norollbackdiff": "دؤما واگردانی تفاوت نیشؤن نه",
-       "tog-useeditwarning": "Ù\88خت Ø¦Ù\87 Ø¯Ø±Ú\86ئÙ\86  Ù\88Ù\84Ú¯Û\80 Ø¯Ø³Ú©Ø§Ø±Û\8c Ø¦Û\80ر Ø¯Ø³Ú©Ø§Ø±Û\8cÛ\80Ù\84 Ø°Ø®Û\8cرÙ\84 Ù\86ؤÛ\8cÙ\85ئ Ø¯Ø§Ø´Øª Ù\87ؤشدارÙ\85Û\80 Ø¨Ø¦Ø¯Û\80",
+       "tog-useeditwarning": "Ù\87Û\95Ù\86Û\8e(زÙ\85اÙ\86Û\8c Ú©Ù\87)Ú¯Ù\90ستÙ\85 Ø¥Ú\98 Ù\88Û\95ÚµÚ¯Û\95 Ø¯Û\95سکارÛ\8c Ø°Ø®Û\8cرÙ\87 Ù\86ؤÙ\8a Ø¨Ù\90Ú\86Ù\85Ø¥ Ø¯Û\95ر.دÛ\95سگÛ\8cرÙ\85 Ú©Û\95",
        "tog-prefershttps": "همؤیشۀ ئۀرا ئۀ نؤم سیستم هۀتن ئژ اتصالۀل امن بهرۀ بگر-استفادۀ کۀ",
        "underline-always": "همؤیشۀ",
        "underline-never": "هؤیچ وخت",
        "october-date": "$1 اکتبر",
        "november-date": "$1 نوامبر",
        "december-date": "$1 دسامبر",
-       "pagecategories": "{{PLURAL:$1|رده|ردۀل}}",
-       "category_header": "\"Ù\88Û\80Ù\84Ú¯Û\80Ù\84  Ø±Ø¯Ù\87Ù\94 \"$1",
-       "subcategories": "Ú\98Û\8cر Ø±Ø¯Ù\87 ل",
-       "category-media-header": "\"Ù¾Û\80رÙ\88Û\80Ù\86دÛ\80Ù\84 Ø±Ø¯Ù\87Ù\94 \"$1",
+       "pagecategories": "{{PLURAL:$1|ڕِزگ|ڕِزگەل}}",
+       "category_header": "\"Ù\88Û\95ÚµÚ¯Û\95Ù\84 Ú\95زگ(ردÙ\87) \"$1",
+       "subcategories": "Ú\98Û\8eر Ú\95زگÛ\95ل",
+       "category-media-header": "\"Ù¾Û\95رÙ\88Û\95Ù\86دÛ\95Ù\84 Ú\95Ù\90زگ \"$1",
        "category-empty": "<em>این رده در حال حاضر حاوی هیچ صفحه یا پرونده‌ای نیست.</em>",
-       "hidden-categories": "{{PLURAL:$1|رده آشاریا|رده ل آشاریا}}",
-       "hidden-category-category": "رده‌های پنهان",
+       "hidden-categories": "{{PLURAL:$1|ڕزگ آشاریا|ڕزگەل آشاریا}}",
+       "hidden-category-category": "ڕزگەل آشاریا",
        "category-subcat-count": "{{PLURAL:$2|این رده تنها حاوی زیرردهٔ زیر است.|{{PLURAL:$1|این زیررده|این $1 زیررده}} در این رده قرار {{PLURAL:$1|دارد|دارند}}؛ این رده در کل حاوی $2 زیررده است.}}",
        "category-subcat-count-limited": "ئئ رده/ڕِزگة شامل {{PLURAL:$1|یک|$1}} ژئر رده زیر است.",
        "category-article-count": "{{PLURAL:$2|این رده فقط دارای صفحهٔ زیر است.|{{PLURAL:$1|این صفحه|این $1 صفحه}} در این رده قرار {{PLURAL:$1|دارد|دارند}}؛ این رده در کل حاوی $2 صفحه است.}}",
        "noindex-category": "صفحه‌های نمایه‌نشده",
        "broken-file-category": "صفحه‌های دارای پیوند خراب به پرونده",
        "about": "دۀربارۀ",
-       "article": "وةڵگة محتوایی",
+       "article": "وەڵگە نۆم چێنە",
        "newwindow": "(واز کردن ئۀر دۀروۀچۀ جدید)",
        "cancel": "ئآهووسانن/لغو",
        "moredotdotdot": "...ویشتر/فرةتر",
        "morenotlisted": "لیست کامل نیۀ",
-       "mypage": "وةڵگة/پەڕە",
-       "mytalk": "قسۀ-گۀپ",
-       "anontalk": "قسۀ-گۀپ",
+       "mypage": "وةڵگە(پەڕە)",
+       "mytalk": "گەپ(قسە)",
+       "anontalk": "گەپ(قسە)",
        "navigation": "ناوبری",
        "and": "&#32;و",
-       "qbfind": "پیاکردن-ئادین",
+       "qbfind": "آدئین(پێاکرن)",
        "qbbrowse": "مِنِی -گۀشتن",
        "qbedit": "دسکاری",
-       "qbpageoptions": "ئئ وةڵگة",
-       "qbmyoptions": "Ù\88Û\80Ù\84Ú¯Û\80Ù\84 Ù\85Ù\90",
+       "qbpageoptions": "ئێ وەڵگە",
+       "qbmyoptions": "Ù\88Û\95ÚµÚ¯Û\95Ù\84 Ù\88Ù\88Ù\90Ú\98Ù\85",
        "faq": "پرسش‌های متداول",
        "faqpage": "Project:پرسش‌های متداول",
        "actions": "کارۀل",
        "returnto": "بازگشت به $1",
        "tagline": "دۀربارۀ {{SITENAME}}",
        "help": "یاری کردن",
-       "search": "گئردین/مهِ نی",
-       "searchbutton": "گئردین/مهِ نی",
+       "search": "مِنِی کِردِن(گێردین)",
+       "searchbutton": "مِنِی کِردِن(گێردین)",
        "go": "بِچۆ",
        "searcharticle": "بِچۆ",
        "history": "تاریخ وةڵگة",
        "print": "چاپ",
        "view": "دیین/سئرکردن",
        "view-foreign": "مشاهده در $1",
-       "edit": "دةسکاری",
+       "edit": "دەسکاری",
        "edit-local": "ویرایش توضیحات محلی",
        "create": "دؤِرس کردن/سازین",
        "create-local": "افزودن توضیحات محلی",
-       "editthispage": "ئئ Ù\88Ø©ÚµÚ¯Û\80 Ø¯Ø©Ø³Ú©Ø§Ø±Û\8c Ú©Ø©ن",
-       "create-this-page": "دؤرِس کردن ئئ وةڵگة",
+       "editthispage": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ø¯Û\95سکارÛ\8c Ú©Û\95ن",
+       "create-this-page": " اێ وەڵگە دؤرِس کە",
        "delete": "حۀذف کردن/پاک کردن",
        "deletethispage": "حذف این صفحه",
-       "undeletethispage": "بازگردانی ئئ وةڵگة",
-       "undelete_short": "احÛ\8cاÛ\8c {{PLURAL:$1|Û\8cØ¥ Ø¯Ø©Ø³Ú©Ø§Ø±Û\8c|$1 Ø¯Ø©سکاری}}",
+       "undeletethispage": "واگردانی(گلآدائن)ئێ وەڵگە",
+       "undelete_short": "زÙ\90Ù\86Û\8e Ø¢Ú©Ø±Ù\86(احÛ\8cا) {{PLURAL:$1|Û\8cÚ¯Ù\84Û\95 Ø¯Û\95سکارÛ\8c|$1 Ø¯Û\95سکاری}}",
        "viewdeleted_short": "نمایش {{PLURAL:$1|یک ویرایش حذف‌شده|$1 ویرایش حذف‌شده}}",
        "protect": "پروژۀ",
-       "protect_change": "تغییر/آلِشت",
-       "protectthispage": "Ù\85حاÙ\81ظت Ø¥Ú\98 Ø¦Ø¦ Ù\88ةڵگة",
-       "unprotect": "تغییر محافظت",
-       "unprotectthispage": "تغییر محافظت ئئ وةڵگة",
+       "protect_change": "گؤەڕانن/تغییر",
+       "protectthispage": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø§Û\8e Ù\88Û\95ÚµÚ¯Û\95",
+       "unprotect": "پڵۆم کردن بگؤەڕِن(تغییر ده)",
+       "unprotectthispage": "گؤەڕانن(تغییر)پڵۆم کردن اێ وەڵگە",
        "newpage": "وةڵگة  تازۀ",
-       "talkpage": "گةپ دةربارة ئئ وةڵگة",
-       "talkpagelinktext": "قسۀ-گۀپ",
+       "talkpage": "دەربارە ئێ وەڵگە گەپ بووشن",
+       "talkpagelinktext": "گەپ(قسە)",
        "specialpage": "وةڵگة/پةرة  ویژة",
-       "personaltools": "ابزارÛ\80Ù\84 Ù\88Ù\88Ú\98Û\8c/شخصÛ\8c",
+       "personaltools": "ابزارÛ\95Ù\84 Ù\88Ù\88Ú\98Û\8c(شخصÛ\8c)",
        "articlepage": "نمایش مةقاڵة",
        "talk": "قسۀل-گۀپۀل",
        "views": "دیین/سئرکردن",
-       "toolbox": "ابزارÛ\80ل",
+       "toolbox": "ابزارÛ\95ل",
        "userpage": "وةڵگة کاربۀر بؤین",
        "projectpage": "وةڵگة پروژۀ بوین",
        "imagepage": "وةڵگة پرونده بؤین",
        "mediawikipage": "نمایش وةڵگة پیغام",
        "templatepage": "نمایش وةڵگة الگو",
        "viewhelppage": "نمایش وةڵگة هؤمیاری",
-       "categorypage": "Ù\86Ù\85اÛ\8cØ´ Ù\88ةڵگة Ø±Ø¯Ù\87\95Ù\90زگ",
+       "categorypage": "Ù\88Û\95ÚµÚ¯Û\95 Ú\95Ù\90زگ Ø¨Û\86Û\8cÙ\86",
        "viewtalkpage": "نمایش وةڵگة  گةپ ؤ گفت",
        "otherlanguages": "وۀ زوونۀلئ تر",
        "redirectedfrom": "(تغییرمسیر إژ $1)",
        "redirectto": ":تغییر مسیر به",
        "lastmodifiedat": ".ئئ وۀلگۀآخرین گِل ئۀ $1 سات $2 تۀغیر گِرتیۀسئ",
        "viewcount": "إژ ئئ وةڵگة  {{PLURAL:$1|یإ گِل|$1$1چةن گِل}} بازدید بیة.",
-       "protectedpage": "وةڵگة محافظت/پڵؤم بیة",
+       "protectedpage": "وەڵگە پڵۆم بیە",
        "jumpto": ":وازآ کردن/پریدن وۀ",
        "jumptonavigation": "ناوبری",
-       "jumptosearch": "گئردین/مهِ نی",
+       "jumptosearch": "مِنِی کِردِن(گێردین)",
        "view-pool-error": "متأسفانه سرورها در حال حاضر دچار بار اضافی هستند.\nتعداد زیادی از کاربران دارند تلاش می‌کنند که این صفحه را ببینند.\nلطفاً قبل از تلاش دوباره برای دیدن این صفحه مدتی صبر کنید.\n\n$1",
        "generic-pool-error": "متأسفانه سرورها در حال حاضر دچار بار اضافی هستند.\nتعداد زیادی از کاربران دارند تلاش می‌کنند که این صفحه را ببینند.\nلطفاً قبل از تلاش دوباره برای دیدن این صفحه مدتی صبر کنید.",
        "pool-timeout": "اتمام مهلت انتظار برای قفل",
        "aboutpage": "Project:درباره",
        "copyright": " محتوایۀل هانإ ژئرنظر اجازه‌نامهٔ $1 مۀگۀر یۀگإ خلاف یۀ بوشرئ/ذکر بو",
        "copyrightpage": "{{ns:project}}:حق تکثیر",
-       "currentevents": "رویدادۀل ایسۀ",
+       "currentevents": "پێش هەتێەل ایسگە",
        "currentevents-url": "Project:رویدادةل ایسة",
        "disclaimers": "دروو نامه -تکذیب نامه",
        "disclaimerpage": "Project:تکذیب‌نامهٔ عمومی",
        "editsection": "دةسکاری",
        "editold": "دةسکاری",
        "viewsourceold": "سئرکردن بِنچۀک/مۀنبۀع",
-       "editlink": "دةسکاری",
+       "editlink": "دەسکاری",
        "viewsourcelink": "سئرکردن بِنچۀک/مۀنبۀع",
        "editsectionhint": "دۀسکاری بۀخش: $1",
        "toc": "محتویات",
        "site-atom-feed": " $1 أرا atom هاوول خوۀن",
        "page-rss-feed": "خوراک آراس‌اس برای «$1»",
        "page-atom-feed": " $1 أرا atom هاوول خوۀن",
-       "red-link-title": "$1(ئئ وۀلگۀ نیۀسئ)",
+       "red-link-title": "$1(هانە وەڵگەئ نیە)",
        "sort-descending": "مرتب‌سازی نزولی",
        "sort-ascending": "مرتب‌سازی صعودی",
-       "nstab-main": "وةڵگة/پەڕە",
-       "nstab-user": "Ù\88Û\80Ù\84Ú¯Û\80 Ú©Ø§Ø±Ø¨Û\80ر",
-       "nstab-media": "وةڵگة رسانه",
-       "nstab-special": "Ù\88Û\80Ù\84Ú¯Û\80/Ù¾Û\80رÛ\80 Ù\88Û\8cÚ\98Û\80",
-       "nstab-project": "Ù\88Û\80Ù\84Ú¯Û\80 Ù¾Ø±Ù\88Ú\98Û\80",
+       "nstab-main": "وةڵگە(پەڕە)",
+       "nstab-user": "Ù\88Û\95ÚµÚ¯Û\95 Ú©Ø§Ø±Ø¨Û\95ر",
+       "nstab-media": "وەڵگە رسانه",
+       "nstab-special": "Ù\88Û\95ÚµÚ¯Û\95(Ù¾Û\95Ú\95Û\95\88Û\8cÚ\98Û\95",
+       "nstab-project": "Ù\88Û\95ÚµÚ¯Û\95 Ù¾Ø±Ù\88Ú\98Ù\87",
        "nstab-image": "فایل",
        "nstab-mediawiki": "پیغام",
        "nstab-template": "نمونه",
-       "nstab-help": "Ù\88Û\80Ù\84Ú¯Û\80Ù\84 Ø±Ø§Ù\87Ù\86Ù\85ا",
-       "nstab-category": "رِزگ -دۀسۀ",
+       "nstab-help": "Ù\88Û\95ÚµÚ¯Û\95Ù\84 Û\8cارÛ\8c Ú©Û\95ر(راÙ\87Ù\86Ù\85ا)",
+       "nstab-category": "ڕِزگ",
        "mainpage-nstab": "سەر پەڕە",
        "nosuchaction": "چنین عملی وجود نئرێ",
        "nosuchactiontext": "عمل مشخص‌شده در نشانی اینترنتی نامجاز است.\nممکن است نشانی اینترنتی را اشتباه وارد کرده باشید یا پیوند مشکل‌داری را دنبال کرده باشید.\nهمچنین ممکن است ایرادی در نرم‌افزار استفاده‌شده در {{SITENAME}} وجود داشته باشد.",
        "databaseerror-query": "پرس‌ و جو: $1",
        "databaseerror-function": "تابع: $1",
        "databaseerror-error": "خطا: $1",
-       "transaction-duration-limit-exceeded": "In order 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, trying doing multiple smaller operations instead.",
+       "transaction-duration-limit-exceeded": "برای پرهیز از ایجاد تاخیر بالا در نسخه‌برداری، این تراکنش لغو شد چرا که مدت زمان نوشتن ($1) از حد $2 {{PLURAL:$2|ثانیه|ثانیه ها}} بیشتر بود.\nاگر در حال تغییر چیزهای زیادی به طور همزمان هستید، سعی کنید به جایش چند عمل را در گروه‌های کوچکتر انجام بدهید.",
        "laggedslavemode": "'''هشدار:''' صفحه ممکن است به‌روزرسانی‌های اخیر را شامل نشود.",
        "readonly": "پایگاه داده قفل بیة",
        "enterlockreason": "دلیلی برای قفل کردن ذکر کنید، که حاوی تقریبی از زمانی باشد که قفل برداشته خواهد شد",
        "mypreferencesprotected": "شما دارای مجوز ویرایش تنظیمات خود نیستید.",
        "ns-specialprotected": ".وةڵگةل ویژه غیر قابل دةسکاریِن",
        "titleprotected": "این عنوان توسط [[User:$1|$1]] در برابر ایجاد محافظت شده‌است.\nدلیل ارائه‌شده این است: «''$2''».",
-       "filereadonlyerror": "تغییر پرونده «$1» ممکن نیست چون مخزن پرونده «$2» در حالت فقط خواندنی قرار دارد.\n\nمدیری که آن را قفل کرده چنین توضیحی را ذکر کرده:  «$3».",
+       "filereadonlyerror": "تغییر پروندهٔ «$1» ممکن نیست چون مخزن پروندهٔ «$2» در حالت فقط خواندنی قرار دارد.\n\nمدیری که آن را قفل کرده چنین توضیحی را ذکر کرده:  «$3».",
        "invalidtitle-knownnamespace": "عنوان نامعتبر با فضای نام «$2» و متن «$3»",
        "invalidtitle-unknownnamespace": "عنوان نامعتبر با فضای نام ناشناختهٔ شمارهٔ $1 و متن «$2»",
        "exception-nologin": "وارد سیستم نؤینۀ",
        "virus-unknownscanner": ":ضدویروس ناشناخته",
        "logouttext": "'''ایسة دةر چئن هؤمة ثبت بیة.'''\nتوجه داشته باشید که تا حافظهٔ نهان مرورگرتان را پاک نکنید، بعضی از صفحات ممکن است همچنان به گونه‌ای نمایش یابند که انگار وارد شده‌اید.",
        "welcomeuser": "خؤةش هةتینة/هاتینة $1!",
-       "welcomecreation-msg": "حساوو کاربری هؤمة دؤرس بی.\nفراموش نکنید که [[Special:Preferences|ترجیحات {{SITENAME}}]] خود را تغییر دهید.",
-       "yourname": ":نؤم کاربری",
+       "welcomecreation-msg": "حساوو کاربری هۆمە دؤرس بی.\nویرتان نەچوو(فراموش نشە) گإ [[Special:Preferences|تمارزووەل(ترجیحات) {{SITENAME}}]] ووِژت بگؤەڕنین( تغییر دهی).",
+       "yourname": ":نۆم کاربەری",
        "userlogin-yourname": "نؤم بهرۀگر-کاربر",
-       "userlogin-yourname-ph": "نام کاربۀری تؤن وارد کۀن",
+       "userlogin-yourname-ph": "نۆم کاربەری ووژت وارد کە",
        "createacct-another-username-ph": "نام کاربۀری تؤن وارد کۀن",
        "yourpassword": ":رمز عبور",
        "userlogin-yourpassword": "رۀمز",
-       "userlogin-yourpassword-ph": "Û\8cÚ© Ú¯Ø°Ø±Ù\88اÚ\98Ù\87 Ù\88ارد Ú©Ù\86Û\8cد",
+       "userlogin-yourpassword-ph": "Û\8cÚ¯Ù\84Û\95 Ø±Û\95Ù\85ز Ø¨Ù\86Û\86Û\8cس",
        "createacct-yourpassword-ph": "یک گذرواژه وارد کنید",
        "yourpasswordagain": "تکرار گذرواژه:",
        "createacct-yourpasswordagain": "گذرواژه را دوباره وارد کنید",
        "yourdomainname": ":دامنهٔ شما",
        "password-change-forbidden": ".شما نمی‌توانید گذرواژه‌ها را در این ویکی تغییر دهید",
        "externaldberror": "خطایی در ارتباط با پایگاه داده رخ داده است یا اینکه شما اجازهٔ به‌روزرسانی حساب خارجی خود را ندارید.",
-       "login": "نؤم هۀتن سیستم",
+       "login": "إ نۆم هەتن سیستم",
        "nav-login-createaccount": " إ نؤم هةتن سیستم/ حساوو کاربةری سازین",
        "userlogin": " إ نؤم هةتن سیستم/ حساوو کاربةری سازین",
        "userloginnocreate": "نؤم هۀتن سیستم",
-       "logout": "دةرچئن/خروج",
-       "userlogout": "دةرچئن/خروج",
+       "logout": "دەرچێن|خروج",
+       "userlogout": "دەرچێن|خروج",
        "notloggedin": "وارد سیستم نؤینۀ",
        "userlogin-noaccount": "حساوو کاربۀری نرین؟",
        "userlogin-joinproject": "{{SITENAME}}نام نؤیسی کۀن",
        "nologinlink": "حساووئ أرا ووژتان بِسازِن",
        "createaccount": "حساووئ أرا ووژتان بِسازِن",
        "gotaccount": "حساوو کاربۀری دِرین؟$1",
-       "gotaccountlink": "نؤم هۀتن سیستم",
-       "userlogin-resetlink": "جزئیات ورود إ ویرتان/یادتان چئة؟",
+       "gotaccountlink": "إ نۆم هەتن سیستم",
+       "userlogin-resetlink": "جزئیات ورودتە ویر(یاد)چێە؟",
        "userlogin-resetpassword-link": "رۀمزۀتان  ویر/ یاد  چئۀ؟",
        "userlogin-helplink2": "کمک با ورود",
        "userlogin-loggedin": "شما در حال حاضر به عنوان {{GENDER:$1|$1}} وارد شده‌اید.\nاز فرم پایین برای ورود به عنوان یک کاربر دیگر استفاده کنید.",
        "userlogin-createanother": "حساووئ کاربةری تِر بِسازِن",
        "createacct-emailrequired": "نیشانی ایمیل",
        "createacct-emailoptional": ")نشانی ایمیل (اختیاری",
-       "createacct-email-ph": "نیشانی ایمیل ووژتان بنؤیسِن",
+       "createacct-email-ph": "نیشانی ایمیل ووژت بنۆیس",
        "createacct-another-email-ph": "نیشانی ایمیل ووژتان بنؤیسِن",
        "createaccountmail": "استفاده از رمز عبور موقت تصادفی و ارسال آن به آدرس ایمیل مشخص شده",
        "createacct-realname": "*نام راسکانی/واقعی *دل بخواهی",
        "createaccountreason": ":دةلیل",
        "createacct-reason": "دةلیل",
        "createacct-reason-ph": "ئةرا حساووێ  تر مةسازین؟",
-       "createacct-submit": "حساووئ أرا ووژتان بِسازِن",
+       "createacct-submit": "حساووێ أڕا ووژت بِساز",
        "createacct-another-submit": "حساووئ أرا ووژتان بِسازِن",
        "createacct-benefit-heading": "{{SITENAME}} is made by people like you.",
        "createacct-benefit-body1": "{{PLURAL:$1|دۀسکاری|دۀسکاریۀل}}",
-       "createacct-benefit-body2": "{{PLURAL:$1|Ù\88Û\80Ù\84Ú¯Û\80\88Û\80Ù\84Ú¯Û\80ل}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|Ù\88Û\95ÚµÚ¯Û\95\88Û\95ÚµÚ¯Û\95ل}}",
        "createacct-benefit-body3": "recent {{PLURAL:$1|contributor|contributors}}",
        "badretype": "گذرواژةلێ گإ نۆیساتة چؤِی یةک نیِن",
        "usernameinprogress": ". دِرێ حساوو دؤرسة مةکإ . خواهشا صبر کةن",
        "nocookieslogin": "{{SITENAME}} برای ورود کاربران به سامانه از کوکی‌ها استفاده می‌کند.\nشما کوکی‌ها را از کار انداخته‌اید.\nلطفاً کوکی‌ها را به کار بیندازید و دوباره امتحان کنید.",
        "nocookiesfornew": "حساوو کاربةری نةسازریا، زیرا نتوانستیم منبع آن را تأیید کنیم.\nمطمئن شوید که کوکی‌ها فعال هستند، آن‌گاه صفحه را از نو بارگیری کنید و دوباره امتحان کنید.",
        "noname": ".هؤمة نام کاربةری معتبری دیاری نکردئة",
-       "loginsuccesstitle": "موفق بین چینإ سیستم",
+       "loginsuccesstitle": "إنۆم سیستم هەتن انجۆم گرت",
        "loginsuccess": "هؤمة ایسة هةتیإ نؤم سیستم {{SITENAME}} وۀ نام\"$1\".'",
        "nosuchuser": "کاربةری وۀ نام «$1» ئة ائرة نیة.\nنام کاربةری وة کةڵنگی و گؤجةری حروف حساسة .\nاملای نام را بررسی کنید، یا [[Special:UserLogin/signup|یک حساب کاربری تازه بسازید]].",
        "nosuchusershort": "هؤیچ کاربةری وة نام ''$1'' ئة ائرة نیة.\nاملایتان را وارسی کنید.",
        "nouserspecified": ".باید یإ گِلة  نام کاربةری دیاری کئین",
-       "login-userblocked": ".ئئ کاربةرة بةسیائة. إنؤم هةتِن سیستم مجاز نیة",
+       "login-userblocked": ".ئی کاربرە بەسیائە. إنؤم هەتِن سیستم ڕاووآ(مجاز)نیە",
        "wrongpassword": "گذرواژه‌ای که وارد کردید نادرستة.\nخواهشا دووباره تلاش کةن.",
        "wrongpasswordempty": "گذرواژه‌ای که وارد کرده‌اید، خالی است.\nلطفاً دوباره تلاش کنید.",
        "passwordtooshort": "گذرواژه باید دست‌کم {{PLURAL:$1|۱ حرف|$1 حرف}} داشته باشد.",
        "loginlanguagelabel": "$1:زوون",
        "suspicious-userlogout": "درخواست هؤمة ئةرا  دةرچئن إژ سیستم  رد بیة زیرا به نظر می‌رسد که این  .درخواست توسط یک مرورگر معیوب یا پروکسی میانگیر کل/ارسال بیة",
        "createacct-another-realname-tip": "نام راسکانی/واقعی دڵ بخواهیة.\nاگر آن را وارد کنید هنگام ارجاع به آثارتان و انتساب آن‌ها به شما از نام واقعی‌تان استفاده خواهد شد.",
-       "pt-login": "نؤم هۀتن.",
-       "pt-login-button": "نؤم هۀتن سیستم",
+       "pt-login": "إنۆم هەتِن.",
+       "pt-login-button": "إ نۆم هەتن سیستم",
        "pt-createaccount": "حساووئ أرا ووژتان بِسازِن",
-       "pt-userlogout": "دةرچئن/خروج",
+       "pt-userlogout": "دەرچێن|خروج",
        "php-mail-error-unknown": "خطای ناشناخته در تابع  mail()‎ پی‌اچ‌پی",
        "user-mail-no-addy": "تلاش برای ارسال ایمیل بدون آدرس ایمیل.",
        "user-mail-no-body": "سعی کردید ایمیلی با محتوای بی‌دلیل کوتاه و یا خالی بفرستید.",
        "changepassword": "تغییردائن رمز",
        "resetpass_announce": "شما باید برای پایان ورود به سامانه، گذرواژهٔ جدیدی را تنظیم کنید.",
-       "resetpass_header": "تغییر گذرواژهٔ حساب کاربری",
+       "resetpass_header": "گؤەڕانن/تغییر رمز حساب کاربری",
        "oldpassword": "گذرواژهٔ پیشین:",
        "newpassword": "گذرواژهٔ تازه:",
        "retypenew": "گذرواژهٔ تازه را دوباره وارد کنید",
        "passwordreset-text-many": "{{PLURAL:$1|برای دریافت یک گذرواژهٔ موقت از طریق ایمیل، یکی از خانه‌ها را پر کنید.}}",
        "passwordreset-disabled": "بازنشانی گذرواژه در این ویکی غیرفعال شده است.",
        "passwordreset-emaildisabled": "قابلیت‌های ایمیل در این ویکی غیرفعال شده‌اند.",
-       "passwordreset-username": ":نؤم کاربری",
+       "passwordreset-username": ":نۆم کاربەری",
        "passwordreset-domain": "دامنه:",
        "passwordreset-capture": "ایمیل نهایی نشان داده شود؟",
        "passwordreset-capture-help": "اگر این گزینه را علامت بزنید، ایمیل (حاوی گذرواژهٔ موقت) به شما نشان داده خواهد شد و برای کاربر نیز فرستاده خواهد شد.",
        "passwordreset-email": ":نیشانی ایمیل",
-       "passwordreset-emailtitle": "جزئیات حساب در {{SITENAME}}",
+       "passwordreset-emailtitle": "جزئیات حساوو أڕ {{SITENAME}}",
        "passwordreset-emailtext-ip": "یک نفر (احتمالاً شما، با نشانی آی‌پی $1) درخواست بازنشانی گذرواژه‌تان در {{SITENAME}} ($4) را کرده‌است. {{PLURAL:$3|حساب|حساب‌های}} کاربری زیر با این آدرس ایمیل مرتبط هستند:\n\n$2\n\n{{PLURAL:$3|این گذرواژهٔ موقت|این گذرواژه‌های موقت}} پس از {{PLURAL:$5|یک روز|$5 روز}} باطل خواهند شد.\nشما باید هم‌اکنون ثبت ورود کنید و گذرواژه‌ای جدید برگزینید. اگر فکر می‌کنید شخص دیگری این درخواست را داده است یا اگر گذرواژهٔ اصلی‌تان را به یاد آوردید و دیگر نمی‌خواهید آن را تغییر دهید، می‌توانید این پیغام را نادیده بگیرید و به استفاده از گذرواژهٔ قبلی‌تان ادامه دهید.",
        "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-emailsentemail": "اگر نشانی پست الکترونیکی که وارد کردید برای حساب کاربریتان ثبت شده باشد، یک نامهٔ بازنشانی گذرواژه به آن فرستاده می‌شود.",
+       "passwordreset-emailsentusername": "اگر نشانی پست الکترونیکی مرتبطی موجود باشد، یک نامه برای بازنشانی گذرواژه به آن ارسال خواهد شد.",
        "passwordreset-emailsent-capture": "یک ایمیل بازنشانی که در پایین نمایش داده شده، فرستاده شده است.",
        "passwordreset-emailerror-capture": "ایمیل بازنشانی، که در زیر نمایش داده شده، ایجاد شد، ولی ارسال آن به {{GENDER:$2|کاربر}} موفقیت‌آمیز نبود: $1",
        "changeemail": "تغییر یا حذف نشانی ایمیل",
        "changeemail-newemail-help": "برای حذف ایمیل باید این بخش را خالی رها کنید در نتیجه امکان بازگردانی گذرواژه و دریافت ایمیل از ویکی برای شما مقدور نخواهد بود.",
        "changeemail-none": "(هؤیچ کام)",
        "changeemail-password": "گذرواژهٔ {{SITENAME}} هؤمة:",
-       "changeemail-submit": "تغییر ایمیل",
+       "changeemail-submit": "گؤەڕانن/تغییر ایمیل",
        "changeemail-throttled": "شما به مراتب برای ورود تلاش کرده‌اید.\nلطفاً پیش از آنکه دوباره تلاش کنید $1 صبر کنید.",
        "changeemail-nochange": "لطفاً رایانامهٔ جدید و متفاوتی وارد کنید.",
        "resettokens": "بازنشانی شناساننده‌ها",
        "summary": "خلاصه:",
        "subject": "عنوان:",
        "minoredit": "یۀ دۀسکاری جزئیکۀ",
-       "watchthis": "پی‌گیری ئئ وۀلگۀ",
-       "savearticle": "ذۀخیرۀ وۀلگۀ",
+       "watchthis": "پئ گیری اێ وەلگە",
+       "savearticle": "وەڵگە بِیل(ذخیره کە)",
        "preview": "پیش‌نمایش",
        "showpreview": "پیش‌نمایش",
-       "showdiff": "نمایش تغییرةل/پالانةل",
-       "blankarticle": "<strong>هشدار:</strong> شما در حال ایجاد صفحه خالی هستید.\nاگر «{{int:savearticle}}» را دوباره کلیک کنید، صفحه بدون محتوا ایجاد می‌شود.",
+       "showdiff": "گؤەڕیال(تغییرات) بۆین",
+       "blankarticle": "<strong>هوشدار:</strong> هۆمە وەڵگەتۆن سازیە پەتیە(حالیە).\nأڕ «{{int:savearticle}}» دۆِ گِل کلیک کِین ، وەڵگە بێ  نۆم جِک(محتوا) مەسازێ.",
        "anoneditwarning": "<strong>هشدار:</strong> شما وارد نشده‌اید. نشانی آی‌پی شما برای عموم قابل مشاهده خواهد بود اگر هر تغییری ایجاد کنید. اگر <strong>[$1 وارد شوید]</strong> یا <strong>[$2 یک حساب کاربری بسازید]</strong>، ویرایش‌هایتان به نام کاربری‌تان نسبت داده خواهد شد، همراه با مزایای دیگر.",
        "anonpreviewwarning": "''شما به سامانه وارد نشده‌اید. ذخیره کردن باعث می‌شود که نشانی آی‌پی شما در تاریخچهٔ این صفحه ثبت گردد.''",
        "missingsummary": "'''یادآوری:''' شما خلاصهٔ ویرایش ننوشته‌اید.\nاگر دوباره دکمهٔ «{{int:savearticle}}» را فشار دهید ویرایش شما بدون آن ذخیره خواهد شد.",
        "copyrightwarning2": "لطفاً توجه داشته‌باشید که همهٔ مشارکت‌ها در {{SITENAME}} ممکن است توسط دیگر مشارکت‌کنندگان تغییر یابند، ویرایش یا حذف شوند.\nاگر نمی‌خواهید نوشته‌هایتان بی‌رحمانه ویرایش شوند؛ بنابراین، آنها را اینجا ارائه نکنید.<br />\nشما همچنین به ما تعهد می‌کنید که خودتان این را نوشته‌اید یا آن را از یک منبع با مالکیت عمومی یا مشابه آزاد آن برداشته‌اید ($1 را برای جزئیات بیشتر ببینید).\n<strong>کارهای دارای حق تکثیر را بدون اجازه ارائه نکنید!</strong>",
        "editpage-cannot-use-custom-model": "مدل محتوای این صفحه نمی‌تواند عوض شود.",
        "longpageerror": "'''خطا: متنی که ارسال کرده‌اید {{PULAR:$1|یک کیلوبایت|$1 کیلوبایت}} طول دارد. این مقدار از مقدار بیشینهٔ {{PLURAL:$2|یک کیلوبایت|$2 کیلوبایت}} بیشتر است.'''\nنمی‌توان آن را ذخیره کرد.",
-       "readonlywarning": "'''هشدار: پایگاه داده برای نگهداری قفل شده‌است، به همین علت هم‌اکنون نمی‌توانید ویرایش‌هایتان را ذخیره کنید.'''\nاگر می‌خواهید متن را در یک پروندهٔ متنی کپی کنید و برای آینده ذخیره‌اش کنید.\n\nمدیری که آن را قفل کرده این توضیح را ارائه کرده‌است: $1",
+       "readonlywarning": "<strong>هشدار: پایگاه داده برای نگهداری قفل شده‌است، به همین علت هم‌اکنون نمی‌توانید ویرایش‌هایتان را ذخیره کنید.</strong>\nاگر می‌خواهید متن را در یک پروندهٔ متنی کپی کنید و برای آینده ذخیره‌اش کنید.\n\nمدیری که آن را قفل کرده این توضیح را ارائه کرده‌است: $1",
        "protectedpagewarning": "'''هشدار: این صفحه قفل شده‌است تا فقط کاربران با دسترسی مدیریت بتوانند ویرایشش کنند.'''\nآخرین موارد سیاهه در زیر آمده‌است:",
        "semiprotectedpagewarning": "'''توجه:''' این صفحه قفل شده‌است تا تنها کاربران ثبت‌نام‌کرده قادر به ویرایش آن باشند.\nآخرین موارد سیاهه در زیر آمده‌است:",
        "cascadeprotectedwarning": "<strong>هشدار:</strong> این صفحه به علت قرارگرفتن در {{PLURAL:$1|صفحهٔ|صفحه‌های}} آبشاری-محافظت‌شدهٔ زیر قفل شده‌است تا فقط مدیران بتوانند ویرایشش کنند.",
        "permissionserrors": "خطای سطح دسترسی",
        "permissionserrorstext": "شما اجازهٔ انجام این کار را به این {{PLURAL:$1|دلیل|دلایل}} ندارید:",
        "permissionserrorstext-withaction": "You do not have permission to $2, for the following {{PLURAL:$1|reason|reasons}}:",
-       "contentmodelediterror": "امکان ویرایش این نسخه برای شما نیست چون نوع محتوای آن <code>$1</code> است و نوع محتوای کنونی صفحه <code>$2</code> است.",
+       "contentmodelediterror": "امکان ویرایش این نسخه برای شما نیست چون نوع محتوای آن <code>$1</code> است که متفاوت است با نوع محتوای کنونی صفحه <code>$2</code> است.",
        "recreate-moveddeleted-warn": "<strong>هشدار: شما در حال ایجاد صفحه‌ای هستید که قبلاً حذف شده‌است.</strong>\n\nدر نظر داشته باشید که آیا ادامهٔ ویرایش این صفحه کار درستی‌است یا نه.\nسیاههٔ حذف و انتقال این صفحه در زیر نشان داده شده‌است:",
        "moveddeleted-notice": "این صفحه حذف شده‌است.\nدر زیر سیاههٔ حذف و انتقال این صفحه نمایش داده شده‌است.",
        "moveddeleted-notice-recent": "متاسفانه صفحه قبلا حذف شده‌است (در ۲۴ ساعت اخیر) \nدلیل حذف و سیاههٔ انتقال در پائین موجود است.",
        "invalid-content-data": "داده محتوای نامعتبر",
        "content-not-allowed-here": "محتوای «$1» در صفحهٔ [[$2]] مجاز نیست",
        "editwarning-warning": "خروج از این برگه ممکن است باعث شود که شما هر شانسی که به وجود آورده‌اید را از دست بدهید.\nاگر شما وارد سامانه شده‌اید، می‌توانید این هشدار را در بخش «{{int:prefs-editing}}» ترجیحاتتان غیرفعال کنید.",
-       "editpage-notsupportedcontentformat-title": "Ù\81رÙ\85ت Ù\85حتÙ\88ا پشتیبانی نشده",
+       "editpage-notsupportedcontentformat-title": "Ù\81رÙ\85ت Ù\86Û\86Ù\85 Ø¬Ù\90Ú©(Ù\85حتÙ\88ا)پشتیبانی نشده",
        "editpage-notsupportedcontentformat-text": "فرمت محتوای $1 توسط مدل محتوای $2 پشتیبانی نشده‌است.",
        "content-model-wikitext": "ویکی‌متن",
        "content-model-text": "متنی ساده",
        "revdelete-hide-comment": "خلاصة دةسکاری",
        "revdelete-hide-user": "نام کاربری/نشانی آی‌پی",
        "revdelete-hide-restricted": "فرونشانی اطلاعات برای مدیران به همراه دیگران",
-       "revdelete-radio-same": "(بدون تغییر)",
-       "revdelete-radio-set": "آشاریا/پنهان",
+       "revdelete-radio-same": "(بدون گؤەڕانن/تغییر )",
+       "revdelete-radio-set": "آشاریا",
        "revdelete-radio-unset": "دیارۀ-نمایان",
        "revdelete-suppress": "از دسترسی مدیران به داده نیز مانند سایر کاربران جلوگیری به عمل آید.",
        "revdelete-unsuppress": "حذف محدودیت‌ها در بازبینی‌های ترمیم‌شده",
        "revdelete-failure": "'''پیدایی ورژن ها قابل به روز کردن نیست:'''\n$1",
        "logdelete-success": "تغییر پیدایی مورد با موفقیت انجام شد.",
        "logdelete-failure": "'''پیدایی سیاهه‌ها قابل تنظیم نیست:'''\n$1",
-       "revdel-restore": "تغییر پیدایی",
+       "revdel-restore": "گؤەڕانن/تغییر پیدایی",
        "pagehist": "تاریخ وةڵگة",
        "deletedhist": "تاریخچهٔ پاک شده",
        "revdelete-hide-current": "خطا در پنهان کردن مورد مورخ $2 ساعت $1: این نسخه، ورژن اخیر است و قابل پنهان کردن نیست.",
        "revdelete-reason-dropdown": "*دلایل متداول حذف\n** نقض حق تکثیر\n** اظهار نظر یا اطلاعات فردی نامناسب\n** نام کاربری نامناسب\n** اطلاعات به طور بالقوه افتراآمیز",
        "revdelete-otherreason": ":دلیل دیگر/اضافی",
        "revdelete-reasonotherlist": "دلیل دیگر",
-       "revdelete-edit-reasonlist": "دةسکاری دلایل حذف",
+       "revdelete-edit-reasonlist": "دەسکاری دلایل حذف",
        "revdelete-offender": "نویسنده ورژن:",
        "suppressionlog": "سیاههٔ فرونشانی",
        "suppressionlogtext": "در زیر فهرستی از آخرین حذف‌ها و قطع دسترسی‌هایی که حاوی محتوایی هستند که از مدیران پنهان شده‌اند را می‌بینید.\nبرای مشاهدهٔ فهرستی از قطع دسترسی‌های فعال [[Special:BlockList|فهرست بسته‌شده‌ها]] را ببینید.",
        "mergehistory": "یکی کردن تاریخچه صفحات",
        "mergehistory-header": "این صفحه به شما این امکان را می‌دهد که نسخه‌های تاریخچهٔ یک مقاله را با یک مقاله دیگر ادغام کنید.\nاطمینان حاصل کنید که این تغییر به توالی زمانی ویرایش‌ها لطمه نخواهد زد.",
        "mergehistory-box": "ادغام ورژنهای دو صفحه:",
-       "mergehistory-from": "صفحهٔ مبدأ:",
-       "mergehistory-into": "صفحهٔ مقصد:",
+       "mergehistory-from": "وەڵگە بنچێنە:",
+       "mergehistory-into": "وەڵگە مقصد:",
        "mergehistory-list": "تاریخچهٔ قابل ادغام",
        "mergehistory-merge": "ورژن های زیر از [[:$1]] قابل ادغام با [[:$2]] هستند.\nاز ستون دکمه‌های رادیویی استفاده کنید تا نسخه‌هایی را که تا قبل از زمانی مشخص ایجاد شده‌اند انتخاب کنید.\nتوجه کنید که کلیک روی پیوندها سبب بازگشت ستون به حالت اولیه می‌شود.",
        "mergehistory-go": "نمایش تاریخچه قابل ادغام",
        "searchprofile-advanced": "پیشرفتۀ",
        "searchprofile-articles-tooltip": "جستجو در $1",
        "searchprofile-images-tooltip": "مِنِی کردن پۀروۀندۀل",
-       "searchprofile-everything-tooltip": "مِنِی کردن کؤل محتوا) (شاملا وۀلگۀل گۀپ و قسۀ",
+       "searchprofile-everything-tooltip": "مِنِی کردن کؤل(گِشت)نۆم جِک(محتوا) \"شامل وەڵگەل گەپ و قسە\"",
        "searchprofile-advanced-tooltip": "جستجو در فضاهای نام دلخواه",
        "search-result-size": "$1 ({{PLURAL:$2|1 واژۀ|$2 واژۀل}})",
        "search-result-category-size": "{{PLURAL:$1|یک عضو|$1 عضو}} ({{PLURAL:$2|یک زیررده|$2 زیررده}}، {{PLURAL:$3|یک پرونده|$3 پرونده}})",
        "search-redirect": "(تغییرمسیر $1)",
        "search-section": "(بۀخش $1)",
-       "search-category": "(رده  $1)",
+       "search-category": "(ڕِزگ $1)",
        "search-file-match": "(تشابه محتوی پرونده)",
        "search-suggest": "نۀظۀرتان یۀ بیۀ: $1",
        "search-rewritten": "نمایش نتایج $1. جستجوی به جای $2.",
        "powersearch-legend": "مِنِی کردن پیشرفته",
        "powersearch-ns": "جستجو در فضاهای نام:",
        "powersearch-togglelabel": "بررسی:",
-       "powersearch-toggleall": "کۆل",
+       "powersearch-toggleall": "کؤل(گشت)",
        "powersearch-togglenone": "هؤیچ کام",
        "powersearch-remember": "انتخاب را برای جستجوهای بعدی به خاطر داشته‌باش",
        "search-external": "جستجوی خارجی",
        "searchdisabled": "جستجو در {{SITENAME}} فعال نیست.\nموقتاً می‌توانید از جستجوی Google استفاده کنید.\nتوجه کنید که نتایج حاصل از جستجو با آن روش ممکن است به‌روز نباشند.",
        "search-error": "خطایی هنگام جست‌وجو رخ داده است: $1",
-       "preferences": "تمارزوةل/ترجیحات",
-       "mypreferences": "تمارزوةل/ترجیحات",
+       "preferences": "تمارزووەل(ترجیحات)",
+       "mypreferences": "تمارزووەل(ترجیحات)",
        "prefs-edits": "تعداد دةسکاریةل:",
        "prefsnologintext2": "خواهشمند است برای تغییر تنظیمات‌تان وارد شوید.",
        "prefs-skin": "پوسته",
        "prefs-labs": "گزینه‌های آزمایشی",
        "prefs-user-pages": "وةڵگةل کاربۀری",
        "prefs-personal": "داده‌های کاربر",
-       "prefs-rc": "تۀغیرۀل/ئالشتیۀل ایسۀ",
+       "prefs-rc": "گؤەڕاننەل(تغییرات) ایسگە",
        "prefs-watchlist": "لیست پی‌گیریۀل",
        "prefs-editwatchlist": "ویرایش فهرست پی‌گیری‌ها",
        "prefs-editwatchlist-label": "ویرایش همه فهرست پیگیری‌هایتان:",
        "prefs-setemail": "تنظیم آدرس ایمیل",
        "prefs-email": "گزینه‌های ایمیل",
        "prefs-rendering": "نمایش صفحه",
-       "saveprefs": "ذخیرۀ",
+       "saveprefs": "هیشتن(ذخیره)",
        "restoreprefs": "برگرداندن تمام تنظیمات پیش‌فرض (در تمامی قسمت‌ها)",
-       "prefs-editing": "دةسکاری کردن",
+       "prefs-editing": "دەسکاری کردن",
        "rows": "تعداد سطرها:",
        "columns": "تعداد ستون‌ها:",
-       "searchresultshead": "گئردین/مهِ نی",
+       "searchresultshead": "مِنِی کِردِن(گێردین)",
        "stub-threshold": "آستانهٔ ویرایش پیوندهای ناقص ($1):",
        "stub-threshold-sample-link": "نمونه",
        "stub-threshold-disabled": "غیرفعال‌سازی/إ کار کةتن",
        "recentchangescount": ":تعداد پیش‌فرض ویرایش‌های نمایش یافته",
        "prefs-help-recentchangescount": "این گزینه شامل تغییرات اخیر، تاریخچهٔ صفحه‌ها و سیاهه‌ها می‌شود.",
        "prefs-help-watchlist-token2": "این کلید رمز خوراک وب فهرست پی‌گیری‌های شماست.\nهرکس آن را بداند می‌تواند فهرست پی‌گیری‌هایتان را بخواند، بنابراین آن را به اشتراک نگذارید. [[Special:ResetTokens|اگر لازم است آن را تغییر دهید اینجا را کلیک کنید]].",
-       "savedprefs": "ترجیحات شما ذخیره شد.",
+       "savedprefs": "تمارزووەل(ترجیحات) هۆمە هیشتێ(ذخیرە بی)",
        "savedrights": "دسترسی کاربری {{GENDER:$1|$1}} ذخیره شده‌است.",
        "timezonelegend": "منطقه زمانی:",
        "localtime": "زمان محلی:",
        "timezoneregion-indian": "اؤقیانووس هند",
        "timezoneregion-pacific": "اؤقیانوس آرام",
        "allowemail": "امکان دریافت ایمیل از دیگر کاربران",
-       "prefs-searchoptions": "گئردین/مهِ نی",
+       "prefs-searchoptions": "مِنِی کِردِن(گێردین)",
        "prefs-namespaces": "فضای نامۀل",
        "default": "پیش‌فرض",
        "prefs-files": "فایل",
        "prefs-registration": "زمان ثبت‌نام:",
        "yourrealname": ":نام ڕاسکانی",
        "yourlanguage": ":زوون",
-       "yourvariant": "گویش زبان محتوا:",
+       "yourvariant": "گویش زوون نۆم جِک(محتوا):",
        "prefs-help-variant": "گویش انتخابی شما برای نمایش محتوای صفحه‌ها در این ویکی",
        "yournick": "امضای تازه:",
        "prefs-help-signature": "نظرهای نوشته‌شده در صفحهٔ بحث باید با «<nowiki>~~~~</nowiki>» امضا شوند؛ این علامت به‌صورت خودکار به امضای شما و مهر تاریخ تبدیل خواهد شد.",
        "prefs-dateformat": "آرایش تاریخ",
        "prefs-timeoffset": "فاصلهٔ زمانی",
        "prefs-advancedediting": "تنظیمات عمومی",
-       "prefs-editor": "دةسکاری کةر",
+       "prefs-editor": "دەسکاری کەر",
        "prefs-preview": "پیش‌نمایش",
        "prefs-advancedrc": "گزینه‌های پیشرفته",
        "prefs-advancedrendering": "گزینه‌های پیشرفته",
        "editusergroup": "ویرایش گروه‌های کاربری",
        "editinguser": "تغییر اختیارات کاربری کاربر {{GENDER:$1|کاربر}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "ویرایش گروه‌های کاربری",
-       "saveusergroups": "ثبت گروه‌های کاربری",
+       "saveusergroups": "هیشتِن(ذخیرە)گؤەڕیال(تغییرات)کوو(گروە)کاربەری",
        "userrights-groupsmember": "عضو:",
        "userrights-groupsmember-auto": "عضو ضمنی:",
        "userrights-groups-help": "شما می‌توانید گروه‌هایی را که کاربر در آن قرار دارد تغییر دهید:\n* جعبهٔ علامت‌خورده نشانهٔ بودن کاربر در آن گروه است.\n* جعبهٔ خالی نشانهٔ نبودن کاربر در آن گروه است.\n* علامت * به این معنی‌است که اگر آن گروه را بیفزایید نمی‌توانید بعداً برش دارید، و برعکس.",
        "group-sysop": "مدیران",
        "group-bureaucrat": "دیوان‌سالاران",
        "group-suppress": "فرونشانندگان",
-       "group-all": "(کۆل)",
+       "group-all": "(کؤل(گشت))",
        "group-user-member": "{{GENDER:$1|کاربةر}}",
        "group-autoconfirmed-member": "{{GENDER:$1|کاربةر تأیید بی}}",
        "group-bot-member": "ربات",
        "grouppage-sysop": "{{ns:project}}:مدیرةل",
        "grouppage-bureaucrat": "{{ns:project}}:دیوان‌سالاران",
        "grouppage-suppress": "{{ns:project}}:فرونشانی",
-       "right-read": "خؤةنِن وةڵگةل",
+       "right-read": "خووەنِن وةڵگةل",
        "right-edit": ".دةسکاری وةڵگةل",
        "right-createpage": "ایجاد صفحه (در مورد صفحه‌های غیر بحث)",
        "right-createtalk": "یجاد صفحه‌های بحث",
        "right-move": "جاوواز کردن وةڵگة",
        "right-move-subpages": "انتقال صفحات به همراه زیر‌صفحات‌شان",
        "right-move-rootuserpages": "انتقال صفحه‌های کاربری سرشاخه",
-       "right-move-categorypages": "انتقال صفحهٔ رده",
+       "right-move-categorypages": "وەڵگە ڕِزگ جاوواز کە",
        "right-movefile": "انتقال پرونده‌ها",
        "right-suppressredirect": "انتقال صفحه بدون ایجاد تغییرمسیر از نام قبلی",
        "right-upload": "بارنیائن فایلۀل",
        "right-blockemail": "قطع دسترسی دیگر کاربران برای ارسال ایمیل",
        "right-hideuser": "قطع دسترسی کاربر و پنهان کردن آن از دید عموم",
        "right-ipblock-exempt": "تاثیر نپذیرفتن از قطع دسترسی‌های آی‌پی، خودکار یا فاصله‌ای",
-       "right-proxyunbannable": "تاثیر نپذیرفتن از قطع دسترسی خودکار پروکسی‌ها",
        "right-unblockself": "بازکردن دسترسی خود",
        "right-protect": "تغییر میزان محافظت صفحات و ویرایش صفحات محافظت‌شده آبشاری",
        "right-editprotected": "ویرایش صفحه‌های محافظت‌شده به عنوان «{{int:protect-level-sysop}}»",
        "right-editmywatchlist": "فهرست پیگیری‌های خود را ویرایش کنید. توجه داشته باشید برخی از اقدامات حتی بدون این دسترسی هم صفحات را اضافه می‌کنند.",
        "right-viewmyprivateinfo": "داده‌های خصوصی خود را ببینید (مانند آدرس ایمیل و نام واقعی)",
        "right-editmyprivateinfo": "داده‌های خصوصی خود را ویرایش کنید (مانند آدرس ایمیل و نام واقعی)",
-       "right-editmyoptions": "ویرایش ترجیحات خود",
+       "right-editmyoptions": "تمارزووەل(ترجیحات)ووِژِت دەسکاری کە",
        "right-rollback": "واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است",
        "right-markbotedits": "علامت زدن ویرایش‌های واگردانی‌شده به عنوان ویرایش ربات",
        "right-noratelimit": "تاثیر نپذیرفتن از محدودیت سرعت",
        "right-managechangetags": "ایجاد و حذف [[Special:Tags|برچسب‌ها]] از پایگاه داده",
        "right-applychangetags": "تائید [[Special:Tags|برچسب]] بر روی تغییرات یک نفر",
        "right-changetags": "افزودن یا حذف [[Special:Tags|برچسب]] قراردادی بر روی نسخه یا سیاهه ورودی‌ها",
+       "grant-group-customization": "سفارشی‌سازی و تنظیمات",
        "newuserlogpage": "سیاههٔ ایجاد کاربر",
        "newuserlogpagetext": "این سیاهه‌ای از نام‌های کاربری تازه‌ساخته‌شده است.",
        "rightslog": "سیاههٔ اختیارات کاربر",
        "rightslogtext": "این سیاههٔ تغییرات اختیارات کاربر است.",
-       "action-read": "خؤةÙ\86Ù\90Ù\86 Ø¦Ø¦ Ù\88ةڵگة",
-       "action-edit": "ئئ Ù\88Ø©ÚµÚ¯Û\80 Ø¯Ø©Ø³Ú©Ø§Ø±Û\8c Ú©Ø©ن",
+       "action-read": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ø¨Ø®Ù\88Ù\88Û\95Ù\86",
+       "action-edit": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ø¯Û\95سکارÛ\8c Ú©Û\95ن",
        "action-createpage": "دورس کردن وةڵگة",
        "action-createtalk": "ایجاد صفحه‌های بحث",
        "action-createaccount": "ایجاد این حساب کاربری",
        "action-history": "مشاهده تاریخچه این صفحه",
        "action-minoredit": "علامت زدن این ویرایش به عنوان جزئی",
-       "action-move": "جاÙ\88Ù\88از Ú©Ø±Ø¯Ù\86 Ø¦Ø¦ Ù\88Û\80Ù\84Ú¯Û\80",
+       "action-move": "جاÙ\88Ù\88از Ú©Ø±Ø¯Ù\86 Ø§Û\8e Ù\88Û\95ÚµÚ¯Û\95",
        "action-move-subpages": "انتقال این صفحه و زیرصفحه‌های آن",
        "action-move-rootuserpages": "انتقال صفحه‌های کاربری سرشاخه",
-       "action-move-categorypages": "انتقال صفحهٔ رده",
+       "action-move-categorypages": "وەڵگە ڕِزگ جاوواز کە",
        "action-movefile": "جاوواز کردن ئئ وۀلگۀ",
        "action-upload": "بارگذاری این پرونده",
        "action-reupload": "نوشتن روی این پرونده موجود",
        "action-reupload-shared": "باطل کردن این پرونده روی یک مخزن مشترک",
        "action-upload_by_url": "بارگذاری این پرونده از یک نشانی اینترنتی",
        "action-writeapi": "استفاده از API نوشتن",
-       "action-delete": "حةذف ئئ وةڵگة",
+       "action-delete": "پاک کردن ئێ وەڵگە",
        "action-deleterevision": "حذف این نسخه",
        "action-deletedhistory": "مشاهدهٔ تاریخچهٔ حذف شدهٔ این صفحه",
        "action-browsearchive": "جستجوی صفحه‌های حذف‌شده",
-       "action-undelete": "بازگردانی ئئ وةڵگة",
+       "action-undelete": "واگردانی(گلآدائن)ئێ وەڵگە",
        "action-suppressrevision": "مشاهده و احیای ویرایش‌های حذف شده",
        "action-suppressionlog": "مشاهدهٔ این سیاههٔ خصوصی",
        "action-block": "قطع دسترسی این کاربر از ویرایش‌کردن",
-       "action-protect": "تغییر سطح محافظت این صفحه",
+       "action-protect": "گؤەڕانن(تغییر)سطح پڵۆم کردن اێ وەڵگە",
        "action-rollback": "واگردانی سریع ویرایش‌های آخرین کاربری که یک صفحه را ویرایش کرده‌است",
        "action-import": "واردکردن صفحه از ویکی‌های دیگر",
        "action-importupload": "واردکردن صفحه از طریق بارگذاری پرونده",
        "action-managechangetags": "ایجاد و حذف تگ‌ها از پایگاه داده",
        "action-applychangetags": "اعمال برچسب بر روی تغییرات شما",
        "action-changetags": "افزودن یا حذف برچسب قراردادی بر روی نسخه یا سیاهه ورودی‌ها",
-       "nchanges": "$1 {{PLURAL:$1|تغییرةل|تغییر}}",
+       "nchanges": "$1 {{PLURAL:$1|گؤەڕاننەل|گؤەڕانن}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|از آخرین بازدید}}",
        "enhancedrc-history": "تاریخچه",
-       "recentchanges": "تۀغیرۀل/ئالشتیۀل ایسۀ",
+       "recentchanges": "گؤەڕاننەل(تغییرات) ایسگە",
        "recentchanges-legend": "گزینه‌های تغییرات اخیر",
        "recentchanges-summary": ".آخرین تغییرۀل ویکی ئۀ ئئ وۀلگۀ پی‌گیری کۀن",
        "recentchanges-noresult": "هیچ تغییری در طول دورهٔ تعیین‌شده با این معیارها هم‌خوانی نداشت.",
        "recentchanges-feed-description": "آخرین تغییرات ویکی را در این خوراک پی‌گیری کنید.",
        "recentchanges-label-newpage": "ئئ دۀسکاریۀ وۀلگۀ تازه ساختئ",
-       "recentchanges-label-minor": "یة دةسکاری جزئیکة",
-       "recentchanges-label-bot": "ئئ Ø¯Û\80سکارÛ\8c Û\8cÛ\80 Ø±Ø¨Ø§ØªØ¦ Ø§Ù\86جؤم دائه",
+       "recentchanges-label-minor": "یە دەسکاری گؤجەرێ کە(جزئیە)",
+       "recentchanges-label-bot": "اÛ\8e Ø¯Û\95سکارÛ\8cÛ\95 Ø±Ø¨Ø§ØªÛ\8e Ø§Ù\86جÛ\86م دائه",
        "recentchanges-label-unpatrolled": "این ویرایش هنوز گشت‌زنی نشده است",
        "recentchanges-label-plusminus": "حجم وۀلگۀ به اندازه این مقدار بایت تغییر یافته است",
        "recentchanges-legend-heading": "'''اختصارۀل:'''",
        "recentchangeslinked-summary": "در زیر فهرستی از تغییرات اخیر صفحه‌های پیوند داده شده از این صفحه (یا اعضای رده مورد نظر) را می‌بینید.\nصفحه‌هایی که در [[Special:Watchlist|فهرست پی‌گیری‌هایتان]] باشند به صورت '''پررنگ''' نشان داده می‌شوند.",
        "recentchangeslinked-page": ":نام وةڵگة",
        "recentchangeslinked-to": "نمایش تغییرات صفحه‌هایی که به صفحهٔ داده‌شده پیوند دارند",
-       "recentchanges-page-added-to-category": "[[:$1]] اضافة بیة رده/ڕِزگ",
+       "recentchanges-page-added-to-category": "[[:$1]] اضاف بیە ڕِزگ",
        "recentchanges-page-added-to-category-bundled": "[[:$1]] و {{PLURAL:$2|یک صفحه|$2 صفحه}}ٔ دیگر به رده اضافه شدند",
-       "recentchanges-page-removed-from-category": "[[:$1]] إژ رده/ڕِزگ حةذف بی",
+       "recentchanges-page-removed-from-category": "[[:$1]] إژ ڕِزگ پاکآ بی",
        "recentchanges-page-removed-from-category-bundled": "[[:$1]] و {{PLURAL:$2|یک صفحه|$2 صفحه}}ٔ دیگر از رده حذف شدند",
        "autochange-username": "تغییرات خودکار مدیاویکی",
        "upload": "بارنیائن فایل",
        "file-deleted-duplicate-notitle": "یک پرونده یکسان بااین پرونده قبلاً حذف شده است و عنوان متوقف شده‌است.\nشما باید از کسی که دسترسی مشاهدهٔ پرونده متوقف شده را دارد، درخواست کنید تا شرایط را قبل از بارگذاری مجدد بررسی کند.",
        "uploadwarning": "هشدار بارگذاری",
        "uploadwarning-text": "لطفاً توضیحات پرونده را در زیر تغییر دهید و دوباره تلاش کنید.",
-       "savefile": "ذخیرهٔ پرونده",
+       "savefile": "پەروەنده بِیل(ذخیره کە)",
        "uploaddisabled": "بارگذاری غیرفعال است.",
        "copyuploaddisabled": "بارگذاری از طریق نشانی اینترنتی غیرفعال است.",
        "uploaddisabledtext": "امکان بارگذاری پرونده غیرفعال است.",
        "upload-dialog-title": "بارنیائن فایل",
        "upload-dialog-button-cancel": "ئآهووسانن/لغو",
        "upload-dialog-button-done": "انجؤم بی",
-       "upload-dialog-button-save": "ذخیرۀ",
+       "upload-dialog-button-save": "هیشتن(ذخیره)",
        "upload-dialog-button-upload": "بارگذاری بی",
        "upload-form-label-select-file": "فایلئ انتخاب کۀ",
        "upload-form-label-infoform-title": "جزئیات",
-       "upload-form-label-infoform-name": "نؤم",
+       "upload-form-label-infoform-name": "نۆم",
        "upload-form-label-infoform-description": "توضیحةل",
        "upload-form-label-usage-title": "کاربرد",
        "upload-form-label-usage-filename": "نؤم فایل",
        "foreign-structured-upload-form-label-own-work": "یة کار ووژمة",
-       "foreign-structured-upload-form-label-infoform-categories": "رده ها/ڕِزگةل",
+       "foreign-structured-upload-form-label-infoform-categories": "ڕزگەل",
        "foreign-structured-upload-form-label-infoform-date": "تاریخ",
        "foreign-structured-upload-form-label-own-work-message-local": "تائید می کنم که این پرونده را تحت مجوزها و سیاست‌های {{SITENAME}} بارگذاری می‌کنم.",
        "foreign-structured-upload-form-label-not-own-work-message-local": "اگر امکان بارگذاری پرونده تحت سیاست‌های {{SITENAME}} را ندارید، لطفاً این پنجره را ببندید و از روش‌های دیگر استفاده کنید.",
        "foreign-structured-upload-form-label-own-work-message-shared": "تصدیق می‌کنم که مالک حق تکثیر این پرونده هستم و موافق اشتراک‌گذاری آن تحت مجوز [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] هستم و موافق [https://wikimediafoundation.org/wiki/Terms_of_Use سیاست نحوهٔ استفاده] هستم.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "اگر مالک حق تکثیر این پرونده نیستید یا قصد بارگذاری تحت مجوز دیگری دارید، از [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] استفاده کنید.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "در صورتی که سایت امکان بارگذاری پرونده را تحت سیاست‌ها بارگذاری می‌دهد ممکن است بخواهید از [[Special:Upload|پنجرهٔ بارگذاری در {{SITENAME}}]] استفاده کنید.",
+       "foreign-structured-upload-form-2-label-intro": "از این که تصویری را واگذار می‌کنید تا در {{SITENAME}} استفاده شود متشکریم. شما باید این کار را تنها در صورتی انجام دهید که چندین شرط برقرار باشد:",
+       "foreign-structured-upload-form-2-label-ownwork": "باید تماماً <strong>کار خود شما </strong> باشد، نه این که از اینترنت برداشته باشید",
+       "foreign-structured-upload-form-2-label-noderiv": "باید حاوی چیزی که <strong>دیگران خلق کرده باشند نباشد<strong>، و یا متاثر از اثر کسی دیگر نباشد",
+       "foreign-structured-upload-form-2-label-useful": "این باید <strong>مفید و دانشورانه</strong> برای تدریس به دیگران باشد.",
+       "foreign-structured-upload-form-2-label-ccbysa": "باید <strong>بشود برای همیشه</strong> آن را در اینترنت با مجوز [https://creativecommons.org/licenses/by-sa/4.0/ عامه خلاق با ذکر صاحب اثر و نشر بدون تغییر نسخه ۴٫۰] منتشر کرد",
+       "foreign-structured-upload-form-2-label-alternative": "اگر تمام شرایط بالا برقرار نیست، شما ممکن است کماکان بتوانید آن را از طریق [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] بارگذاری کنید، به شرط آن که تحت یک مجوز آزاد باشد.",
+       "foreign-structured-upload-form-2-label-termsofuse": "با بارگذاری پرونده، شما تایید می‌کنید که صاحب حق تکثیر این پرونده هستید، و قبول می‌کنید که حقوق آن را طبق مجوز عامه خلاق با ذکر صاحب اثر و نشر بدون تغییر نسخه  ۴٫۰ و به صورت غیر قابل برگشت به ویکی‌انبار ببخشید، و نیز [https://wikimediafoundation.org/wiki/Terms_of_Use قوانین استفاده] ویکی‌مدیا را می‌پذیرید.",
+       "foreign-structured-upload-form-3-label-question-website": "آیا شما این تصویر را از یک وب‌سایت دانلود کرده‌اید یا از یک سرویس جستجوی تصویر استفاده کردید؟",
+       "foreign-structured-upload-form-3-label-question-ownwork": "آیا این تصویر را خودتان تولید کردید؟ (عکس گرفتن، طراحی با دست و غیره)",
+       "foreign-structured-upload-form-3-label-question-noderiv": "آیا این اثر متعلق یا مشتق شده از اثر فرد دیگری است مانند نشان؟",
+       "foreign-structured-upload-form-3-label-yes": "أرێ-بةلئ",
+       "foreign-structured-upload-form-3-label-no": "نة-نةخئر",
+       "foreign-structured-upload-form-3-label-alternative": "متاسفانه در این شرایط این ابزار از بارگذاری این پرونده پشتیبانی نمی‌کند.  شما ممکن است کماکان بتوانید آن را از طریق [https://commons.wikimedia.org/wiki/Special:UploadWizard جادوگر بارگذاری ویکی‌انبار] بارگذاری کنید، به شرط آن که تحت یک مجوز آزاد باشد.",
+       "foreign-structured-upload-form-4-label-good": "با استفاده از این ابزار شما می‌توانید تصاویر آموزشی که خود ساخته‌اید یا خودتان عکاسی کرده‌اید را بارگذاری کنید، مادامی که حاوی اثری که دیگری تولید کرده نباشند.",
+       "foreign-structured-upload-form-4-label-bad": "شما نمی‌توانید تصویر بدست آمده از جستجو در موتورهای جستجو یا متعلق به سایر وب‌گاه‌ها را بارگذاری کنید.",
        "backend-fail-stream": "نمی‌توان پروندهٔ $1 را ارسال کرد.",
        "backend-fail-backup": "نمی‌توان نسخهٔ پشتیبان برای پروندهٔ $1 ایجاد کرد",
        "backend-fail-notexists": "پروندهٔ $1 وجود ندارد.",
        "listfiles": "فهرست پرونده‌ها",
        "listfiles_thumb": "چنه کلئکی/گؤجۀر بی",
        "listfiles_date": "تاریخ",
-       "listfiles_name": "نؤم",
+       "listfiles_name": "نۆم",
        "listfiles_user": "کاربۀر",
        "listfiles_size": "اندازۀ",
        "listfiles_description": "توضیحةل",
        "file-anchor-link": "فایل",
        "filehist": "تاریخ پۀروۀندۀ",
        "filehist-help": ".روی تاریخ/زمان‌ها کلیک کنید تا نسخهٔ مربوط به آن هنگام را ببینید",
-       "filehist-deleteall": "حذف کۆل",
+       "filehist-deleteall": "کؤل(گشت) پاکآکە",
        "filehist-deleteone": "حۀذف کردن/پاک کردن",
        "filehist-revert": "واگردانی/گِلآ دائن",
        "filehist-current": "نؤسخهٔ ایسه",
        "filehist-filesize": "قاووارۀ-اندازۀ پرؤندۀ",
        "filehist-comment": "توضیح",
        "imagelinks": "استفادۀ إژ پۀروۀنده",
-       "linkstoimage": "ژئر پیوندۀ دِرِنإ ئئ عۀسگۀ{{PLURAL:$1|وۀلگۀل$1|وۀلگۀ}}",
+       "linkstoimage": "{{PLURAL:$1|وەڵگە|وەڵگەل}} ژێر وە اێ عەسگە پیوەند {{PLURAL:$1|هەنْگِتِێە(دریائە)|هەنْگِتِنە(دریانە)}}:",
        "linkstoimage-more": "بیش از $1 صفحه به این پرونده پیوند {{PLURAL:$1|دارد|دارند}}.\nفهرست زیر تنها {{PLURAL:$1|اولین پیوند|اولین $1 پیوند}} به این صفحه را نشان می‌دهد.\n[[Special:WhatLinksHere/$2|فهرست کامل]] نیز موجود است.",
        "nolinkstoimage": ".این پرونده در هیچ صفحه‌ای به کار نرفته‌است",
        "morelinkstoimage": "[[Special:WhatLinksHere/$1|پیوندهای دیگر]] به این پرونده را ببینید.",
        "unusedtemplates": "الگوهای استفاده‌نشده",
        "unusedtemplatestext": "این صفحه همهٔ صفحاتی در فضای نام {{ns:template}} را که در هیچ صفحه‌ای به کار نرفته‌اند، فهرست می‌کند.\nبه یاد داشته باشید که پیش از پاک‌کردن این صفحات پیوندهای دیگر به آنها را هم وارسی کنید.",
        "unusedtemplateswlh": "پیوندهای دیگر",
-       "randompage": "وةڵگة بةختةکی",
+       "randompage": "وەڵگە بەختەکی",
        "randompage-nopages": "هیچ صفحه‌ای در این {{PLURAL:$2|فضای نام|فضاهای نام}} موجود نیست: $1.",
-       "randomincategory": "وةڵگة بةختةکی در رده/ڕِزگ",
+       "randomincategory": "وەڵگە بەختەکی أڕ ڕِزگ",
        "randomincategory-invalidcategory": "«$1» نامی معتبر برای یک ردهٔ نیست.",
-       "randomincategory-nopages": "  .وجود نئرێ[[:Category:$1|$1]]هؤیج وةڵگة وة رده/ڕِزگ",
-       "randomincategory-category": "رده/ڕِزگ:",
-       "randomincategory-legend": "وةڵگة بةختةکی در رده/ڕِزگ",
+       "randomincategory-nopages": "  .وجود نئرێ[[:Category:$1|$1]]هؤیج وەڵگە وە ڕِزگ",
+       "randomincategory-category": "ڕِزگ:",
+       "randomincategory-legend": "وەڵگە بەختەکی أڕ ڕِزگ",
        "randomincategory-submit": "بِچۆ",
        "randomredirect": "تغییرمسیر تصادفی",
        "randomredirect-nopages": "هیج صفحهٔ تغییرمسیری در فضای نام «$1» موجود نیست.",
        "double-redirect-fixer": "تعمیرکار تغییرمسیرها",
        "brokenredirects": "تغییرمسیرهای خراب",
        "brokenredirectstext": "تغییرمسیرهای زیر به یک صفحهٔ ناموجود پیوند دارند:",
-       "brokenredirects-edit": "دةسکاری",
+       "brokenredirects-edit": "دەسکاری",
        "brokenredirects-delete": "حۀذف کردن/پاک کردن",
        "withoutinterwiki": "صفحه‌های بدون پیوند میان‌ویکی",
        "withoutinterwiki-summary": "این صفحات پیوندی به صفحه‌ای به زبان دیگر نمی‌دارند:",
        "withoutinterwiki-submit": "نیشان دائن",
        "fewestrevisions": "مقاله‌های دارای کمترین شمار ویرایش",
        "nbytes": "$1 {{PLURAL:$1|بایت|بایتۀل}}",
-       "ncategories": "$1{{PLURAL:$1|رده|رده ها}}",
+       "ncategories": "$1{{PLURAL:$1|ڕزگ |ڕزگەل}}",
        "ninterwikis": "$1 {{PLURAL:$1|میان‌ویکیةل|میان‌ویکی}}",
        "nlinks": "$1 {{PLURAL:$1|پیوندةل|پیوند}}",
        "nmembers": "$1 {{PLURAL:$1|member|members}}",
        "mostlinked": "صفحه‌هایی که بیشتر از همه به آن‌ها پیوند داده شده‌است",
        "mostlinkedcategories": "رده‌هایی که بیشتر از همه به آن‌ها پیوند داده شده‌است",
        "mostlinkedtemplates": "بیشترین صفحات تراگنجانده‌شده",
-       "mostcategories": "صفحه‌های دارای بیشترین رده",
+       "mostcategories": "وەڵگەل فرەتەرین ڕزگ دار",
        "mostimages": "پرونده‌هایی که بیشتر از همه به آن‌ها پیوند داده شده‌است",
        "mostinterwikis": "صفحه‌های دارای بیشترین میان‌ویکی",
        "mostrevisions": "صفحه‌های دارای بیشترین نسخه",
-       "prefixindex": "کۆل وةڵگةل با پیشوند",
-       "prefixindex-namespace": "کۆل وةڵگةل دارای پیشوند (فضای‌نام $1)",
+       "prefixindex": "کؤل(گشت)وەڵگەل پێشؤەند دار",
+       "prefixindex-namespace": "کؤل(گشت)وەڵگەل پیشوند دار(فضای‌نۆم $1)",
        "prefixindex-submit": "نیشان دائن",
        "prefixindex-strip": "حذف پیشوند در فهرست",
        "shortpages": "وةڵگةل کؤِڵ/کوتاه",
        "longpages": "وةڵگةل دؤِڕ/دراز",
        "deadendpages": "وةڵگةل بن بست",
        "deadendpagestext": "صفحه‌های زیر به هیچ صفحهٔ دیگری در {{SITENAME}} پیوند ندارند.",
-       "protectedpages": "وةڵگة محافظت/پڵؤم بیة",
+       "protectedpages": "وەڵگە پڵؤم بیە",
        "protectedpages-indef": "فقط محافظت‌های بی‌پایان",
        "protectedpages-summary": "در این صفحه فهرست صفحات موجود است که در حال حاضر محافظت شده اند. برای فهرست عنوان‌هایی که از ایجاد محافظت شده‌اند، به [[{{#special:ProtectedTitles}}|{{int:protectedtitles}}]] مراجعه کنید.",
        "protectedpages-cascade": "فقط محافظت‌های آبشاری",
        "protectedpages-noredirect": "پنهان کردن تغییر مسیرها",
-       "protectedpagesempty": "در Ø­Ø§Ù\84 Ø­Ø§Ø¶Ø± Ù\87Û\8cÚ\86â\80\8cصÙ\81Ø­Ù\87â\80\8cاÛ\8c Ù\85حاÙ\81ظت Ù\86شدÙ\87â\80\8cاست.",
+       "protectedpagesempty": "Ø£ Ø§Û\8cسÛ\95 Ù\87ؤÛ\8cÚ\86â\80\8cÙ\88Û\95ÚµÚ¯Û\95ئ Ù¾ÚµÛ\86Ù\85 Ù\86ؤÛ\8cÛ\95 .",
        "protectedpages-timestamp": "برچسب زمان",
-       "protectedpages-page": "وةڵگة/پەڕە",
+       "protectedpages-page": "وةڵگە(پەڕە)",
        "protectedpages-expiry": "انقضا",
        "protectedpages-performer": "کاربر حفاظت‌کننده",
        "protectedpages-params": "پارامترهای حفاظت",
        "protectedpages-submit": "نمایش وةڵگةل",
        "protectedpages-unknown-timestamp": "ناشنا/نادیار",
        "protectedpages-unknown-performer": "کاربر ناشناس",
-       "protectedtitles": "عÙ\86Ù\88اÙ\86Ø©Ù\84 Ù\85حاÙ\81ظت/پڵؤÙ\85 Ø¨Û\8cØ©",
+       "protectedtitles": "سÛ\95رپÛ\95Ú\95Û\95(عÙ\86Ù\88اÙ\86)Ù¾ÚµÛ\86Ù\85 Ø¨Û\8cÛ\95",
        "protectedtitles-summary": "این صفحه فهرست صفحات موجود است که در حال حاضر محافظت از ساخت شده‌اند. برای فهرست عنوان‌هایی که محافظت از ویرایش شده‌اند، به [[{{#special:ProtectedPages}}|{{int:protectedpages}}]] مراجعه کنید.",
        "protectedtitlesempty": "در حال حاضر هیچ عنوانی با این پارامترها محافظت نشده‌است.",
        "protectedtitles-submit": "نمایش عناوین",
        "usereditcount": "$1 {{PLURAL:$1|ویرایش ها|ویرایش}}",
        "usercreated": "{{GENDER:$3|ایجادشده}} در تاریخ $1 در ساعت $2",
        "newpages": "وۀلگۀ تازۀ",
+       "newpages-submit": "نیشان دائن",
        "newpages-username": ":نؤم کاربری",
        "ancientpages": " کۆنةترین/قدیمی ترین وةڵگةل",
        "move": "جاوواز کرِدِن",
-       "movethispage": "جاÙ\88Ù\88از Ú©Ø±Ø¯Ù\86 Ø¦Ø¦ Ù\88Û\80Ù\84Ú¯Û\80",
+       "movethispage": "جاÙ\88Ù\88از Ú©Ø±Ø¯Ù\86 Ø§Û\8e Ù\88Û\95Ù\84Ú¯Û\95",
        "unusedimagestext": "پرونده‌های زیر موجودند اما در هیچ صفحه‌ای به کار نرفته‌اند.\nلطفاً توجه داشته باشید که دیگر وبگاه‌ها ممکن است با یک نشانی اینترنتی مستقیم به یک پرونده پیوند دهند، و با وجود این که در استفادهٔ فعال هستند در این جا فهرست شوند.",
        "unusedcategoriestext": "این رده‌ها وجود دارند ولی هیچ مقاله یا ردهٔ دیگری از آنها استفاده نمی‌کند.",
        "notargettitle": "مقصدی نیست",
        "apihelp-no-such-module": "پودمان \" $1 \" یافت نشد.",
        "booksources": "منابع کتاب",
        "booksources-search-legend": "جستجوی منابع کتاب",
-       "booksources-search": "گئردین/مهِ نی",
+       "booksources-search": "مِنِی کِردِن(گێردین)",
        "booksources-text": "در زیر فهرستی از پیوندها به وبگاه‌های دیگر آمده‌است که کتاب‌های نو و دست دوم می‌فروشند، و همچنین ممکن است اطلاعات بیشتری راجع به کتاب مورد نظر شما داشته باشند:",
        "booksources-invalid-isbn": "شابک داده شده مجاز به نظر نمی‌رسد؛ از جهت اشکالات هنگام کپی کردن از منبع اصلی بررسی کنید.",
        "specialloguserlabel": "مجری:",
        "speciallogtitlelabel": "هدف (عنوان یا {{ns:user}}:نام کاربر برای کاربر):",
        "log": "سیاهه‌ها",
+       "logeventslist-submit": "نیشان دائن",
        "all-logs-page": "تمام سیاهه‌های عمومی",
        "alllogstext": "نمایش یک‌جای تمام سیاهه‌های موجود در {{SITENAME}}.\nمی‌توانید با انتخاب نوع سیاهه، نام کاربری (حساس به کوچکی و بزرگی حروف) و صفحه‌های تغییریافته (حساس به بزرگی و کوچکی حروف)، نمایش را محدودتر سازید.",
        "logempty": "مورد منطبق با منظور شما در سیاهه یافت نشد.",
        "log-title-wildcard": "صفحه‌هایی را جستجو کن که عنوانشان با این عبارت آغاز می‌شود",
        "showhideselectedlogentries": "تغییر پدیداری موارد انتخاب‌شده سیاهه",
        "log-edit-tags": "ویرایش برچسب سیاههٔ انتخاب شده",
-       "allpages": "کۆل وةڵگةل",
+       "allpages": "کؤل(گشت)وەڵگەل",
        "nextpage": "وةڵگةبعد ($1)",
        "prevpage": "وةڵگة قبلی ($1)",
        "allpagesfrom": "نمایش وةڵگةل با شروع إژ:",
        "allpagesto": "نمایش صفحات با پایان در:",
-       "allarticles": "کول وۀلگۀل",
+       "allarticles": "کؤل(گشت)وەڵگەل",
        "allinnamespace": "همهٔ صفحات (فضای نام $1)",
        "allpagessubmit": "بِچۆ",
        "allpagesprefix": "نمایش صفحه‌های دارای پیشوند:",
        "cachedspecial-viewing-cached-ts": "شما در حال مشاهدهٔ نسخه‌ای از این صفحه که در میانگیر قرار دارد هستید، و این نسخه ممکن است کاملاً واقعی نباشد.",
        "cachedspecial-refresh-now": "مشاهده آخرین.",
        "categories": "رده‌ ل",
+       "categories-submit": "نیشان دائن",
        "categoriespagetext": "{{PLURAL:$1|ردهٔ|رده‌های}} زیر دارای صفحات یا پرونده‌هایی {{PLURAL:$1|است|هستند}}.\n[[Special:UnusedCategories|رده‌های استفاده‌نشده]] در اینجا نمایش داده نشده‌اند.\nهمچنین [[Special:WantedCategories|رده‌های مورد نیاز]] را ببینید.",
        "categoriesfrom": "نمایش رده‌ها با شروع از:",
        "special-categories-sort-count": "مرتب کردن بر اساس تعداد",
        "linksearch": "جستجوی پیوندهای بیرونی",
        "linksearch-pat": "الگوی جستجو:",
        "linksearch-ns": "فضای نام:",
-       "linksearch-ok": "گئردین/مهِ نی",
+       "linksearch-ok": "مِنِی کِردِن(گێردین)",
        "linksearch-text": "نشانه‌هایی مانند «‎*.wikipedia.org» را می‌توان استفاده کرد.\nحداقل یک دامنه سطح بالا ، به عنوان مثال \"*.org\" نیاز دارد.<br />\n{{PLURAL:$2|پروتکل|پروتکل‌های}} پشتیبانی‌شده: $1 (پیش‌فرض برای http:// در صورت مشخص نشدن پروتکل تنظیم شده‌است).",
        "linksearch-line": "$1 از $2 پیوند دارد",
        "linksearch-error": "نشانه‌ها فقط در ابتدای نام میزبان اینترنتی می‌توانند استفاده شوند.",
        "listgrouprights-namespaceprotection-header": "محدودیت فضای نام",
        "listgrouprights-namespaceprotection-namespace": "فضای نام",
        "listgrouprights-namespaceprotection-restrictedto": "دسترسی(های) مجاز کاربر برای ویرایش",
-       "trackingcategories": "تؤرگِرتنردیابی رده/ڕِزگ\n)رده/ڕِزگ تؤرگِرتن(ردیابی",
+       "trackingcategories": "ڕزگەل تؤڕگِقفِن(ردیاب)",
        "trackingcategories-summary": "این صفحه فهرست رده‌هایی ردیابی است که به‌صورت خودکار توسط مدیاویکی پر می‌شوند است. نام‌هایشان می‌تواند پس از تغییر پیام‌های سامانه‌ای مرتبط در فضای نام {{ns:8}} تغییر یابد.",
-       "trackingcategories-msg": "تؤرگِرتن(ردیابی) رده/ڕِزگ",
+       "trackingcategories-msg": "تؤڕگِرتن(ردیابی) ڕِزگ",
        "trackingcategories-name": "نام پیغام",
        "trackingcategories-desc": "معیارهای گنجایش رده",
        "noindex-category-desc": "این صفحه توسط ربات‌ها فهرست‌نشده‌است به این دلیل که واژه جادویی <code><nowiki>__NOINDEX__</nowiki></code> در آن یا در فضای که پرچم مجاز است دارد.",
        "broken-file-category-desc": "صفحه شامل یک پیوند پرونده خراب است (پیوندی برای جاسازی‌کردن یک پرونده هنگامی که آن پرونده وجود ندارد).",
        "hidden-category-category-desc": "رده در محتوای صفحه‌اش شامل <code><nowiki>__HIDDENCAT__</nowiki></code> است، که از به طور پیش‌فرض نشان‌دادن جعبه پیوندهای رده در صفحات جلوگیری می‌کند.",
        "trackingcategories-nodesc": "هیچ شرحی وة دةسرةس نیة.",
-       "trackingcategories-disabled": "رده غیرفعال‌شده است",
+       "trackingcategories-disabled": "ڕِزگ هووساسآ(غیرفعال بیە)",
        "mailnologin": "نشانی‌ای از فرستنده موجود نیست",
        "mailnologintext": "برای ارسال ایمیل به کاربران دیگر باید [[Special:UserLogin|به سیستم وارد شوید]] و آدرس ایمیل معتبری در [[Special:Preferences|ترجیحات]] خود داشته باشید.",
        "emailuser": "ایمیل به این کاربر",
        "nowikiemailtext": "این کاربر انتخاب کرده که از دیگر کاربران ایمیل دریافت نکند.",
        "emailnotarget": "نام کاربری ناموجود یا نامعتبر برای گیرنده.",
        "emailtarget": "نام کاربری دریافت‌کننده را وارد کنید",
-       "emailusername": ":نؤم کاربری",
+       "emailusername": ":نۆم کاربەری",
        "emailusernamesubmit": "ارسال/کِل کردن",
        "email-legend": "ارسال ایمیل به کاربر دیگر {{SITENAME}}",
        "emailfrom": ":إژ",
        "removedwatchtext": "صفحهٔ «[[:$1]]» و صفحهٔ بحث آن از [[Special:Watchlist|فهرست پی‌گیری‌های شما]] برداشته شد.",
        "removedwatchtext-short": "صفحهٔ \"$1\" از فهرست پیگیری‌های شما حذف شده‌است.",
        "watch": "پی‌گیری",
-       "watchthispage": "پی‌گیری ئئ وۀلگۀ",
+       "watchthispage": "پئ گیری اێ وەڵگە",
        "unwatch": "توقف پی‌گیری",
        "unwatchthispage": "توقف پی‌گیری",
        "notanarticle": "صفحه محتوایی نیست",
        "wlshowhideanons": " کاربرۀل ناشنا/نادیاری",
        "wlshowhidepatr": "ویرایش‌های گشت‌خورده",
        "wlshowhidemine": "دةسکاریةل مإ",
+       "wlshowhidecategorization": "رده‌بندی(رزگ بنی) صفحه‌ها",
        "watchlist-options": "گزینه‌های پی‌گیری",
        "watching": "...پی‌گیری",
        "unwatching": "توقف پی‌گیری...",
        "enotif_lastdiff": "برای نمایش این تغییر $1 را ببینید.",
        "enotif_anon_editor": " کاربرۀل ناشنا/نادیاری$1",
        "enotif_body": "$WATCHINGUSERNAME گرامی،\n\n$PAGEINTRO $NEWPAGE\n\n\nتوضیح ویراستار: $PAGESUMMARY $PAGEMINOREDIT\n\nتماس با ویراستار:\nنامه: $PAGEEDITOR_EMAIL\nویکی: $PAGEEDITOR_WIKI\n\nتا هنگامی که به صفحه سر نزده‌اید، در صورت رخ‌دادنِ احتمالیِ فعالیت بیشتر، تا زمانی که در با کاربریتان در سامانه هستید، اعلانیه‌ای برای شما فرستاده نخواهد شد.\nشما همچنین می‌توانید در صفحهٔ پی‌گیری‌های خود پرچم‌های مربوط به آگاهی‌رسانی را صفر کنید همچنین می‌توانید پرچم‌های آگاهی‌سازی را بازنشانی کنید.\n\nدوستدار شما، سامانهٔ آگاهی‌رسانی {{SITENAME}}\n\n--\nبرای تغییر تنظیمات فهرست ایمیل‌های اعلان به {{canonicalurl:{{#special:EditWatchlist}}}} بروید.\n\nبرای تغییر تنظیمات فهرست پی‌گیری‌هایتان به {{canonicalurl:{{#special:EditWatchlist}}}} بروید.\n\nبرای حذف صفحه از فهرست پی‌گیری‌هایتان به $UNWATCHURL بروید.\n\nبازخورد و کمک بیشتر:\n$HELPPAGE",
-       "deletepage": "حۀذف وةڵگة",
+       "deletepage": "وەڵگە پاکآکە",
        "confirm": "تأیید",
        "excontent": "محتوای وةڵگة این بود: «$1»",
        "excontentauthor": "محتوای صفحه این بود: «$1» و تنها مشارکت‌کننده «[[Special:Contributions/$2|$2]] ([[User talk:$2|بحث]])» بود",
        "delete-confirm": "حذف «$1»",
        "delete-legend": "حۀذف کردن/پاک کردن",
        "historywarning": "<strong>هشدار:</strong> صفحه‌ای که در حال پاک‌کردن آن هستید دارای یک تاریخچه همراه $1 {{PLURAL:$1|بازبینی|بازبینی‌ها}} است:",
+       "historyaction-submit": "نیشان دائن",
        "confirmdeletetext": "شما در حال حذف کردن یک صفحه یا تصویر از پایگاه داده‌ها همراه با تمام تاریخچهٔ آن هستید.\nلطفاً این عمل را تأیید کنید و اطمینان حاصل کنید که عواقب این کار را می‌دانید و این عمل را مطابق با [[{{MediaWiki:Policy-url}}|سیاست‌ها]] انجام می‌دهید.",
        "actioncomplete": "عملكرد كامل بيه",
        "actionfailed": "عمل ناموفق بود",
        "sessionfailure-title": "خطای نشست کاربری",
        "sessionfailure": "به نظر می‌رسد مشکلی در مورد نشست کاربری شما وجود دارد؛\nعمل درخواست شده در اقدامی پیشگیرانه در برابر ربوده‌شدن اطلاعات نشست کاربری، لغو شد.\nلطفاً دکمهٔ «بازگشت» را در مرورگر خود بفشارید و صفحه‌ای که از آن به اینجا رسیده‌اید را دوباره فراخوانی کنید، سپس مجدداً سعی کنید.",
        "changecontentmodel": "ویرایش نمونه محتوای یک صفحه",
-       "changecontentmodel-legend": "تغییر نوع محتوی",
-       "changecontentmodel-title-label": "عنوان وةڵگة",
+       "changecontentmodel-legend": "گؤەڕانن/تغییر نوع محتوی",
+       "changecontentmodel-title-label": "عنوان وەڵگە",
        "changecontentmodel-model-label": "نمونه محتوای جدید",
        "changecontentmodel-reason-label": ":دةلیل",
-       "changecontentmodel-success-title": "نمونه محتوی تغییر یافت",
+       "changecontentmodel-success-title": "نمونه محتوی گؤەڕیا/تغییر یافت",
        "changecontentmodel-success-text": "نوع محتوی [[:$1]]  تغییر یافت",
        "changecontentmodel-cannot-convert": "محتوی در [[:$1]] نمی‌تواند به گونه‌ای از $2 تبدیل شود.",
        "changecontentmodel-nodirectediting": "نمونه محتوی $1 امکان ویرایش مستقیم را پشتیبانی نمی‌کند",
        "logentry-contentmodel-change": "نمونه محتوای صفحهٔ $3 از \"$4\" به \"$5\" توسط $1 {{GENDER:$2|تغییر داده شد}}",
        "logentry-contentmodel-change-revertlink": "واگردانی/گِلآ دائن",
        "logentry-contentmodel-change-revert": "واگردانی/گِلآ دائن",
-       "protectlogpage": "سیاههٔ محافظت",
+       "protectlogpage": "گزارشت پڵۆم کردن",
        "protectlogtext": "در زیر فهرستی از تغییرات سطح محافظت صفحه‌ها آمده‌است.\n[[Special:ProtectedPages|فهرست صفحه‌های محافظت‌شده]] را برای دیدن فهرست محافظت‌های مؤثر صفحه‌ها ببینید.",
-       "protectedarticle": "«[[$1]]» را محافظت کرد",
-       "modifiedarticleprotection": "Ù\88ضعÛ\8cت Ù\85حاÙ\81ظت Â«[[$1]]» Ø±Ø§ ØªØºÛ\8cÛ\8cر Ø¯Ø§Ø¯",
-       "unprotectedarticle": "صفحهٔ «[[$1]]» را از محافظت بیرون آورد",
+       "protectedarticle": "«[[$1]]» پڵۆم کِردێ",
+       "modifiedarticleprotection": "Ù\88ضعÛ\8cت Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86«[[$1]]»گؤÛ\95Ú\95اÙ\86Û\8e(تغÛ\8cÛ\8cر Ø¯Ø§Ø¯)",
+       "unprotectedarticle": "وەڵگە«[[$1]]» إژ  پڵۆم کردن آووردێ دەر",
        "movedarticleprotection": "تنظیمات محافظت را از «[[$2]]» به «[[$1]]» منتقل کرد",
-       "protect-title": "تغییر وضعیت محافظت «$1»",
+       "protect-title": "وضعیت پڵۆم کردن \"$1\" گؤەڕائە(تغییریافتە)",
        "protect-title-notallowed": "مشاهده سطح حفاظت  \" $1 \"",
        "prot_1movedto2": "[[$1]] به [[$2]] منتقل بی",
        "protect-badnamespace-title": "فضای نام بدون محافظت",
        "protect-badnamespace-text": "صفحه‌های موجود در این فضای نام، نمی‌توانند محافظت شوند.",
        "protect-norestrictiontypes-text": "امکان محافظت این صفحه به علت نبودن نوع محدودیت، مقدور نیست.",
-       "protect-norestrictiontypes-title": "صفحهٔ غیرقابل محافظت",
-       "protect-legend": "تأÛ\8cÛ\8cد Ù\85حاÙ\81ظت",
+       "protect-norestrictiontypes-title": "وەڵگە غیرقابل پڵۆم کردن",
+       "protect-legend": "تأÛ\8cÛ\8cد Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86",
        "protectcomment": ":دةلیل",
        "protectexpiry": "زمان سرآمدن:",
        "protect_expiry_invalid": "زمان سرآمدن نامعتبر است.",
        "protect-expiring": "زمان سرآمدن $1 (UTC)",
        "protect-expiring-local": "منقضی $1",
        "protect-expiry-indefinite": "بی‌پایان",
-       "protect-cascade": "Ù\85حاÙ\81ظت Ø¢Ø¨Ø´Ø§Ø±Û\8c - Ø§Ø² Ù\87Ù\85Ù\87Ù\94 ØµÙ\81Ø­Ù\87â\80\8cÙ\87اÛ\8cÛ\8c Ú©Ù\87 Ø¯Ø± Ø§Û\8cÙ\86 ØµÙ\81Ø­Ù\87 Ø¢Ù\85دÙ\87â\80\8cاÙ\86د Ù\86Û\8cز Ù\85حاÙ\81ظت Ù\85Û\8câ\80\8cØ´Ù\88د.",
+       "protect-cascade": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø¢Ø¨Ø´Ø§Ø±Û\8c(تاÙ\81Ú¯Û\95ئ)- Ú©Ø¤Ù\84(گشت)Ù\88Û\95ÚµÚ¯Û\95Ù\84Û\8e Ú¯Ø¥ Ù\87Û\95تÙ\86 Ø§Û\8e Ù\88Û\95ÚµÚ¯Û\95 Ù¾ÚµÛ\86Ù\85 Ù\85Û\95Ú©Ø¥.",
        "protect-cantedit": "شما نمی‌تواند وضعیت محافظت این صفحه را تغییر دهید، چون اجازه ویرایش آن را ندارید.",
        "protect-othertime": "زمانی دیگر:",
        "protect-othertime-op": "زمانی دیگر",
        "protect-otherreason": "دلیل دیگر/اضافی:",
        "protect-otherreason-op": "دلیل دیگر",
        "protect-dropdown": "*دلایل متداول محافظت\n** خرابکاری گسترده\n** هرزنگاری گسترده\n** جنگ ویرایشی غیر سازنده\n** صفحهٔ پر بازدید",
-       "protect-edit-reasonlist": "ویرایش دلایل محافظت",
+       "protect-edit-reasonlist": "دەسکاری دلایل پڵۆم کردن",
        "protect-expiry-options": "۱ ساعت:1 hour,۱ روز:1 day,۱ هفته:1 week,۲ هفته:2 weeks,۱ ماه:1 month,۳ ماه:3 months,۶ ماه:6 months,۱ سال:1 year,بی‌پایان:infinite",
        "restriction-type": "دسترسی:",
        "restriction-level": "سطح محدودیت:",
        "minimum-size": "حداقل اندازه",
        "maximum-size": "حداکثر اندازه:",
        "pagesize": "(بایت)",
-       "restriction-edit": "دةسکاری",
+       "restriction-edit": "دەسکاری",
        "restriction-move": "جاوواز کرِدِن",
        "restriction-create": "دؤِرس کردن/سازین",
        "restriction-upload": "بارگذاری",
        "undelete-search-title": "جستجوی صفحه‌های حذف‌شده",
        "undelete-search-box": "جستجوی صفحه‌های حذف‌شده",
        "undelete-search-prefix": "نمایش صفحات با شروع از:",
-       "undelete-search-submit": "گئردین/مهِ نی",
+       "undelete-search-submit": "مِنِی کِردِن(گێردین)",
        "undelete-no-results": "هیچ صفحهٔ منطبقی در بایگانی حذف‌شده‌ها یافت نشد.",
        "undelete-filename-mismatch": "امکان احیای نسخهٔ $1 وجود ندارد: نام پرونده مطابقت نمی‌کند.",
        "undelete-bad-store-key": "امکان احیای نسخهٔ $1 وجود ندارد: پرونده قبل از حذف از دست رفته بود.",
        "sp-contributions-deleted": "مشارکت‌های حذف‌شدهٔ کاربر",
        "sp-contributions-uploads": "بارگذاری‌ها",
        "sp-contributions-logs": "سیاهه‌ها",
-       "sp-contributions-talk": "قسۀ-گۀپ",
+       "sp-contributions-talk": "گەپ(قسە)",
        "sp-contributions-userrights": "مدیریت اختیارات کاربر",
        "sp-contributions-blocked-notice": "این کاربر در حال حاضر بسته شده‌است.\nآخرین سیاههٔ بسته شدن در زیر آمده‌است:",
        "sp-contributions-blocked-notice-anon": "این نشانی آی‌پی در حال حاضر بسته است.\nآخرین سیاههٔ بسته شدن در زیر آمده‌است:",
        "sp-contributions-username": "نشانی آی‌پی یا نام کاربری:",
        "sp-contributions-toponly": "فقط ویرایش‌هایی که آخرین نسخه‌اند نمایش داده شود",
        "sp-contributions-newonly": "فقط نمایش ویرایش‌هایی که ایجاد صفحه هستند",
-       "sp-contributions-submit": "گئردین/مهِ نی",
-       "whatlinkshere": "Ù¾Û\8cÙ\88Ù\86دÛ\80Ù\84 Ù\88Û\80 Ø¦Ø¦ Ù\88ةڵگة",
+       "sp-contributions-submit": "مِنِی کِردِن(گێردین)",
+       "whatlinkshere": "Ù¾Û\8cÙ\88Ù\86دÛ\95Ù\84 Ù\88Û\95 Ø¦Û\8e Ù\88Û\95ÚµÚ¯Û\95",
        "whatlinkshere-title": "وۀلگۀلئ گإ  وۀ «$1» پیوۀند دِرِن",
-       "whatlinkshere-page": ":Ù¾Û\95Ú\95Û\95\88ةڵگة",
+       "whatlinkshere-page": ":Ù\88Ø©ÚµÚ¯Û\95(Ù¾Û\95Ú\95Û\95)",
        "linkshere": "The following pages link to <strong>[[:$1]]</strong>:",
        "nolinkshere": "هیچ صفحه‌ای به '''[[:$1]]''' پیوند ندارد.",
        "nolinkshere-ns": "هیچ صفحه‌ای از فضای نام انتخاب شده به '''[[:$1]]''' پیوند ندارد.",
-       "isredirect": "صفحهٔ تغییرمسیر",
+       "isredirect": "وەڵگە ڕێ گؤەڕن(تغییرمسییر)",
        "istemplate": " تراگنجانش‌ها",
        "isimage": "پیوند پرونده",
        "whatlinkshere-prev": "{{PLURAL:$1|previous|previous $1}}",
        "blocklist-by": "مدیر قطع دسترسی کننده",
        "blocklist-params": "پارامترهای بستن",
        "blocklist-reason": "دةلیل",
-       "ipblocklist-submit": "گئردین/مهِ نی",
+       "ipblocklist-submit": "مِنِی کِردِن(گێردین)",
        "ipblocklist-localblock": "قطع دسترسی محلی",
        "ipblocklist-otherblocks": "سایر {{PLURAL:$1|بستن‌ها|بستن‌}}",
        "infiniteblock": "بی‌پایان",
        "block-log-flags-noemail": "ایمیل بسته‌شد",
        "block-log-flags-nousertalk": "صفحهٔ بحث خود را نمی‌تواند ویرایش کند",
        "block-log-flags-angry-autoblock": "قطع دسترسی خودکار پیشرفته فعال شد",
-       "block-log-flags-hiddenname": "نام کاربری پنهان",
+       "block-log-flags-hiddenname": "نۆم کاربەری آشاریا",
        "range_block_disabled": "بستن یک بازه توسط مدیران غیر فعال است.",
        "ipb_expiry_invalid": "زمان سرآمدن نامعتبر.",
        "ipb_expiry_temp": "قطع دسترسی کاربرهای پهنان باید همیشگی باشد.",
        "databasenotlocked": "پایگاه داده قفل نیست.",
        "lockedbyandtime": "(به وسیلهٔ $1 در $2 ساعت $3)",
        "move-page": "انتقال $1",
-       "move-page-legend": "جاوواز کردن وةڵگة",
+       "move-page-legend": "جاوواز کردن وەڵگە",
        "movepagetext": "با استفاده از فرم زیر نام صفحه تغییر خواهد کرد، و تمام تاریخچه‌اش به نام جدید منتقل خواهد شد.\nعنوان قدیمی تبدیل به یک صفحهٔ تغییرمسیر به عنوان جدید خواهد شد.\nشما می‌توانید تغییرمسیرهایی که به عنوان اصلی اشاره دارند را به صورت خودکار به‌روزرسانی کنید.\nپیوندهای که به عنوان صفحهٔ قدیمی وجود دارند، تغییر نخواهند کرد؛ حتماً تغییرمسیرهای [[Special:DoubleRedirects|دوتایی]] یا [[Special:BrokenRedirects|خراب]] را بررسی کنید.\n'''شما''' مسئول اطمینان از این هستید که پیوندها هنوز به همان‌جایی که قرار است بروند.\n\nتوجه کنید که اگر از قبل صفحه‌ای در عنوان جدید وجود داشته باشد صفحه منتقل '''نخواهد شد'''،\nمگر این آخرین ویرایش تغییرمسیر باشد و در  تاریخچهٔ ویرایشی نداشته باشد.\nاین یعنی اگر اشتباه کردید می‌توانید صفحه را به همان جایی که از آن منتقل شده بود برگردانید، و این که نمی‌توانید روی صفحات موجود بنویسید.\n\n'''هشدار!'''\nانتقال صفحات به نام جدید ممکن است تغییر اساسی و غیرمنتظره‌ای برای صفحات محبوب باشد؛\nلطفاً مطمئن شوید که قبل از انتقال دادن صفحه، عواقب این کار را درک می‌کنید.",
        "movepagetext-noredirectfixer": "استفاده از فرم زیر سبب تغییر نام یک صفحه و انتقال تمام تاریخچهٔ آن به نام جدید می‌شود.\nعنوان پیشین تغییرمسیری به عنوان جدید خواهد شد.\nبه خاطر داشته باشید که [[Special:DoubleRedirects|تغییرمسیرهای دوتایی]] یا [[Special:BrokenRedirects|تغییرمسیرهای خراب]] را بررسی کنید.\nشما مسئولید که مطمئن شوید پس از انتقال، پیوندها به عنوان پیشین به جایی منتهی می‌شوند که باید.\n\nتوجه کنید که اگر صفحه‌ای تحت عنوان جدید از قبل موجود باشد، انتقال انجام '''نخواهد شد'''، مگر اینکه صفحه خالی و یا تغییرمسیر باشد و تاریخچهٔ ویرایشی دیگری نداشته باشد.\nاین یعنی اگر صفحه را به نامی اشتباه منتقل کردید می‌توانید این تغییر را واگردانی کنید، اما نمی‌توانید یک صفحه را به صفحه‌ای که از قبل موجود است انتقال دهید.\n\n'''هشدار!'''\nانتقال صفحه‌های پربیننده ممکن است عملی غیرمنتظره باشد؛\nلطفاً پیش از انتقال مطمئن شوید از نتیجهٔ کار آگاهید.",
        "movepagetalktext": "اگر این گزینه را انتخاب کنید، صفحهٔ بحث مرتبط به صورت خودکار انتقال داده می‌شود به عنوان جدید و در صورت عدم انتخاب گزینه، صفحهٔ بحث جدید یک صفحهٔ خالی خواهد بود و در این حالت، باید صفحه را بطور دستی انتقال داده و یا محتویات دو صفحه را با ویرایش ادغام کنید.",
        "cant-move-to-category-page": "شما مجوز برای انتقال صفحه به صفحه رده ندارید.",
        "newtitle": "عنوان تازه:",
        "move-watch": "پی‌گیری صفحه‌های مبدأ و مقصد",
-       "movepagebtn": "جاوواز کردن وةڵگة",
+       "movepagebtn": "جاوواز کردن وەڵگە",
        "pagemovedsub": "انتقال با موفقیت انجام شد",
        "movepage-moved": "'''«$1» به «$2» منتقل شد'''",
        "movepage-moved-redirect": "یک تغییرمسیر ایجاد شد.",
        "exportnohistory": "----\n'''توجه:''' امکان برون‌بری تاریخچهٔ کامل صفحات از طریق این صفحه به دلایل اجرایی از کار انداخته شده‌است.",
        "exportlistauthors": "شامل فهرست کامل مشارکت‌کنندگان هر صفحه",
        "export-submit": "در بِردن",
-       "export-addcattext": ":افزودن وةڵگةل إژ رده/ڕِزگ",
+       "export-addcattext": ":افزودن وەڵگەل إژ ڕِزگ",
        "export-addcat": "افزودن",
        "export-addnstext": "افزودن صفحات از فضای نام:",
        "export-addns": "افزودن",
-       "export-download": "ذخÛ\8cرÙ\87 Ø¨Ù\87 ØµÙ\88رت Ù¾Ø±Ù\88نده",
+       "export-download": "بÙ\90Û\8cÙ\84(ذخÛ\8cرÙ\87 Ú©Û\95\88Û\95 Ø¹Ù\86Ù\88اÙ\86 Ù¾Û\95رÙ\88Û\95نده",
        "export-templates": "شامل شدن الگوها",
        "export-pagelinks": "شامل شدن صفحه‌های پیوند شده تا این عمق:",
        "allmessages": "پیغام‌های سامانه",
-       "allmessagesname": "نؤم",
+       "allmessagesname": "نۆم",
        "allmessagesdefault": "متن پیش‌فرض پیغام",
        "allmessagescurrent": "متن کنونی پیغام",
        "allmessagestext": "این فهرستی از پیغام‌های سامانه‌ای موجود در فضای نام مدیاویکی است.\nچنانچه مایل به مشارکت در محلی‌سازی مدیاویکی هستید لطفاً [https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation محلی‌سازی مدیاویکی] و [//translatewiki.net translatewiki.net] را ببینید.",
        "allmessagesnotsupportedDB": "این صفحه نمی‌تواند استفاده شود به این دلیل که <bdi>'''$wgUseDatabaseMessages'''</bdi> غیرفعال شده‌است.",
        "allmessages-filter-legend": "پالانۀل/فیلترۀل",
        "allmessages-filter": "پالودن بر اساس وضعیت شخصی‌سازی:",
-       "allmessages-filter-unmodified": "تغییر نیافته",
-       "allmessages-filter-all": "کۆل",
-       "allmessages-filter-modified": "تغییر یافته",
+       "allmessages-filter-unmodified": "نگؤەڕیائە/تغییرنیافته",
+       "allmessages-filter-all": "کؤل(گشت)",
+       "allmessages-filter-modified": "گؤەڕیائە/تغییریافته",
        "allmessages-prefix": "پالودن بر اساس پسوند:",
        "allmessages-language": ":زوون",
        "allmessages-filter-submit": "بِچۆ",
-       "allmessages-filter-translate": "Ú\86اÙ\88Ù\88اشÛ\80گر زوون",
+       "allmessages-filter-translate": "Ú\86اÙ\88Ù\88اشÛ\95Ú©Ù\90ردÙ\86 زوون",
        "thumbnail-more": "کۀلنگ کِردن",
        "filemissing": "پرونده وجود ندارد",
        "thumbnail_error": "خطا در ایجاد بندانگشتی: $1",
        "importinterwiki": "درون‌ریزی از یک ویکی دیگر",
        "import-interwiki-text": "یک ویکی و یک نام صفحه را انتخاب کنید تا اطلاعات از آن درون‌ریزی شود.\nتاریخ نسخه‌ها و نام ویرایش‌کنندگان ثابت خواهد ماند.\nاطلاعات مربوط به درون‌ریزی صفحات از ویکی دیگر در [[Special:Log/import|سیاههٔ درون‌ریزی‌ها]] درج خواهد شد.",
        "import-interwiki-sourcewiki": "ویکی منبع:",
-       "import-interwiki-sourcepage": "صفحهٔ مبدأ:",
+       "import-interwiki-sourcepage": "وەڵگە بنچێنە:",
        "import-interwiki-history": "تمام نسخه‌های تاریخچهٔ این صفحه انتقال داده شود",
        "import-interwiki-templates": "تمام الگوها را شامل شود",
        "import-interwiki-submit": "درون‌ریزی شود",
        "javascripttest-qunit-intro": "[$1 مستندات آزمایش] را در mediawiki.org ببینید.",
        "tooltip-pt-userpage": "وةڵگة کاربۀری هؤمۀ",
        "tooltip-pt-anonuserpage": "صفحهٔ کاربری نشانی آی‌پی‌ای که با آن ویرایش می‌کنید",
-       "tooltip-pt-mytalk": "Ù\88Û\80Ù\84Ú¯Û\80 Ú¯Û\80Ù¾ Ù\87ؤÙ\85Û\80",
+       "tooltip-pt-mytalk": "Ù\88Û\95ÚµÚ¯Û\95 Ú¯Û\95Ù¾(Ù\82سÛ\95\87Û\86Ù\85Û\95",
        "tooltip-pt-anontalk": "بحث پیرامون ویرایش‌های این نشانی آی‌پی",
-       "tooltip-pt-preferences": "ترجیحات ووِژم",
+       "tooltip-pt-preferences": "تمارزووەل(ترجیحات)ووِژم",
        "tooltip-pt-watchlist": "فهرست صفحه‌هایی که شما تغییرات آن‌ها را پی‌گیری می‌کنید",
        "tooltip-pt-mycontris": "فهرست مشارکت‌های شما",
        "tooltip-pt-anoncontribs": "لیست دةسکاریةل دؤرس بی/سازریا إژ ئئ آدرس ای پی",
        "tooltip-pt-login": "توصیه مۀکیم بونإ نام سامانه ، هۀرچۀند اجباری نیۀ",
-       "tooltip-pt-logout": "دةرچئن/خروج",
+       "tooltip-pt-logout": "دەرچێن|خروج",
        "tooltip-pt-createaccount": "مکئس تانۀ مۀکیم حساووئ بسازن و بونإ سامانۀ؛ هرچۀند حساوو کاربری سازین دل .بخوایۀ",
-       "tooltip-ca-talk": "Ú¯Û\80Ù¾/Ù\82سÛ\80 Ø¯Û\80ربارÛ\80 Ø¨Ù\86Ú\86Û\80Ú©/Ù\85حتÙ\88ا Ù\88Û\80Ù\84Ú¯Û\80",
-       "tooltip-ca-edit": "ئئ Ù\88Û\80Ù\84Ú¯Û\80 Ø¯Û\80سکارÛ\8c Ú©Û\80Ù\86",
+       "tooltip-ca-talk": "Ú¯Û\95Ù¾(Ù\82سÛ\95)دÛ\95ربارÛ\95 Ù\86Û\86Ù\85 Ø¬Ù\90Ú©(Ù\85حتÙ\88ا)Ù\88Û\95ÚµÚ¯Û\95",
+       "tooltip-ca-edit": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ø¯Û\95سکارÛ\8c Ú©Û\95",
        "tooltip-ca-addsection": "بۀخش تازه بسازِن",
        "tooltip-ca-viewsource": ".ئئ وۀلگۀ محافظۀت بیۀ\nمۀتؤنین  متن مبدأ/بنچۀک بؤینین",
        "tooltip-ca-history": "ورژن دؤمائن/پئش ئئ وۀلگۀ",
-       "tooltip-ca-protect": "Ù\85حاÙ\81ظت Ø¥Ú\98 Ø¦Ø¦ Ù\88ةڵگة",
-       "tooltip-ca-unprotect": "تغییر محافظت ئئ وةڵگة",
-       "tooltip-ca-delete": "حةذف ئئ وةڵگة",
+       "tooltip-ca-protect": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø§Û\8e Ù\88Û\95ÚµÚ¯Û\95",
+       "tooltip-ca-unprotect": "گؤەڕانن(تغییر)پڵۆم کردن اێ وەڵگە",
+       "tooltip-ca-delete": "حەذف اێ وەڵگە",
        "tooltip-ca-undelete": "بازگرداندن نسخه‌های صفحهٔ حذف‌شده",
-       "tooltip-ca-move": "جاÙ\88Ù\88از Ú©Ø±Ø¯Ù\86 Ø¦Ø¦ Ù\88Û\80Ù\84Ú¯Û\80",
+       "tooltip-ca-move": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ø¬Ø§Ù\88Ù\88آز Ú©Û\95",
        "tooltip-ca-watch": "اضافۀ کردن ئئ وۀلگۀ وۀ لیست پیگیریۀل تان",
        "tooltip-ca-unwatch": "حذف کردن ئئ وۀلگۀ وۀ لیست پیگیریۀل تان",
        "tooltip-search": " {{SITENAME}}مِنِی کرد أ نوم",
        "tooltip-search-go": "بچؤ وۀلگۀ گإ نام راسکانی/دقیق هۀسئ",
-       "tooltip-search-fulltext": "Ù\85Ù\90Ù\86Ù\90Û\8c Ú©Ø±Ø¯Ù\86 Ù\88Û\80Ù\84Ú¯Û\80Ù\84/Ù¾Û\80رÛ\80Ù\84 Ø¦Û\80را Ø¦Ø¦ Ù\85Û\80تÙ\86Û\80",
+       "tooltip-search-fulltext": "Ù\85Ù\90Ù\86Ù\90Û\8c Ú©Ø±Ø¯Ù\86 Ù\88Û\95ÚµÚ¯Û\95Ù\84(Ù¾Û\95Ú\95Û\95Ù\84)Ø£Ú\95ا Ø§Û\8e Ù\85Û\95تÙ\86Û\95",
        "tooltip-p-logo": "سەر پەڕە بوینن",
        "tooltip-n-mainpage": "سەر پەڕە بوینن",
        "tooltip-n-mainpage-description": "سەر پەڕە بوینن",
        "tooltip-n-portal": "دۀربارۀ پروژه،أؤۀگإ مۀتؤینین انجؤم دین و چۀ وۀ کؤ بکینۀ دی/پئا کین",
        "tooltip-n-currentevents": "پئا کردن اطلاعات پس‌زمینه دۀربارۀ رویدادۀلایسۀ",
-       "tooltip-n-recentchanges": "لیستی إژ تۀغیرۀل ایسۀ إ ویکی",
+       "tooltip-n-recentchanges": "لیستی إژ گؤەڕاننەل(تغییرات) ایسگە إ ویکی",
        "tooltip-n-randompage": " وۀلگۀ بۀختۀکی  ئآوردِن",
        "tooltip-n-help": "جای أرا پئاکردن/أدی کردن",
-       "tooltip-t-whatlinkshere": "لیست کؤل وۀلگۀل ویکی گإ پیوۀند درن ائرۀ",
-       "tooltip-t-recentchangeslinked": "تغییرۀل ایسۀ وۀلگۀلئ گإ ئئ وۀلگۀ  پیوند درئۀ  اؤِنۀ",
+       "tooltip-t-whatlinkshere": "لیست کؤل(گِشت)وەڵگەل ویکی پیوەند هەنْگِتِنە(دریانە) اێرە",
+       "tooltip-t-recentchangeslinked": "گؤەڕاننەل(تغییرات)ایسگە أڕ وەڵگەل پیوەند هەنگتێ(دریا)وە اێ وەڵگە",
        "tooltip-feed-rss": "خبرنامه آراس‌اس برای این صفحه",
        "tooltip-feed-atom": "حاووال اتم أرا ئئ وۀلگۀ",
-       "tooltip-t-contributions": "Ù\84Û\8cست Ù\87ؤÙ\85کارÛ\8cÛ\80Ù\84 Ø¦Ø¦ Ú©Ø§Ø±Ø¨Ø±Û\80",
+       "tooltip-t-contributions": "Ù\84Û\8cست Ù\87اÙ\85 Ú©Ø§Ø±Û\8cÛ\95Ù\84\87Û\95Ù\85آرتÛ\8cÛ\95Ù\84)ئÛ\8e Ú©Ø§Ø±Ø¨Ø±Û\95",
        "tooltip-t-emailuser": "ارسال ایمیل به این کاربر",
        "tooltip-t-info": "اطلاعات بیشتر دربارهٔ این صفحه",
        "tooltip-t-upload": "بارنیائن فایلۀل",
-       "tooltip-t-specialpages": "لیست کؤل وۀلگۀل ویژۀ",
+       "tooltip-t-specialpages": "لیست کؤل(گشت)وەڵگەل(پەڕەل)ویژە",
        "tooltip-t-print": "نوسخهٔ قاوول چاپ ئئ وۀلگۀ",
-       "tooltip-t-permalink": "پیوند پایدار وۀ ئئ نؤسخه إژ وۀلگۀ",
+       "tooltip-t-permalink": "پیوەند پایدار وە اێ نؤسخه إژ وەڵگە",
        "tooltip-ca-nstab-main": "وۀلگۀ محتویات بوینن",
        "tooltip-ca-nstab-user": "وةڵگة  کاربۀر بؤین",
        "tooltip-ca-nstab-media": "دیدن صفحهٔ مدیا",
        "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-save": "گؤەڕیال(تغییرات) ووژت بِیل(ذخیره کە)",
        "tooltip-preview": "پیش‌نمایش تغییرات شما، لطفاً قبل از ذخیره‌کردن صفحه از این کلید استفاده     \nکنید",
        "tooltip-diff": ".نمایش تغییراتی که شما در متن داده‌اید",
        "tooltip-compareselectedversions": "دیدن تفاوت‌های دو نسخهٔ انتخاب‌شده از این صفحه",
        "tooltip-upload": "شروع بارگذاری",
        "tooltip-rollback": "«واگردانی» ویرایش(های) آخرین ویرایش‌کنندهٔ این صفحه را با یک کلیک بازمی‌گرداند.",
        "tooltip-undo": "«خنثی‌سازی» این ویرایش را خنثی می‌کند و جعبهٔ ویرایش را در حالت پیش‌نمایش باز می‌کند تا افزودن دلیل در خلاصهٔ ویرایش ممکن شود.",
-       "tooltip-preferences-save": "ذخÛ\8cرÙ\87 Ú©Ø±Ø¯Ù\86 ØªØ±Ø¬Û\8cحات",
+       "tooltip-preferences-save": "تÙ\85ارزÙ\88Ù\88Û\95Ù\84/ترجÛ\8cحات Ø¨Ù\90Û\8cÙ\84(ذخÛ\8cرÙ\87 Ú©Û\95)",
        "tooltip-summary": "خلاصه‌ای وارد کنید",
        "anonymous": "{{PLURAL:$1|کاربر|کاربران}} گمنام {{SITENAME}}",
        "siteuser": "$1، کاربر {{SITENAME}}",
        "pageinfo-not-current": "متأسفانه تهیه اطلاعات ویرایش‌های قدیمی غیرممکن است.",
        "pageinfo-header-basic": "اطلاعات اولیه",
        "pageinfo-header-edits": "ویرایش تاریخچه",
-       "pageinfo-header-restrictions": "حفاظت از صفحه",
-       "pageinfo-header-properties": "ويژگی‌های صفحه",
+       "pageinfo-header-restrictions": "پڵۆم کردن وەڵگە",
+       "pageinfo-header-properties": "ويژگیەل وەڵگە",
        "pageinfo-display-title": "نمایش عنوان",
        "pageinfo-default-sort": "کلید مرتب‌سازی پیش‌فرض",
        "pageinfo-length": "حجم صفحه  (بایت)",
        "pageinfo-redirects-name": "تعداد تغییرمسیرها به این صفحه",
        "pageinfo-subpages-name": "زیرصفحه‌های این صفحه",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|تغییرمسیر|تغییرمسیرها}}; $3 {{PLURAL:$3|غیرتغییرمسیر|غیرتغییرمسیر ها}})",
-       "pageinfo-firstuser": "به‌وجود آورندهٔ صفحه",
+       "pageinfo-firstuser": "وەڵگە ساز",
        "pageinfo-firsttime": "زمان ایجاد صفحه",
-       "pageinfo-lastuser": "آخرÛ\8cÙ\86 Ø¯Ø©Ø³Ú©Ø§Ø±Û\8c Ú©Ø©ر",
+       "pageinfo-lastuser": "دؤÙ\85اآخرÛ\8cÙ\86 Ø¯Û\95سکارÛ\8c Ú©Û\95ر",
        "pageinfo-lasttime": "تاریخ آخرین ویرایش",
        "pageinfo-edits": "شمار کلی ویرایش‌ها",
        "pageinfo-authors": "تعداد کلی نویسندگان یکتا",
        "pageinfo-hidden-categories": "{{PLURAL:$1| ردهٔ|ردهٔ}} پنهان ( $1 )",
        "pageinfo-templates": "{{PLURAL:$1|الگو|الگوها}} استفاده‌شده ($1)",
        "pageinfo-transclusions": "{{PLURAL:$1|صفحهٔ|صفحه‌های}} تراگنجانش‌شده در ($1)",
-       "pageinfo-toolboxlink": "اطÙ\84اعات Ù\88Û\80Ù\84Ú¯Û\80/Ù¾Û\80رÛ\80",
+       "pageinfo-toolboxlink": "زاÙ\86ستÛ\95Ù\86Û\8cÛ\95Ù\84 Ù\88Û\95ÚµÚ¯Û\95(Ù¾Û\95Ú\95Û\95)",
        "pageinfo-redirectsto": "تغییرمسیر به",
        "pageinfo-redirectsto-info": "زانستةنیةل",
        "pageinfo-contentpage": "شمرده شده به عنوان صفحهٔ محتوایی",
        "pageinfo-contentpage-yes": "أرێ-بةلئ",
-       "pageinfo-protect-cascading": "Ù\85حاÙ\81ظت Ø¢Ø¨Ø´Ø§Ø±Û\8c Ø§Ø² Ø§Û\8cÙ\86جا",
+       "pageinfo-protect-cascading": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø¢Ø¨Ø´Ø§Ø±Û\8c(تاÙ\81Ú¯Û\95)Ø¥Ú\98 Ø§Û\8eرÛ\95",
        "pageinfo-protect-cascading-yes": "أرێ-بةلئ",
-       "pageinfo-protect-cascading-from": "Ù\85حاÙ\81ظت Ø¢Ø¨Ø´Ø§Ø±Û\8c Ø§Ø²",
-       "pageinfo-category-info": "اطÙ\84اعات Ø±Ø¯Ù\87",
+       "pageinfo-protect-cascading-from": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø¢Ø¨Ø´Ø§Ø±Û\8c(تاÙ\81Ú¯Û\95)Ø¥Ú\98",
+       "pageinfo-category-info": "زاÙ\86ستÛ\95Ù\86Û\8cÛ\95Ù\84 Ú\95Ù\90زگ",
        "pageinfo-category-total": "تعداد کلی اعضاء",
-       "pageinfo-category-pages": "تعداد وۀلگۀل",
+       "pageinfo-category-pages": "گلەشؤماری وەڵگەل",
        "pageinfo-category-subcats": "تعداد زیررده‌ها",
        "pageinfo-category-files": "تعداد پرونده‌ها",
        "markaspatrolleddiff": "برچسب گشت بزن",
        "newimages-label": "نام پرونده (یا قسمتی از آن):",
        "newimages-showbots": "نمایش بارگذاری‌ها توسط ربات‌ها",
        "noimages": "چیزی برای دیدن نیست.",
-       "ilsubmit": "گئردین/مهِ نی",
+       "ilsubmit": "مِنِی کِردِن(گێردین)",
        "bydate": "از روی تاریخ",
        "sp-newimages-showfrom": "نشان‌دادن تصویرهای جدید از $2، $1 به بعد",
        "seconds": "{{PLURAL:$1|$1 ثانیه ها|$1 ثانیه یا}}",
        "exif-writer": "نویسنده",
        "exif-languagecode": "زوون",
        "exif-iimversion": "نسخه IIM",
-       "exif-iimcategory": "رده/ڕِزگ",
+       "exif-iimcategory": "ڕِزگ",
        "exif-iimsupplementalcategory": "رده‌های تکمیلی",
        "exif-datetimeexpires": "استفاده تا تاریخ",
        "exif-datetimereleased": "منتشر شده در",
        "exif-serialnumber": "شماره سریال دوربین",
        "exif-cameraownername": "صاحب دوربین",
        "exif-label": "برچسب",
-       "exif-datetimemetadata": "تاریخ آخرین تغییر فراداده",
+       "exif-datetimemetadata": "تاریخ آخرین گؤەڕانن/تغییر فراداده",
        "exif-nickname": "نام غیررسمی تصویر",
        "exif-rating": "امتیاز (از 5)",
        "exif-rightscertificate": "گواهینامه مدیریت حقوق",
        "exif-preferredattributionname": "در زمان استفاده مجدد، لطفاً اعتبار دهید به",
        "exif-pngfilecomment": "توضیحات پرونده PNG",
        "exif-disclaimer": "تکذیب‌نامه/درۆنامة",
-       "exif-contentwarning": "هشدار محتوا",
+       "exif-contentwarning": "هوشدار  نۆم جِک(محتوا)",
        "exif-giffilecomment": "توضیحات پرونده GIF",
        "exif-intellectualgenre": "نوع مورد",
        "exif-subjectnewscode": "کد موضوع",
        "exif-compression-4": "رمزگذاری نمابر سی‌سی‌آی‌تی‌تی گروه ۴",
        "exif-copyrighted-true": "دارای حق تکثیر",
        "exif-copyrighted-false": "وضعیت حق‌تکثیر تعیین نشده است",
+       "exif-photometricinterpretation-1": "سیاه و سفید (سیاه ۰ است)",
        "exif-unknowndate": "تاریخ نامعلوم/نادیار",
        "exif-orientation-1": "عادی",
        "exif-orientation-2": "افقی/لاووةلا پشت و رو بیة",
        "namespacesall": "کؤل",
        "monthsall": "کؤل",
        "confirmemail": "نیشانی ایمیل ووژتان تأئید کةن",
-       "confirmemail_noemail": "شما در صفحهٔ [[Special:Preferences|ترجیحات کاربری]] خود آدرس ایمیل معتبری وارد نکرده‌اید.",
+       "confirmemail_noemail": "شما در صفحهٔ [[Special:Preferences|تمارزووەل(ترجیحات)  کاربەری]] خود آدرس ایمیل معتبری وارد نکرده‌اید.",
        "confirmemail_text": "این ویکی، شما را ملزم به تأیید آدرس ایمیل خود، پیش از استفاده از خدمات ایمیل در اینجا می‌کند. دکمهٔ زیرین را فعال کنید تا ایمیلی تأییدی به آدرس ایمیل شما فرستاده شود. این ایمیل دربردارندهٔ پیوندی خواهد بود که حاوی یک کد است. پیوند را در مرورگر خود بار کنید کنید تا آدرس ایمیل شما تأیید شود.",
        "confirmemail_pending": "یک کد تأییدی پیشتر برای شما به صورت ایمیل فرستاده شده است. اگر همین اواخر حساب خود را باز کرده‌اید شاید بد نباشد که پیش از درخواست یک کد جدید چند دقیقه درنگ کنید تا شاید ایمیل قبلی برسد.",
        "confirmemail_send": "پُست‌کردن یک کد تأیید",
        "confirm-unwatch-top": " ئئ وۀلگۀ وۀ لیست پیگیریۀل تان حذف بو؟",
        "semicolon-separator": "؛&#32;",
        "quotation-marks": "«$1»",
-       "imgmultipageprev": "←وةڵگة پئش",
+       "imgmultipageprev": "&rarr; وەڵگە دؤماێن",
        "imgmultipagenext": "وةڵگة تِر/بعدی→",
        "imgmultigo": "بِچۆ!",
        "imgmultigoto": "بچۆ وةڵگة $1",
        "img-lang-go": "بِچۆ",
        "ascending_abbrev": "ورِ بِڵِنگی/صعودی",
        "descending_abbrev": "ورِ هووار/نزولی",
-       "table_pager_next": "وةڵگة بعدی",
-       "table_pager_prev": "وةڵگة  پئش",
+       "table_pager_next": "وەڵگە بعدی",
+       "table_pager_prev": "وەڵگە دؤماێن",
        "table_pager_first": "وةڵگة أؤةڵئن",
-       "table_pager_last": "وةڵگة دؤمائن/آخر",
+       "table_pager_last": "وەڵگە دؤمائن(آخر)",
        "table_pager_limit": "نمایش $1 مورد در هر وةڵگة",
        "table_pager_limit_label": "تعداد موارد در هر وةڵگة :",
        "table_pager_limit_submit": "بِچۆ",
        "autosumm-newblank": "ایجاد وةڵگة خالی",
        "lag-warn-normal": "ممکن است تغییرات تازه‌تر از $1 {{PLURAL:$1|ثانیه|ثانیه ها}} در این فهرست نشان داده نشوند.",
        "lag-warn-high": "ممکن است، به خاطر پس‌افتادگی زیاد سرور پایگاه داده، تغییرات تازه‌تر از $1 {{PLURAL:$1|ثانیه|ثانیه ها}} در این فهرست نشان داده نشده باشند.",
-       "watchlistedit-normal-title": "دةسکاری لیست پی‌گیریةل",
+       "watchlistedit-normal-title": "دەسکاری لیست پئ گیریەل",
        "watchlistedit-normal-legend": "حذف عنوان‌ها از فهرست پی‌گیری‌ها",
        "watchlistedit-normal-explain": "عنوان‌های موجود در فهرست پی‌گیری شما در زیر نشان داده شده‌اند.\nبرای حذف هر عنوان جعبهٔ کنار آن را علامت بزنید و دکمهٔ «{{int:Watchlistedit-normal-submit}}» را بفشارید.\nشما همچنین می‌توانید [[Special:EditWatchlist/raw|فهرست خام را ویرایش کنید]].",
        "watchlistedit-normal-submit": "حذف عنوان‌ها",
        "watchlisttools-view": "لیست پیگیریةل",
        "watchlisttools-edit": "مشاهده و ویرایش فهرست پی‌گیری‌ها",
        "watchlisttools-raw": "ویرایش فهرست خام پی‌گیری‌ها",
-       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Ú¯Û\80Ù¾]])",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|Ú¯Û\95Ù¾(Ù\82سÛ\95)]])",
        "duplicate-defaultsort": "هشدار: ترتیب پیش‌فرض «$2» ترتیب پیش‌فرض قبلی «$1» را باطل می‌کند.",
        "duplicate-displaytitle": "<strong>هشدار:</strong> نمایش عنوان \" $2 \"باعث ابطال پیش نمایش عنوان\" $1 \" می‌شود.",
        "invalid-indicator-name": "<strong>خطا:</strong>ویژگی های شاخص‌های وضعیت صفحهٔ <code>name</code> نباید خالی باشند.",
        "redirect-legend": "تغییرمسیر به یک پرونده یا صفحه",
        "redirect-summary": "این صفحهٔ ویژه به پرونده (نام پرونده داده‌شده)، صفحه (شماره شناسهٔ صفحه یا شماره نسخهٔ داده‌شده) یا صفحهٔ کاربری (شناسهٔ عددی کاربری داده‌شده) تغییرمسیر می‌یابد. طرز استفاده: [[{{#Special:Redirect}}/file/Example.jpg]]، \n[[{{#Special:Redirect}}/page/64308]]، [[{{#Special:Redirect}}/revision/328429]] یا [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "بِچۆ",
-       "redirect-lookup": "مِنِی کردن/گئردین:",
+       "redirect-lookup": "مِنِی کِردِن(گێردین):",
        "redirect-value": "ارزش:",
        "redirect-user": "شناسهٔ کاربر",
        "redirect-page": "شناسهٔ وةڵگة",
-       "redirect-revision": "نسخهٔ وةڵگة",
+       "redirect-revision": "نسخهٔ وەڵگە",
        "redirect-file": "نام پرونده",
        "redirect-not-exists": "مقدار پیدا نشد",
        "fileduplicatesearch": "جستجو برای پرونده‌های تکراری",
        "fileduplicatesearch-summary": "جستجو برای پرونده‌های تکراری بر اساس مقدار درهم‌شدهٔ آن‌ها صورت می‌گیرد.",
        "fileduplicatesearch-legend": "جستجوی موارد تکراری",
        "fileduplicatesearch-filename": "نام پرونده:",
-       "fileduplicatesearch-submit": "گئردین/مهِ نی",
+       "fileduplicatesearch-submit": "مِنِی کِردِن(گێردین)",
        "fileduplicatesearch-info": "<span dir=\"ltr\">$1 × $2</span> پیکسل<br />اندازهٔ پرونده: $3<br />نوع MIME: $4",
        "fileduplicatesearch-result-1": "پروندهٔ «$1» مورد تکراری ندارد.",
        "fileduplicatesearch-result-n": "پروندهٔ «$1» دارای {{PLURAL:$2|یک مورد تکراری|$2 مورد تکراری}} است.",
        "fileduplicatesearch-noresults": "پرونده‌ای با نام «$1» أ دی نؤی /پئا نؤی.",
-       "specialpages": "Ù\88Û\80Ù\84Ú¯Û\80Ù\84/Ù¾Û\80رÛ\80Ù\84 Ù\88Û\8cÚ\98Û\80",
+       "specialpages": "Ù\88Û\95ÚµÚ¯Û\95Ù\84(Ù¾Û\95Ú\95Û\95Ù\84\88Û\8cÚ\98Û\95",
        "specialpages-note-top": "شرح علائم",
        "specialpages-note": "* صفحه‌های ویژهٔ عادی.\n* <span class=\"mw-specialpagerestricted\">صفحه‌های ویژهٔ محدودشده.</span>",
        "specialpages-group-maintenance": "گزارش‌های نگهداری",
        "specialpages-group-redirects": "وةڵگةل ویژهٔ تغییرمسیر دهنده",
        "specialpages-group-spam": "ابزارهای هرزنگاری",
        "specialpages-group-developer": "ابزارهای توسعه‌دهندگان",
-       "blankpage": "وةڵگة خالی",
+       "blankpage": "وەڵگە پەتی(حالی)",
        "intentionallyblankpage": "این وةڵگة به طور عمدی خالی گذاشته شده است.",
        "external_image_whitelist": " #این سطر را همان‌گونه که هست رها کنید<pre>\n#عبارت‌های باقاعده (regex) را در زیر قرار دهید (فقط بخشی که بین // قرار می‌گیرد)\n#آن‌ها با نشانی اینترنتی تصاویر خارجی پیوند داده شده تطبیق داده می‌شوند\n#مواردی که مطابق باشند به صورت تصویر نمایش می‌یابند، و در غیر این صورت تنها یک پیوند به تصویر نمایش می‌یابد\n#سطرهایی که با # آغاز شوند به عنوان توضیحات در نظر گرفته می‌شوند\n#این سطرها به کوچکی و بزرگی حروف حساس هستند\n\n#عبارت‌های باقاعده (regex)  را زیر این سطر قرار دهید. این سطر را همان‌گونه که هست رها کنید</pre>",
-       "tags": "برچسب‌های تغییر مجاز",
+       "tags": "برچسب‌های گؤەڕانن/تغییر مجاز",
        "tag-filter": ":فیلتر کۀ[[Special:Tags|برچسب‌ۀل]]",
        "tag-filter-submit": "پالانۀل/فیلترۀل",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|بۀرچۀسب|بۀرچۀسبۀل}}]]:$2)",
        "tags-source-extension": "تعریف‌شده بر پایه افزونه",
        "tags-source-manual": "اعمال شده به صورت دستی توسط ربات‌ها یا کاربرها",
        "tags-source-none": "دیگر استفاده نمی‌شود",
-       "tags-edit": "دةسکاری",
+       "tags-edit": "دەسکاری",
        "tags-delete": "حۀذف کردن/پاک کردن",
        "tags-activate": "فعال کردن",
        "tags-deactivate": "غیرفعال کردن/إکار کةتن",
        "tags-hitcount": "$1 {{PLURAL:$1|تغییرها|تغییر}}",
        "tags-manage-no-permission": "شما اجازه مدیریت تغییر تگ‌ها را ندارید.",
+       "tags-manage-blocked": "امکان تغییر برچسب‌ها را در زمان بسته‌بودن ندارید",
        "tags-create-heading": "ایجاد یک برچسب جدید",
        "tags-create-explanation": "به طور پیش‌فرض، تگ‌های تازه ایجاد شده برای استفاده کاربران و ربات‌ها در دسترس قرار می‌گیرند.",
        "tags-create-tag-name": "نام برچسب:",
        "tags-deactivate-not-allowed": "غیرفعال‌سازی تگ «$1» ممکن نیست.",
        "tags-deactivate-submit": "غیرفعال کردن/إکار کةتن",
        "tags-apply-no-permission": "دسترسی برای تغییر برچسب تغییراتتان را ندارید.",
+       "tags-apply-blocked": "در زمان بسته‌بودن امکان اعمال تغییراتتان بر روی برچسب‌ها را ندارید.",
        "tags-apply-not-allowed-one": "اجازهٔ تائید برچسب «$1» به صورت دستی وجود ندارد.",
        "tags-apply-not-allowed-multi": "اجازهٔ تائید {{PLURAL:$2|برچسب|برچسب ها}} به صورت دستی وجود ندارد:$1",
        "tags-update-no-permission": "شما اجازهٔ افزودن یا حذف برچسب از خود نسخه یا سیاهه را ندارید.",
+       "tags-update-blocked": "در زمان بسته بودن امکان حذف برچسب‌ها را ندارید.",
        "tags-update-add-not-allowed-one": "اجازهٔ افزودن برچسب «$1» به صورت دستی وجود ندارد.",
        "tags-update-add-not-allowed-multi": "اجازهٔ افزودن {{PLURAL:$2|برچسب|برچسب ها}} به صورت دستی وجود ندارد:$1",
        "tags-update-remove-not-allowed-one": "اجازهٔ حذف برچسب «$1» به صورت دستی وجود ندارد.",
        "tags-edit-nooldid-text": "نسخهٔ مقصد برای اعمال تابع مورد نظر را مشخص نکرده‌اید، یا نسخهٔ مورد نظر وجود ندارد.",
        "tags-edit-none-selected": "لطفاً حداقل یک برچسب برای افزودن یا حذف انتخاب کنید.",
        "comparepages": "مقایسة وةڵگةل",
-       "compare-page1": "وةڵگةل ۱",
+       "compare-page1": "وەڵگەل ۱",
        "compare-page2": "وةڵگةل ۲",
        "compare-rev1": "نسخهٔ ۱",
        "compare-rev2": "نسخهٔ ۲",
        "logentry-suppress-revision": "$1 پیدایی {{PLURAL:$5|یک نسخه|$5 نسخه}} صفحه $3 را مخفیانه {{GENDER:$2|تغییر داد}}: $4",
        "logentry-suppress-event-legacy": "$1 پیدایی موارد سیاهه را در $3 مخفیانه {{GENDER:$2|تغییر داد}}",
        "logentry-suppress-revision-legacy": "$1 پیدایی نسخه‌های $3 را مخفیانه {{GENDER:$2|تغییر داد}}",
-       "revdelete-content-hid": "Ù\85حتÙ\88ا Ø´Ø§Ø±Ø¯Ø¦Ø§/Ù¾Ù\86Ù\87اÙ\86 Ú©Ø±Ø¯",
+       "revdelete-content-hid": "Ù\86Û\86Ù\85 Ø¬Ù\90Ú©(Ù\85حتÙ\88ا)آشارÛ\8cائÛ\95",
        "revdelete-summary-hid": "خلاصه ویرایش را پنهان کرد",
-       "revdelete-uname-hid": "نام کاربری را پنهان کرد",
-       "revdelete-content-unhid": "Ù\85حتÙ\88ا Ø±Ø§ Ø¢Ø´Ú©Ø§Ø± Ú©Ø±Ø¯",
+       "revdelete-uname-hid": "نۆم کاربەری شاردێآ",
+       "revdelete-content-unhid": "Ù\86Û\86Ù\85 Ø¬Ù\90Ú©(Ù\85حتÙ\88ا)Ù\86Û\95شارÛ\8cاسآ(Ù¾Ù\86Ù\87اÙ\86 Ù\86ؤÛ\8cÛ\95)",
        "revdelete-summary-unhid": "خلاصه ویرایش را آشکار کرد",
        "revdelete-uname-unhid": "نام کاربری را آشکار کرد",
        "revdelete-restricted": "مدیران را محدود کرد",
        "logentry-import-upload": "$1 $3 را توسط بارگذار پرونده {{GENDER:$2|درون‌ریزی کرد}}",
        "logentry-import-upload-details": "$1 {{GENDER:$2|imported}} $3 by file upload ($4 {{PLURAL:$4|revision|revisions}})",
        "logentry-import-interwiki": "$1 $3 را از ویکی دیگر {{GENDER:$2|درون‌ریز کرد}}",
-       "logentry-import-interwiki-details": "$1 {{GENDER:$2|imported}} $3 from $5 ($4 {{PLURAL:$4|revision|revisions}})",
+       "logentry-import-interwiki-details": "$1 $3 را از $5 {{GENDER:$2|درون‌ریزی کرد}} ($4 {{PLURAL:$4|نسخه}})",
        "logentry-merge-merge": "$1  $3  را به  $4 {{GENDER:$2| ادغام کرد}} (نسخه تا  $5)",
        "logentry-move-move": "$1 صفحهٔ $3 را به $4 {{GENDER:$2|منتقل کرد}}",
        "logentry-move-move-noredirect": "$1 وةڵگة $3 را بدون برجای‌گذاشتن تغییرمسیر به $4 {{GENDER:$2|منتقل کرد}}",
        "feedback-thanks": "سپاس! بازخورد شما در وةڵگة «[$1 $2]» ثبت شد.",
        "feedback-thanks-title": "سپاس إژ هؤمة!",
        "feedback-useragent": "رابط کاربر:",
-       "searchsuggest-search": "گئردین/مهِ نی",
+       "searchsuggest-search": "مِنِی کِردِن(گێردین)",
        "searchsuggest-containing": "وةڵگةل  دربردارنده...",
        "api-error-badaccess-groups": "شما اجازهٔ بارگذاری پرونده‌ها را در این ویکی ندارید.",
        "api-error-badtoken": "خطای داخلی: کد امنیتی اشتباه (Bad token).",
        "expand_templates_preview_fail_html": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروع است، لطفا دوباره سعی کنید. اگر هنوز کار نمی کند، سعی کنید [[Special:UserLogout|خروج از سیستم]] را کلیک نموده و دوباره وارد شوید.",
        "expand_templates_preview_fail_html_anon": "<em>زیرا {{SITENAME}} تا به HTML خام فعال و یک دست رفتن اطلاعات نشست وجود دارد، پیش نمایش به عنوان یک اقدام احتیاطی در برابر حملات جاوا اسکریپت پنهان است.</em>\n\n<strong>اگر این تلاش پیشنمایش مشروح است ، لطفا دوباره سعی کنید اگر هنوز کار میکند،سعی کنید  Following link is missing: [[Special:UserLogin|ورود|خروج]]را کلیک کنید و دوباره وارد شوید",
        "pagelanguage": "وةڵگة انتخاب زوون",
-       "pagelang-name": "وةڵگة/پەڕە",
+       "pagelang-name": "وةڵگە(پەڕە)",
        "pagelang-language": "زوون",
        "pagelang-use-default": "استفاده إژ زوون پئش فرض",
        "pagelang-select-lang": "زوون انتخاب کۀ",
-       "right-pagelang": "تغییر وةڵگة زوون",
-       "action-pagelang": "تغییر زوون وةڵگة",
+       "pagelang-submit": "تائید کردن",
+       "right-pagelang": "گؤەڕانن/تغییر وةڵگة زوون",
+       "action-pagelang": "گؤەڕانن/تغییر زوون وةڵگة",
        "log-name-pagelang": "تغییر سیاههٔ زبان",
        "log-description-pagelang": "ای پهرستنومه در بلگه زونا آلشت گرته.",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2| تغییریافت}} زبان صفحه برای  $3  از  $4  به  $5 .",
-       "default-skin-not-found": "Whoops! The default skin for your wiki, defined in <code dir=\"ltr\">$wgDefaultSkin</code> as <code>$1</code>, is not available.\n\nYour installation seems to include the following {{PLURAL:$4|skin|skins}}. See [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: Skin configuration] for information how to enable {{PLURAL:$4|it|them and choose the default}}.\n\n$2\n\n; If you have just installed MediaWiki:\n: You probably installed from git, or directly from the source code using some other method. This is expected. Try installing some skins from [https://www.mediawiki.org/wiki/Category:All_skins mediawiki.org's skin directory], by:\n:* Downloading the [https://www.mediawiki.org/wiki/Download tarball installer], which comes with several skins and extensions. You can copy and paste the <code>skins/</code> directory from it.\n:* Downloading individual skin tarballs from [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org].\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins Using Git to download skins].\n: Doing this should not interfere with your git repository if you're a MediaWiki developer.\n\n; If you have just upgraded MediaWiki:\n: MediaWiki 1.24 and newer no longer automatically enables installed skins (see [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual: Skin autodiscovery]). You can paste the following {{PLURAL:$5|line|lines}} into <code>LocalSettings.php</code> to enable {{PLURAL:$5|the|all}} installed {{PLURAL:$5|skin|skins}}:\n\n<pre dir=\"ltr\">$3</pre>\n\n; If you have just modified <code>LocalSettings.php</code>:\n: Double-check the skin names for typos.",
+       "default-skin-not-found": "اوه! پوسته پیش‌فرض برای ویکی شما تعریف‌شده در <code dir=\"ltr\"<$wgDefaultSkin</code> به عنوان <code>$1</code>، در دسترس نیست.\n\nبه نظر می‌آید نصب شما شامل پوسته‌های زیر می‌شود. [https://www.mediawiki.org/wiki/Manual:Skin_configuration راهنما: تنظیمات پوسته] را برای کسب اطلاعات در باره چگونگی فعال‌ساختن آن‌ها و انتخاب پیش‌فرض ببینید.\n\n$2\n\n; اگر اخیراً مدیاویکی را نصب کرده‌اید:\n: احتمالاً از گیت، یا به طور مستقیم از کد مبدأ که از چند متد دیگر استفاده می‌کند نصب کردید. انتظار می‌رود. چند {{PLURAL:$4|پوسته|پوسته}} از [https://www.mediawiki.org/wiki/Category:All_skins فهرست پوسته mediawiki.org] نصب کنید، که همراه چندین پوسته و افزونه هستند. شما می‌توانید شاخه <code>skins/</code> را از آن نسخه‌برداری کرده و بچسبانید.\n\n:* [https://www.mediawiki.org/wiki/Download_from_Git#Using_Git_to_download_MediaWiki_skins استفاده از گیت برای دریافت پوسته‌ها].\n: انجام این کار با مخزن گیت‌تان تداخل نمی‌کند اگر توسعه‌دهنده مدیاویکی هستید.\n\n; اگر اخیراً مدیاویکی را ارتقاء دادید:\n: مدیاویکی ۱٫۲۴ و تازه‌تر دیگر به طور خودکار پوسته‌های نصب‌شده را فعال نمی‌کند ([https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery راهنما: کشف خودکار پوسته] را ببینید). شما می‌توانید خطوط زیر را به داخل <code>LocalSettings.php</code> بچسبانید تا {{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:ممکن است از گیت یا از کد منبع با روش‌های دیگر نصب کرده‌اید. انتظار می‌رود MediaWiki 1.24 یا جدیدتر در پوشهٔ اصلی هیچ پوسته‌ای نداشته باشند.\nسعی کنید تعدادی پوسته از [https://www.mediawiki.org/wiki/Category:All_skins پوشهٔ پوسته‌های مدیاویکی]، با:\n:*دریافت [https://www.mediawiki.org/wiki/Download نصب‌کننده تاربال]، که با چندین پوسته و افزونه هست. شما می توانید پوستهٔ <code>skins/</code> را از آن کپی و پیست کنید.\n:*کلون کردن یکی از <code dir=\"ltr\">mediawiki/skins/*</code> از مخزن در پوشهٔ <code>skins/</code> مدیاویکی‌تان.\n:اگر توسعه‌دهندهٔ مدیاویکی هستید، انجام این کار نباید تعارضی با مخزن گیت شما داشته باشد. برای اطلاعات بیشتر و فعال کردن پوسته‌ها و انتخاب آنها به عنوان پیش‌فرض [https://www.mediawiki.org/wiki/Manual:Skin_configuration Manual: تنظیمات پوسته] را مشاهده کنید.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (فعال)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''غیر فعال''')",
        "mediastatistics-header-text": "متنی",
        "mediastatistics-header-executable": "اجرایی",
        "mediastatistics-header-archive": "قالب‌های فشرده",
+       "mediastatistics-header-total": "کؤل(گشت)پەروەندەل",
        "json-warn-trailing-comma": "$1 کامای در انتها از جی‌سن {{PLURAL:$1|حذف شد}}.",
        "json-error-unknown": "مشکلی با جی‌سن بود. خطا: $1",
        "json-error-depth": "بیشینهٔ عمق پشته رد شده است",
        "special-characters-title-emdash": "خط فاصله کشیده",
        "special-characters-title-minus": "علامت منفی",
        "mw-widgets-dateinput-no-date": "هیچ داده‌ای انتخاب نشده",
-       "mw-widgets-titleinput-description-new-page": "ئئ Ù\88ةڵگة Ù\87Ù\86Ù\88ز/حاÙ\84Û\8c وجود نِئرێ",
-       "mw-widgets-titleinput-description-redirect": "تغییر مسیر به $1",
+       "mw-widgets-titleinput-description-new-page": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ù\87Û\95Ù\86Û\8c\87اÙ\84Û\8c)وجود نِئرێ",
+       "mw-widgets-titleinput-description-redirect": "گؤەڕانن/تغییر مسیر به $1",
        "api-error-blacklisted": "لطفاً یک عنوان توصیفی متفاوت انتخاب کنید."
 }
index 38ad715..d79cfe2 100644 (file)
@@ -6,7 +6,8 @@
                        "아라",
                        "Macofe",
                        "Mjbmr",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Lakzon"
                ]
        },
        "tog-underline": "هوم پئیڤأند زیرخأط دار:",
        "history-feed-description": "دوواره دیئن ویرگار سی بلگه د ویکی",
        "history-feed-item-nocomment": "$1 د\n$2",
        "history-feed-empty": "بلگه حاسته بیه وجود ناره.\nشایت وه د ویکی پاکسا بیه، یا نومش آلشت بیه.\nسی بلگیا مرتوط تازه [[ویجه:پی جوری|پی جوری د ویکی]] کوششت بکید.",
-       "history-edit-tags": "ویرایشت سردیسیا وانئریا انتخاو بیه",
+       "history-edit-tags": "ڤیرایئشت کاری ڤانئیأریا گولئ ڤورچی بییە",
        "rev-deleted-comment": "(ویرایشت چکسته جا وه جا بیه)",
        "rev-deleted-user": "(نوم کاروری جا وه جا بیه)",
        "rev-deleted-event": "(انجوم گر پهرستنومه جا وه جا بیه)",
        "prefs-help-variant": "قسه وری انتخاوی شما سی نمائشت مینونه بلگه یا د ای ویکی.",
        "yournick": "امضا تازه:",
        "prefs-help-signature": "ویر و باوریا نیسسه بیه د بلگه چک چنه باید وا«<nowiki>~~~~</nowiki>» امضا بان؛ ای نشون وه شکل خودانجومی وه امضا شما و مؤر ویرگار تبدیل بوئه.",
-       "badsig": "اÙ\85ضا Ø®Ø¤Ù\85 Ø¨Û\8c Ø§Ø¹ØªÙ\88ار.\nسردÛ\8cسÛ\8cا Ø§Ú\86 ØªÛ\8c Ø§Ù\85 Ø§Ù\84 Ù\86Ù\87 Ù\88ارسÛ\8c Ø¨کیت.",
+       "badsig": "ئÙ\85ضا Ø®Ù\88Ù\85 Ø¨Û\8c Ø¦ØªØ¦Ú¤Ø§Ø±.\nسأردÛ\8cسÛ\8cا Ø¦Ú\86 ØªÛ\8c Ø¦Ù\85 Ø¦Ù\84 Ù\86Û\95 Ú¤Ø§Ø±Ø¦Ø³Û\8c Ø¨Ø£کیت.",
        "badsiglength": "امضا شما فره گپه.\nدرازا امضا باید کمتر  د $1 {{PLURAL:$1|نیسه}} بوئه.",
        "yourgender": "شما بیشتر میهایت که چه جوری گوته بوئه؟",
        "gender-unknown": "د گاتی کئ شوما میائیت ڤا ڤیرئموٙ، نأرم أفزار دوبیشتأر  کألمیە یا جئنس خومثا نە ڤئ کار مئیرە",
        "right-blockemail": "کاریار نه د کل کردن انجومانامه منع کو",
        "right-hideuser": "قلف کردن یه گل نوم کاریاری،قام کردن وه د ور تیه کل خلک",
        "right-ipblock-exempt": "کارگرایی نگرتن د قطع دسرسیا آی پی، خودانجوم یا فاصله دار",
-       "right-proxyunbannable": "کارگرایی نگرتن د قطع دسرسی خودکار پروکسیا",
        "right-unblockself": "خوشه قلف نکید",
        "right-protect": "آلشت دئن انازه پر و پیم کردن بلگه یا و ویرایشت بلگه یا پر و پیم بیه تافی",
        "right-editprotected": "ویرایشت بلگه یا پر و پیم بیه چی «{{int:protect-level-sysop}}»",
        "exif-devicesettingdescription": "شرح میزوکاری اوزار",
        "exif-subjectdistancerange": "محدوده دیر د دس بیین سوجه",
        "exif-imageuniqueid": "نوم دیار کن یکونه عسگ",
-       "exif-gpsversionid": "نسقه سردیس جی پی اس",
+       "exif-gpsversionid": "نوسقە سأردیس جی پی ئس",
        "exif-gpslatituderef": "پئنا ولاتشناسی شمالی و هارگه",
        "exif-gpslatitude": "پئنا ولاتشناسی",
        "exif-gpslongituderef": "درازا ولاتشناسی افتوزنون و افتونشین",
        "tags-edit-logentry-legend": "اضاف کردن یا جا وه جاکاری سردیسیا د {{PLURAL:$1|د ای پهرستنومه|همه پهرستنومه یا $1}}",
        "tags-edit-existing-tags": "سردیسیایی که هیئشو:",
        "tags-edit-existing-tags-none": "\"هیشکوم\"",
-       "tags-edit-new-tags": "سردÛ\8cسÛ\8cا ØªØ§Ø²Ù\87:",
+       "tags-edit-new-tags": "سأردÛ\8cسÛ\8cا ØªØ§Ø²Û\95:",
        "tags-edit-add": "ای سردیسیا نه اضاف بکیت",
        "tags-edit-remove": "ای سردیسیا نه ورداریت",
        "tags-edit-remove-all-tags": "(همه سردیسیا نه ورداریت)",
        "special-characters-group-khmer": "خمر",
        "special-characters-title-endash": "خط فاصله",
        "special-characters-title-emdash": "خط فاصله",
-       "special-characters-title-minus": "نشون کم کردن"
+       "special-characters-title-minus": "نشون کم کردن",
+       "mw-widgets-titleinput-description-new-page": "بلگه نیئش"
 }
index 43d8da3..66494bf 100644 (file)
@@ -69,6 +69,7 @@
        "tog-watchlisthidebots": "Slėpti robotų keitimus stebimų sąraše",
        "tog-watchlisthideminor": "Slėpti smulkius keitimus stebimų sąraše",
        "tog-watchlisthideliu": "Slėpti prisijungusių naudotojų keitimus stebimųjų sąraše",
+       "tog-watchlistreloadautomatically": "Atnaujinti stebėjimų sąrašą automatiškai, kai tik filtras yra pakeičiamas (reikalingas JavaScript)",
        "tog-watchlisthideanons": "Slėpti anoniminių naudotojų keitimus stebimųjų sąraše",
        "tog-watchlisthidepatrolled": "Slėpti patikrintus keitimus stebimųjų sąraše",
        "tog-watchlisthidecategorization": "Slėpti puslapių kategorizavimą",
        "october-date": "Spalio $1",
        "november-date": "Lapkričio $1",
        "december-date": "Gruodžio $1",
+       "period-am": "iš ryto",
+       "period-pm": "po pietų",
        "pagecategories": "{{PLURAL:$1|Kategorija|Kategorijos}}",
        "category_header": "Puslapiai kategorijoje „$1“",
        "subcategories": "Subkategorijos",
        "databaseerror-query": "Užklausa: $1",
        "databaseerror-function": "Paskirtis: $1",
        "databaseerror-error": "Klaida: $1",
+       "transaction-duration-limit-exceeded": "Kad išvengtume didelio kopijavimo trukdžio, ši transakcija buvo atšaukta, nes įrašimo trukmė ($1) viršijo $2 {{PLURAL:$2|sekundės|sekundžių}} limitą.\nJeigu keičiate daug elementų vienu metu, vietoje to bandykite atlikti daug smulkesnių operacijų.",
        "laggedslavemode": "Dėmesio: Puslapyje gali nesimatyti naujausių pakeitimų.",
        "readonly": "Duomenų bazė užrakinta",
        "enterlockreason": "Įveskite užrakinimo priežastį, taip pat datą, kada bus atrakinta",
        "mypreferencesprotected": "Jūs neturite teisių redaguoti jūsų parinktys.",
        "ns-specialprotected": "Specialieji puslapiai negali būti redaguojami.",
        "titleprotected": "[[User:$1|$1]] apsaugojo šį pavadinimą nuo sukūrimo.\nNurodyta priežastis yra ''$2''.",
-       "filereadonlyerror": "Neįmanoma pakeisti failo \"$1\" nes failų saugykla \"$2\" yra nustatyta tik skaitymo režimu.\n\nJą užrakinęs administratorius pateikė šį paaiškinimą: \"$3\".",
+       "filereadonlyerror": "Neįmanoma pakeisti failo \"$1\" nes failų saugykla \"$2\" yra nustatyta tik skaitymo režimu.\n\nJą užrakinęs sistemos administratorius pateikė šį paaiškinimą: \"$3\".",
        "invalidtitle-knownnamespace": "Klaidingas pavadinimas vardų erdvėje \"$2\" ir tekstu \"$3\"",
        "invalidtitle-unknownnamespace": "Klaidingas pavadinimas nežinomoje vardų erdvėje numeriu $1 ir tekstu \"$2\"",
        "exception-nologin": "Neprisijungęs",
        "virus-scanfailed": "skanavimas nepavyko (kodas $1)",
        "virus-unknownscanner": "nežinomas antivirusas:",
        "logouttext": "<strong>Dabar jūs esate atsijungęs.</strong>\n\nPastaba: kai kuriuose puslapiuose ir toliau gali rodyti, kad esate prisijungęs iki tol, kol išvalysite savo naršyklės podėlį.",
+       "cannotlogoutnow-title": "Negali atsijungti dabar",
+       "cannotlogoutnow-text": "Atsijungimas negalimas, kai naudojama $1.",
        "welcomeuser": "Sveiki,  $1 !",
        "welcomecreation-msg": "Jūsų paskyra buvo sukurta.\nNepamirškite pakeisti savo [[Special:Preferences|{{SITENAME}} nustatymų]].",
        "yourname": "Naudotojo vardas:",
        "remembermypassword": "Prisiminti prisijungimo duomenis šiame kompiuteryje (daugiausiai $1 {{PLURAL:$1|dieną|dienas|dienų}})",
        "userlogin-remembermypassword": "Įsiminti mane",
        "userlogin-signwithsecure": "Naudoti saugią jungtį",
+       "cannotloginnow-title": "Negalima prisijungti dabar",
+       "cannotloginnow-text": "Prisijungimas negalimas, kai naudojama $1.",
        "yourdomainname": "Jūsų domenas:",
        "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.",
        "wrongpasswordempty": "Įvestas slaptažodis yra tuščias. Pamėginkite vėl.",
        "passwordtooshort": "Slaptažodžiai turi būti bent $1 {{PLURAL:$1|simbolio|simbolių|simbolių}} ilgio.",
        "passwordtoolong": "Slaptažodžiai negali būti ilgesni nei {{PLURAL:$1|1 simbolis|$1 simboliai}}.",
+       "passwordtoopopular": "Dažnai pasirenkami slaptažodžiai negali būti naudojami. Prašome pasirinkti kitą unikalų slaptažodį.",
        "password-name-match": "Jūsų slaptažodis turi skirtis nuo jūsų naudotojo vardo.",
        "password-login-forbidden": "Šito naudotojo vardo ir slaptažodžio naudojimas yra uždraustas.",
        "mailmypassword": "Atkurti slaptažodį",
        "resetpass_submit": "Nustatyti slaptažodį ir prisijungti",
        "changepassword-success": "Jūsų slaptažodis pakeistas sėkmingai!",
        "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-disabled": "Boto slaptažodžiai yra neaktyvuoti.",
+       "botpasswords-no-central-id": "Kad galėtumėte naudoti boto slaptažodžius, turite būti prisijungęs su centralizuota paskyra.",
+       "botpasswords-existing": "Egzistuojantys boto slaptažodžiai",
+       "botpasswords-createnew": "Sukurti naują boto slaptažodį",
+       "botpasswords-editexisting": "Redaguoti egzistuojantį boto slaptažodį",
+       "botpasswords-label-appid": "Boto vardas:",
+       "botpasswords-label-create": "Kurti",
+       "botpasswords-label-update": "Atnaujinti",
+       "botpasswords-label-cancel": "Atšaukti",
+       "botpasswords-label-delete": "Ištrinti",
+       "botpasswords-label-resetpassword": "Atstatyti slaptažodį",
+       "botpasswords-label-grants": "Taikomi leidimai:",
+       "botpasswords-help-grants": "Kiekvienas leidimas suteikia prieigą prie išvardintų naudotojo leidimų, kuriuos paskyra jau turi.\nŽiūrėkite [[Special:ListGrants|leidimų lentelę]], norėdami rasti daugiau informacijos.",
+       "botpasswords-label-restrictions": "Naudojimo apribojimai:",
+       "botpasswords-label-grants-column": "Leidžiama",
+       "botpasswords-bad-appid": "Boto vardas \"$1\" nėra tinkamas.",
+       "botpasswords-insert-failed": "Nepavyko pridėti boto vardo \"$1\". Gal jis jau pridėtas?",
+       "botpasswords-update-failed": "Nepavyko atnaujinti boto vardo \"$1\". Gal jis buvo ištrintas?",
+       "botpasswords-created-title": "Boto slaptažodis sukurtas",
+       "botpasswords-created-body": "Boto slaptažodis \"$1\" buvo sukurtas sėkmingai.",
+       "botpasswords-updated-title": "Boto slaptažodis atnaujintas",
+       "botpasswords-updated-body": "Boto slaptažodis \"$1\" buvo atnaujintas sėkmingai.",
+       "botpasswords-deleted-title": "Boto slaptažodis ištrintas",
+       "botpasswords-deleted-body": "Boto slaptažodis \"$1\" buvo ištrintas.",
+       "botpasswords-newpassword": "Naujas slaptažodis prisijungimui su <strong>$1</strong> yra <strong>$2</strong>. <em>Prašome įsiminti jį naudojimui ateityje.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider nėra prieinamas.",
+       "botpasswords-restriction-failed": "Boto slaptažodžio apribojimai draudžia šį prisijungimą.",
+       "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-no-info": "Jūs turite būti prisijungęs, kad pasiektumėte puslapį tiesiogiai.",
        "resetpass-submit-loggedin": "Keisti slaptažodį",
        "passwordreset-emailtext-ip": "Kažkas (tikriausiai jūs, IP adresu $1) paprašė priminti jūsų slaptažodį svetainėje {{SITENAME}} ($4). Šio naudotojo {PLURAL:$3|paskyra|paskyros}} yra susietos su šiuo elektroninio pašto adresu:\n\n$2\n\n{{PLURAL:$3|Šis laikinas slaptažodis |Šie laikini slaptažodžiai}} baigs galiot po {{PLURAL:$5|vienos dienos|$5 dienų}}. \n\nJūs turėtumėte prisijungti ir pasirinkti naują slaptažodį. Jei kažkas kitas padarė šį prašymą arba jūs prisiminėte savo pirminį slaptažodį, ir jums nebereikia jo pakeisti, galite ignoruoti šį pranešimą ir toliau naudotis savo senuoju slaptažodžiu.",
        "passwordreset-emailtext-user": "Naudotojas $1 svetainėje {{SITENAME}} sukūrė užklausą slaptažodžio priminimui svetainėje {{SITENAME}}\n($4). Šio naudotojo {{PLURAL:$3|paskyra|paskyros}} susieto su šiuo elektroniniu paštu $2. \n\n{{PLURAL:$3|Šis laikinas slaptažodis|Šie laikini slaptažodžiai}} baigs galioti po {{PLURAL:$5|vienos dienos|$5 dienų}}. Jūs turėtumėte prisijungti ir pasirinkti naują slaptažodį. Jei kažkas padarė tai be jūsų žinios arba jūs prisiminėte savo pirminį slaptažodį, ir jūs nebenorite jo pakeisti, galite ignoruoti šį pranešimą ir toliau naudotis savo senuoju slaptažodžiu.",
        "passwordreset-emailelement": "Naudotojo vardas: \n$1\n\nLaikinas slaptažodis: \n$2",
-       "passwordreset-emailsentemail": "Jeigu tai yra jūsų paskyros registruotas el. pašto adresas, tada slaptažodžio atkūrimo laiškas bus išsiųstas.",
+       "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",
        "changeemail": "Pakeisti ar pašalinti el. pašto adresą",
        "copyrightwarning2": "Primename, kad viskas, kas patenka į {{SITENAME}} gali būti redaguojama, perdaroma, ar pašalinama kitų naudotojų. Jei nenorite, kad jūsų indėlis būtų be gailesčio redaguojamas, čia nerašykite.<br />\nTaip pat jūs pasižadate, kad tai jūsų pačių rašytas tekstas arba kopijuotas\niš viešų ar panašių nemokamų šaltinių (detaliau - $1).\n'''NEKOPIJUOKITE AUTORINĖMIS TEISĖMIS APSAUGOTŲ DARBŲ BE LEIDIMO!'''",
        "editpage-cannot-use-custom-model": "Šio puslapio turinio modelis negali būti pakeistas.",
        "longpageerror": "'''KLAIDA: Tekstas, kurį pateikėte, yra $1 {{PLURAL:$1|kilobaito|kilobaitų|kilobaitų}} ilgio, tai yra didesnis nei yra leistina. Yra leidžiami tiktai $2 {{PLURAL:$2|kilobaitas|kilobaitai|kilobaitų}}.''' Jis nebus išsaugotas.",
-       "readonlywarning": "'''Įspėjimas: Duomenų bazė buvo užrakinta techninei profilaktikai, todėl šiuo metu negalėsite išsaugoti savo pakeitimų. Siūlome nusikopijuoti tekstą į tekstinį failą ir vėliau jį čia išsaugoti.'''\n\nJą užrakinusio administratoriaus paaiškinimas: $1",
+       "readonlywarning": "<strong>Įspėjimas: Duomenų bazė buvo užrakinta techninei profilaktikai, todėl šiuo metu negalėsite išsaugoti savo pakeitimų. Siūlome nusikopijuoti tekstą į tekstinį failą ir vėliau jį čia išsaugoti.</strong>\n\nJą užrakinusio sistemos administratoriaus paaiškinimas: $1",
        "protectedpagewarning": "'''Dėmesio: Šis puslapis yra užrakintas taip, kad jį redaguoti gali tik administratoriaus teises turintys naudotojai.'''\nNaujausias įrašas žurnale yra pateiktas žemiau:",
        "semiprotectedpagewarning": "'''Pastaba:''' Šis puslapis buvo užrakintas, jį gali redaguoti tik registruoti naudotojai.\nNaujausias įrašas žurnale yra pateiktas žemiau:",
        "cascadeprotectedwarning": "<strong>Dėmesio:</strong> Šis puslapis buvo užrakintas taip, kad tik naudotojai su administratoriaus teisėmis galėtų jį redaguoti, nes jis yra įtrauktas į {{PLURAL:$1|šį puslapį, apsaugotą|šiuos puslapius, apsaugotus}} „pakopinės apsaugos“ pasirinktimi:",
        "permissionserrors": "Teisių klaida",
        "permissionserrorstext": "Jūs neturite teisių tai daryti dėl {{PLURAL:$1|šios priežasties|šių priežasčių}}:",
        "permissionserrorstext-withaction": "Jūs neturite leidimo $2 dėl {{PLURAL:$1|šios priežasties|šių priežasčių}}:",
-       "contentmodelediterror": "Jūs negalite redaguoti šios versijos, nes jos turinio modelis yra <code>$1</code> ir dabartinis puslapio turinio modelis yra <code>$2</code>.",
+       "contentmodelediterror": "Jūs negalite redaguoti šios versijos, nes jos turinio modelis yra <code>$1</code>, kuris skiriasi nuo dabartinio puslapio turinio modelio, kuris yra <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Dėmesio: Jūs atkuriate puslapį, kuris anksčiau buvo ištrintas.'''\n\nTurėtumėte nuspręsti, ar reikėtų toliau redaguoti šį puslapį.\nJūsų patogumui čia pateikiamas šio puslapio šalinimų ir perkėlimų sąrašas:",
        "moveddeleted-notice": "Šis puslapis buvo ištrintas.\nŽemiau pateikiamas puslapio šalinimų ir pervadinimų sąrašas.",
        "moveddeleted-notice-recent": "Atsiprašome, šis puslapis nesenai buvo ištrintas (per pastarąsias 24 valandas). Puslapio ištrynimo ir perkėlimo istorija yra pateikiama žemiau kaip nuoroda.",
        "userrights": "Naudotojų teisių valdymas",
        "userrights-lookup-user": "Tvarkyti naudotojo grupes",
        "userrights-user-editname": "Įveskite naudotojo vardą:",
-       "editusergroup": "Redaguoti naudotojo grupes",
+       "editusergroup": "Redaguoti {{GENDER:$1|naudotojo}} grupes",
        "editinguser": "Redaguojamos {{GENDER:$1|naudotojo}} <strong>[[User:$1|$1]]</strong> $2 teisės",
        "userrights-editusergroup": "Redaguoti naudotojų grupes",
-       "saveusergroups": "Saugoti naudotojų grupes",
+       "saveusergroups": "Saugoti {{GENDER:$1|naudotojo}} grupes",
        "userrights-groupsmember": "Narys:",
        "userrights-groupsmember-auto": "Narys automatiškai:",
        "userrights-groups-help": "Jūs galite pakeisti grupes, kuriose yra šis naudotojas:\n* Pažymėtas langelis reiškia, kad šis naudotojas yra toje grupėje.\n* Nepažymėtas langelis reiškia, kad šis naudotojas nėra toje grupėje.\n* * parodo, kad jūs nebegalėsite pašalinti grupės, kai ją pridėsite, ir atvirkščiai.",
        "right-createpage": "Kurti puslapius (kurie nėra aptarimų puslapiai)",
        "right-createtalk": "Kurti aptarimų puslapius",
        "right-createaccount": "Kurti naujas naudotojų paskyras",
+       "right-autocreateaccount": "Automatiškai prisijungti su išorine naudotojo paskyra",
        "right-minoredit": "Žymėti keitimus kaip smulkius",
        "right-move": "Pervadinti puslapius",
        "right-move-subpages": "Perkelti puslapius su jų subpuslapiais",
        "right-blockemail": "Blokuoti elektroninio pašto siuntimo galimybę naudotojui",
        "right-hideuser": "Blokuoti naudotojo vardą, paslepiant jį nuo viešinimo",
        "right-ipblock-exempt": "Apeiti IP blokavimus, autoblokavimus ir pakopinius blokavimus",
-       "right-proxyunbannable": "Apeiti automatinius proxy serverių blokavimus",
        "right-unblockself": "Atblokuoti pačius",
        "right-protect": "Pakeisti apsaugos lygius ir redaguoti apsaugotus puslapius",
        "right-editprotected": "Taisyti puslapius, apsaugotus kaip \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Kurti ir ištrinti [[Special:Tags|žymes]] iš duomenų bazės",
        "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",
+       "grant-generic": "\"$1\" teisių rinkinys",
+       "grant-group-page-interaction": "Sąveikauti su puslapiais",
+       "grant-group-file-interaction": "Sąveikauti su medija",
+       "grant-group-watchlist-interaction": "Sąveikauti su savo stebimu sąrašu",
+       "grant-group-email": "Siųsti el. laišką",
+       "grant-group-high-volume": "Atlikti didelės apimties veiklą",
+       "grant-group-customization": "Pritaikymas ir parinktys",
+       "grant-group-administration": "Atlikti administravimo veiksmų",
+       "grant-group-other": "Įvairios veiklos",
+       "grant-blockusers": "Užblokuoti ir atblokuoti naudotojus",
+       "grant-createaccount": "Kurti paskyras",
+       "grant-createeditmovepage": "Kurti, redaguoti ir perkelti puslapius",
+       "grant-delete": "Trinti puslapius, versijas ir žurnalų įrašus",
+       "grant-editinterface": "Redaguoti MediaWiki vardų sritį ir naudotojo CSS/JavaScript",
+       "grant-editmycssjs": "Redaguoti savo naudotojo CSS/JavaScript",
+       "grant-editmyoptions": "Redaguoti savo naudotojo nustatymus",
+       "grant-editmywatchlist": "Redaguoti savo stebėjimų sąrašą",
+       "grant-editpage": "Redaguoti egzistuojančius puslapius",
+       "grant-editprotected": "Redaguoti apsaugotus puslapius",
+       "grant-highvolume": "Didelės apimties redagavimas",
+       "grant-oversight": "Paslėpti naudotojus ir numalšinti versijas",
+       "grant-patrol": "Patrulių pakeitimai puslapiuose",
+       "grant-protect": "Apsaugoti ir neapsaugoti puslapiai",
+       "grant-rollback": "Atšaukti pakeitimus puslapiuose",
+       "grant-sendemail": "Siųsti el. laišką kitiems naudotojams",
+       "grant-uploadeditmovefile": "Įkelti, pakeisti ir perkelti failus",
+       "grant-uploadfile": "Įkelti naujų failų",
+       "grant-basic": "Pagrindinės teisės",
+       "grant-viewdeleted": "Peržiūrėti ištrintus failus ir puslapius",
+       "grant-viewmywatchlist": "Peržiūrėti savo stebėjimų sąrašą",
        "newuserlogpage": "Prisiregistravę naudotojai",
        "newuserlogpagetext": "Tai naudotojų kūrimo sąrašas.",
        "rightslog": "Naudotojų teisių pakeitimai",
        "action-createpage": "kurti puslapius",
        "action-createtalk": "kurti aptarimų puslapius",
        "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-minoredit": "žymėti keitimą kaip smulkų",
        "action-move": "pervadinti šį puslapį",
        "upload-form-label-select-file": "Pasirinkti failą",
        "upload-form-label-infoform-title": "Detalės",
        "upload-form-label-infoform-name": "Pavadinimas",
+       "upload-form-label-infoform-name-tooltip": "Unikalus aprašantis pavadinimas failo, kuris bus tarnaus kaip failo pavadinimas. Galite naudoti įprastą kalbą su tarpais. Nereikia pridėti failo plėtinio.",
        "upload-form-label-infoform-description": "Aprašymas",
+       "upload-form-label-infoform-description-tooltip": "Trumpai apibūdinkite viską, kad įsimintina apie šį darbą.\nNuotraukoms paminėkite pagrindinius dalykus, kurie yra pavaizduoti, asociacijas ar vietą.",
        "upload-form-label-usage-title": "Naudojimas",
        "upload-form-label-usage-filename": "Failo pavadinimas",
        "foreign-structured-upload-form-label-own-work": "Tai yra mano darbas",
        "foreign-structured-upload-form-label-own-work-message-shared": "Aš patvirtinu, kad man priklauso šio failo autorinės teisės ir sutinku neatšaukiamai išleisti šį failą į Wikimedia Commons su [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] licencija, ir aš sutinku su [https://wikimediafoundation.org/wiki/Terms_of_Use paslaugų teikimo sąlygomis].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Jeigu Jums nepriklauso šio failo autorinės teisės arba Jūs norite išleisti jį su kitokia licencija, apsvarstykite naudojimą [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons įkėlimo vedlį].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Jūs taip pat galite norėti išbandyti [[Special:Upload|{{SITENAME}} įkėlimo puslapį]], jeigu šis puslapis leidžia failų įkėlimą pagal jų politiką.",
+       "foreign-structured-upload-form-2-label-intro": "Ačiū jums, kad dovanojate šį paveikslėlį naudojimuisi {{SITENAME}}. Tęskite tik jeigu jis atitinka kelias sąlygas:",
+       "foreign-structured-upload-form-2-label-ownwork": "Tai turi būti visiškai <strong>jūsų paties kūryba</strong>, o ne tiesiog paimta iš interneto",
+       "foreign-structured-upload-form-2-label-noderiv": "Jame <strong>neturi būti kieno nors kito darbo</strong>, arba įkvėpto kitų",
+       "foreign-structured-upload-form-2-label-useful": "Tai turėtų būti <strong>edukaciška ir naudinga</strong> mokyti kitus",
+       "foreign-structured-upload-form-2-label-ccbysa": "Jis turi būti <strong>tinkamas publikuoti visam laikui</strong> internete su [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] licencija",
+       "foreign-structured-upload-form-2-label-alternative": "Jeigu ne viskas viršuje yra tiesa, jūs dar galite pabandyti įkelti šį failą naudodamiesi [https://commons.wikimedia.org/wiki/Special:UploadWizard Bendrybių įkėlimo vedliu], kol jis prieinamas pagal laisvąją licenciją.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Įkeldami failą, jūs patvirtinate, kad jums priklauso autorinės šio failo teisės ir sutinkate besąlygiškai išleisti šį failą į Wikimedia Commons pagal Creative Commons Attribution-ShareAlike 4.0 licenciją, ir jūs sutinkate su [https://wikimediafoundation.org/wiki/Terms_of_Use Naudojimosi sąlygomis].",
+       "foreign-structured-upload-form-3-label-question-website": "Ar atsisiuntėte šį paveikslėlį iš tinklapio, ar gavote jį iš paveikslėlių paieškos?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Ar jūs sukūrėte šį paveikslėlį (nufotografavote, nupiešėte, kt.) pats?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Ar jame yra, arba jis įkvėptas, kitiems priklausančio darbo, kaip pavyzdžiui logotipas?",
        "foreign-structured-upload-form-3-label-yes": "Taip",
        "foreign-structured-upload-form-3-label-no": "Ne",
+       "foreign-structured-upload-form-3-label-alternative": "Deja, šiuo atveju, šis įrankis nepalaiko šio failo įkėlimo. Jūs dar galite pabandyti įkelti jį pasinaudodami [https://commons.wikimedia.org/wiki/Special:UploadWizard Bendrybių įkėlimo vedliu], tol, kol jis yra prieinamas pagal laisvąją licenciją.",
+       "foreign-structured-upload-form-4-label-good": "Naudodami šį įrankį, galite įkelti edukacinius grafikus, kuriuos jūs sukūrėte ar fotografijas, kurias padarėte ir kuriose nėra darbo, kuris priklauso kam nors kitam.",
+       "foreign-structured-upload-form-4-label-bad": "Jūs negalite įkelti paveikslėlių rastų paieškos variklyje ar atsisiųstų iš kitų tinklapių.",
        "backend-fail-stream": "Negali būti apdorotas failas $1.",
        "backend-fail-backup": "Negali būti išsaugotas failas $1.",
        "backend-fail-notexists": "Failas $1 neegzistuoja.",
        "log-title-wildcard": "Ieškoti pavadinimų, prasidedančių šiuo tekstu",
        "showhideselectedlogentries": "Rodyti/slėpti pasirinktus sąrašo elementus",
        "log-edit-tags": "Redaguoti žymes pasirinktuose žurnalo įrašuose",
+       "checkbox-select": "Pasirinkti: $1",
+       "checkbox-all": "Visi",
+       "checkbox-none": "Nieko",
+       "checkbox-invert": "Invertuoti",
        "allpages": "Visi puslapiai",
        "nextpage": "Kitas puslapis ($1)",
        "prevpage": "Ankstesnis puslapis ($1)",
        "listgrouprights-namespaceprotection-header": "Vardų srities apribojimai",
        "listgrouprights-namespaceprotection-namespace": "Vardų sritis",
        "listgrouprights-namespaceprotection-restrictedto": "Teisė(s), leidžiančios naudotojui atlikti keitimus",
+       "listgrants": "Leidimai",
+       "listgrants-summary": "Toliau pateiktas leidimų sąrašas su susietomis naudotojo teisėmis. Naudotojai gali įgalioti programėles naudoti jų paskyras, bet su apribotais leidimais, kuriuos naudotojas suteikė programėlei. Programėlė veikianti naudotojo vardu negali naudotis teisėmis, kurių neturi pats naudotojas.\nGali būti [[{{MediaWiki:Listgrouprights-helppage}}|papildomos informacijos]] apie individualias teises.",
+       "listgrants-grant": "Leisti",
+       "listgrants-rights": "Teisės",
        "trackingcategories": "Sekimo kategorijos",
        "trackingcategories-summary": "Šiame puslapyje išdėstytos sekimo kategorijos, kurias savaime sudaro MediaWiki programinė įranga. Jų pavadinimus galima pakeisti pakeičiant sistemos pranešimus {{ns:8}} vardų srityje.",
        "trackingcategories-msg": "Sekimo kategorija",
        "watchlistall2": "visi",
        "watchlist-hide": "Slėpti",
        "watchlist-submit": "Rodyti",
-       "wlshowtime": "Rodyti paskutinį:",
+       "wlshowtime": "Laiko trukmė, kurią rodyti:",
        "wlshowhideminor": "smulkūs pakeitimai",
        "wlshowhidebots": "robotai",
        "wlshowhideliu": "prisiregistravę naudotojai",
        "wlshowhideanons": "anoniminiai naudotojai",
        "wlshowhidepatr": "prižiūrėti pakeitimai",
        "wlshowhidemine": "mano pakeitimai",
+       "wlshowhidecategorization": "puslapių kategorizavimas",
        "watchlist-options": "Stebimųjų sąrašo parinktys",
        "watching": "Įtraukiama į stebimųjų sąrašą...",
        "unwatching": "Šalinama iš stebimųjų sąrašo...",
        "unblock": "Atblokuoti naudotoją",
        "blockip": "Blokuoti naudotoją",
        "blockip-legend": "Blokuoti naudotoją",
-       "blockiptext": "Naudokite šią formą, kad uždraustumėte redagavimo prieigą pasirinktam IP adresui ar naudotojui. Tai turėtų būti atliekama tik tam, kad sustabdytumėte vandalizmą, ir neprieštarauti [[{{MediaWiki:Policy-url}}|projekte galiojančioms taisyklėms]].\nŽemiau pateikite tikslią priežastį (pavyzdžiui, nurodydami sugadintus puslapius).",
+       "blockiptext": "Naudokite šią formą, kad uždraustumėte redagavimo prieigą pasirinktam IP adresui ar naudotojui. Tai turėtų būti atliekama tik tam, kad sustabdytumėte vandalizmą, ir neprieštarauti [[{{MediaWiki:Policy-url}}|projekte galiojančioms taisyklėms]].\nŽemiau pateikite tikslią priežastį (pavyzdžiui, nurodydami sugadintus puslapius).\nJūs galite blokuoti IP intervalus pasinaudodami [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] sintakse; didžiausias leidžiamas intervalas yra /$1 IPv4 ir /$2 IPv6.",
        "ipaddressorusername": "IP adresas arba naudotojo vardas",
        "ipbexpiry": "Galiojimo laikas",
        "ipbreason": "Priežastis:",
        "block-log-flags-hiddenname": "naudotojo vardas paslėptas",
        "range_block_disabled": "Administratoriams neleidžiama blokuoti IP adresų sričių.",
        "ipb_expiry_invalid": "Galiojimo laikas neleistinas.",
+       "ipb_expiry_old": "Galiojimo laikas yra praeityje.",
        "ipb_expiry_temp": "Paslėptų naudotojų vardų blokavimas turi būti neribotas.",
        "ipb_hide_invalid": "Negalima paslėpti šios paskyros; ji gali turėti daugiau nei {{PLURAL:$1|vieną keitimą|$1 keitimus|$1 keitimų}}.",
        "ipb_already_blocked": "„$1“ jau užblokuotas",
        "move-page-legend": "Puslapio pervadinimas",
        "movepagetext": "Naudodamiesi žemiau pateikta forma, pervadinsite puslapį\nneprarasdami jo istorijos.\nSenasis pavadinimas taps nukreipiamuoju - rodys į naująjį.\nNuorodos į senąjį puslapį nebus automatiškai pakeistos, todėl būtinai\npatikrinkite ar nesukūrėte [[Special:DoubleRedirects|dvigubų]] ar\n[[Special:BrokenRedirects|neveikiančių]] nukreipimų.\nJūs esate atsakingas už tai, kad nuorodos rodytų į ten, kur ir norėta.\n\nPrimename, kad puslapis '''nebus''' pervadintas, jei jau yra puslapis\nnauju pavadinimu, nebent tas puslapis tuščias arba nukreipiamasis ir\nneturi redagavimo istorijos. Taigi, jūs galite pervadinti puslapį\nseniau naudotu vardu, jei prieš tai jis buvo per klaidą pervadintas,\no egzistuojančių puslapių sugadinti negalite.\n\n'''DĖMESIO!'''\nJei pervadinate populiarų puslapį, tai gali sukelti nepageidaujamų\nšalutinių efektų, dėl to šį veiksmą vykdykite tik įsitikinę,\nkad suprantate visas pasekmes.",
        "movepagetext-noredirectfixer": "Naudodamiesi žemiau pateikta forma, pervadinsite puslapį perkeldami visą jo istoriją į naująjį pavadinimą.\nSenasis pavadinimas taps nukreipiamuoju puslapiu į naująjį.\nNuorodos į senąjį puslapį nebus automatiškai pakeistos, todėl būtinai\npatikrinkite, ar nesukūrėte [[Special:DoubleRedirects|dvigubų]] ar [[Special:BrokenRedirects|neveikiančių]] nukreipimų.\nJūs esate atsakingas už tai, kad nuorodos rodytų į ten, kur ir norėta.\n\nPrimename, kad puslapis '''nebus''' pervadintas, jei jau yra puslapis nauju pavadinimu, nebent tas puslapis yra tuščias arba nukreipiamasis ir neturi redagavimo istorijos.\nTaigi, jūs galite pervadinti puslapį seniau naudotu vardu, jei prieš tai jis buvo per klaidą pervadintas, o egzistuojančių puslapių sugadinti negalite.\n\n'''Dėmesio!'''\nJei pervadinate populiarų puslapį, tai gali sukelti nepageidaujamų šalutinių efektų,\ndėl to šį veiksmą vykdykite tik įsitikinę, kad suprantate visas pasekmes.",
-       "movepagetalktext": "Susietas aptarimo puslapis bus automatiškai perkeltas kartu su juo, '''išskyrus:''':\n*Puslapis nauju pavadinimu jau turi netuščią aptarimo puslapį, arba\n*Paliksite žemiau esančia varnelę nepažymėtą.\n\nŠiais atvejais jūs savo nuožiūra turite perkelti arba apjungti aptarimo puslapį.",
+       "movepagetalktext": "Jeigu pažymėsite, susietas aptarimo puslapis bus automatiškai perkeltas į naują pavadinimą, nebent ten jau egzistuoja ne tuščias aptarimo puslapis.\n\nTokiu atveju turėsite perkelti arba apjungti puslapį rankiniu būdu, jeigu norėsite.",
        "moveuserpage-warning": "'''Dėmesio:''' Jūs ruošiatės perkelti naudotojo puslapį. Atkreipkite dėmesį, kad bus perkeltas tik puslapis, naudotojas ''nebus'' pervadintas.",
        "movecategorypage-warning": "<strong>Dėmesio:</strong> Jūs ketinate pervadinti kategorijos puslapį. Atminkite, kad tik pats puslapis bus pervadintas, tačiau kategorijai priskirti puslapiai <em>nebus</em> perkelti naujon kategorijon.",
        "movenologintext": "Norėdami pervadinti puslapį, turite būti užsiregistravęs naudotojas ir būti  [[Special:UserLogin|prisijungęs]].",
        "export-download": "Saugoti kaip failą",
        "export-templates": "Įtraukti šablonus",
        "export-pagelinks": "Įtraukti susietus puslapius iki šio gylio:",
+       "export-manual": "Pridėti puslapių rankiniu būdu:",
        "allmessages": "Visi sistemos tekstai bei pranešimai",
        "allmessagesname": "Pavadinimas",
        "allmessagesdefault": "Pradinis tekstas",
        "javascripttest-pagetext-frameworks": "Prašome pasirinkti vieną iš išvardintų testavimo struktūrų: $1",
        "javascripttest-pagetext-skins": "Pasirinkite naudotojo sąsajos išvaizdą, kuriai atliksite testavimą:",
        "javascripttest-qunit-intro": "Peržiūrėkite [$1 testavimo dokumentaciją]",
-       "tooltip-pt-userpage": "Jūsų naudotojo puslapis",
+       "tooltip-pt-userpage": "{{GENDER:|Jūsų naudotojo}} puslapis",
        "tooltip-pt-anonuserpage": "Naudotojo puslapis jūsų IP adresui",
-       "tooltip-pt-mytalk": "Jūsų aptarimo puslapis",
+       "tooltip-pt-mytalk": "{{GENDER:|Jūsų}} aptarimo puslapis",
        "tooltip-pt-anontalk": "Pakeitimų aptarimas, darytus naudojant šį IP adresą",
-       "tooltip-pt-preferences": "Mano nustatymai",
+       "tooltip-pt-preferences": "{{GENDER:|Mano}} nustatymai",
        "tooltip-pt-watchlist": "Puslapių sąrašas, kuriuos jūs pasirinkote stebėti",
-       "tooltip-pt-mycontris": "Jūsų darytų keitimų sąrašas",
+       "tooltip-pt-mycontris": "{{GENDER:|Jūsų}} darytų keitimų sąrašas",
        "tooltip-pt-anoncontribs": "Keitimų sąrašas, padarytų iš šio IP adreso",
        "tooltip-pt-login": "Rekomenduojame prisijungti, nors tai nėra privaloma",
        "tooltip-pt-logout": "Atsijungti",
        "tooltip-t-recentchangeslinked": "Paskutiniai keitimai puslapiuose, pasiekiamuose iš šio puslapio",
        "tooltip-feed-rss": "Šio puslapio RSS šaltinis",
        "tooltip-feed-atom": "Šio puslapio Atom šaltinis",
-       "tooltip-t-contributions": "Rodyti šio naudotojo keitimų sąrašą",
-       "tooltip-t-emailuser": "Siųsti laišką šiam naudotojui",
+       "tooltip-t-contributions": "Rodyti {{GENDER:$1|šio naudotojo}} keitimų sąrašą",
+       "tooltip-t-emailuser": "Siųsti laišką {{GENDER:$1|šiam naudotojui}}",
        "tooltip-t-info": "Daugiau žinių apie šį puslapį",
        "tooltip-t-upload": "Įkelti rinkmenas",
        "tooltip-t-specialpages": "Specialiųjų puslapių sąrašas",
        "pageinfo-category-files": "Failų skaičius",
        "markaspatrolleddiff": "Žymėti, kad patikrinta",
        "markaspatrolledtext": "Pažymėti, kad puslapis patikrintas",
+       "markaspatrolledtext-file": "Pažymėti šią failo versiją kaip patruliuojamą",
        "markedaspatrolled": "Pažymėtas kaip patikrintas",
        "markedaspatrolledtext": "Pasirinkta [[:$1]] versija pažymėta kaip patikrinta.",
        "rcpatroldisabled": "Paskutinių keitimų tikrinimas išjungtas",
        "newimages-legend": "Filtras",
        "newimages-label": "Failo vardas (ar jo dalis):",
        "newimages-showbots": "Rodyti robotų atliktus įkėlimus",
+       "newimages-hidepatrolled": "Paslėpti patruliuotus įkeltus failus",
        "noimages": "Nėra ką parodyti.",
        "ilsubmit": "Ieškoti",
        "bydate": "pagal datą",
        "scarytranscludefailed-httpstatus": "[Šablono iškviesti nepavyko $1: HTTP $2]",
        "scarytranscludetoolong": "[URL per ilgas]",
        "deletedwhileediting": "Dėmesio: Šis puslapis ištrintas po to, kai pradėjote redaguoti!",
-       "confirmrecreate": "{{GENDER:$1|Naudotojas&nbsp;|Naudotoja&nbsp;|}}[[User:$1|$1]] ([[User talk:$1|aptarimas]]) ištrynė šį puslapį po to, kai pradėjote jį redaguoti. Trynimo priežastis:\n: ''$2''\nPrašome patvirtinti, kad tikrai norite iš naujo sukurti puslapį.",
-       "confirmrecreate-noreason": "{{GENDER:$1|Naudotojas&nbsp;|Naudotoja&nbsp;|}}[[User:$1|$1]] ([[User talk:$1|aptarimas]]) ištrynė šį puslapį po to, kai jūs pradėjote redaguoti. Prašome patvirtinti, jog jūs tikrai norite atkurti šį puslapį.",
+       "confirmrecreate": "{{GENDER:$1|Naudotojas&nbsp;|Naudotoja&nbsp;|}}[[User:$1|$1]] ([[User talk:$1|aptarimas]]) {{GENDER:$1|ištrynė}} šį puslapį po to, kai pradėjote jį redaguoti. Trynimo priežastis:\n: <em>$2</em>\nPrašome patvirtinti, kad tikrai norite iš naujo sukurti puslapį.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Naudotojas&nbsp;|Naudotoja&nbsp;|}}[[User:$1|$1]] ([[User talk:$1|aptarimas]]) {{GENDER:$1|ištrynė}} šį puslapį po to, kai jūs pradėjote redaguoti. Prašome patvirtinti, jog jūs tikrai norite atkurti šį puslapį.",
        "recreate": "Atkurti",
        "confirm_purge_button": "Gerai",
        "confirm-purge-top": "Išvalyti šio puslapio podėlį?",
        "watchlisttools-edit": "Rodyti ir redaguoti stebimųjų sąrašą",
        "watchlisttools-raw": "Redaguoti grynąjį sąrašą",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|aptarimas]])",
+       "timezone-local": "Vietinis",
        "duplicate-defaultsort": "Įspėjimas: Numatytasis rikiavimo raktas „$2“ pakeičia ankstesnį numatytąjį rikiavimo raktą „$1“.",
        "duplicate-displaytitle": "<strong>Perspėjimas:</strong> Rodymas pavadinimas „$2“ pakeičia ankstesnį rodoma pavadinimą „$1“.",
        "invalid-indicator-name": "<strong>Klaida:</strong> Puslapio statuso <code>pavadinimo</code> rodiklio požymis turi būti netuščias.",
        "version-libraries-license": "Licenzija",
        "version-libraries-description": "Aprašymas",
        "version-libraries-authors": "Autoriai",
-       "redirect": "Nukreiptas iš failo, naudotojo arba versijos ID",
+       "redirect": "Nukreiptas iš failo, naudotojo, versijos arba žurnalo įrašo ID",
        "redirect-legend": "Nukreipti į failą ar puslapį",
-       "redirect-summary": "Šis specialus puslapis peradresuoją į failą (nurodant failo pavadinimą), puslapį (nurodant versijos ID ar puslapio ID), arba vartotojo puslapį (nurodant skaitinį vartotojo ID).\nNaudojimas: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Šis specialus puslapis peradresuoją į failą (nurodant failo pavadinimą), puslapį (nurodant versijos ID ar puslapio ID), naudotojo puslapį (nurodant skaitinį naudotojo ID), arba žurnalo įrašą (nurodant žurnalo įrašo ID).\nNaudojimas: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]], arba[[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Eiti",
        "redirect-lookup": "Peržvalgos:",
        "redirect-value": "Vertė:",
        "redirect-page": "Puslapio ID",
        "redirect-revision": "Puslapio peržiūra",
        "redirect-file": "Failo vardas",
+       "redirect-logid": "Žurnalo įrašo ID",
        "redirect-not-exists": "Vertė nėra nustatyta",
        "fileduplicatesearch": "Ieškoti dublikuotų failų",
        "fileduplicatesearch-summary": "Pasikartojančių failų paieška pagal jų kontrolinę sumą.",
        "tags-deactivate": "išjungti",
        "tags-hitcount": "$1 {{PLURAL:$1|pakeitimas|pakeitimai|pakeitimų}}",
        "tags-manage-no-permission": "Jūs neturite teisių valdyti pakeitimo žymes.",
+       "tags-manage-blocked": "Jūs negalite valdyti pakeitimų žymių, kol esate užblokuotas.",
        "tags-create-heading": "Kurti naują žymą",
        "tags-create-explanation": "Pagal numatytuosius nustatymus naujai sukurtos žymos bus prieinamos naudojimui vartotojams ir robotams.",
        "tags-create-tag-name": "Žymos pavadinimas:",
        "tags-deactivate-not-allowed": "Neįmanoma išjungti žymę „$1“.",
        "tags-deactivate-submit": "Išjungti",
        "tags-apply-no-permission": "Jūs neturite leidimo taikyti pakeitimo žymes kartu su savo pakeitimais.",
+       "tags-apply-blocked": "Jūs negalite pridėti pakeitimų žymių prie savo pakeitimų, kol esate užblokuotas.",
        "tags-apply-not-allowed-one": "Žymė „$1“ negali būti taikoma rankiniu būdu.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|žyme|žymės}} negali būti taikoma rankiniu būdu: $1",
        "tags-update-no-permission": "Jūs neturite leidimo pridėti arba pašalinti pokyčių žymes iš atskirų pakeitimų ar žurnalo įrašų.",
+       "tags-update-blocked": "Negalite pridėti ar pašalinti pakeitimų žymių, kol esate užblokuotas.",
        "tags-update-add-not-allowed-one": "Žymė „$1“ negali būti pridėta rankiniu būdu.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|žymė|žymės}} negali būti pridėtos rankiniu būdu: $1",
        "tags-update-remove-not-allowed-one": "Žymė \"$1\" negali būti pašalinta.",
        "expand_templates_preview": "Peržiūra",
        "expand_templates_preview_fail_html": "<em>Nes {{SITENAME}} turi įgalinta gryną HTML ir įvyko sesijos duomenų praradimas, peržiūra yra paslėpta kaip atsargos priemonė prieš JavaScript atakas.</em>\n\n<strong>Jei tai teisėtas peržiūros bandymas, prašome bandyti dar kartą.</strong>\nJei tai vistiek neveikia, pabandykite [[Special:UserLogout|atsijungti]] ir vėl prisijungti.",
        "expand_templates_preview_fail_html_anon": "<em>Nes {{SITENAME}} turi įgalinta gryną HTML ir jūs esate neprisijungęs, peržiūra paslėpta kaip atsargumo priemonė prieš JavaScript atakas.</em>\n\n<strong>Jei tai teisėtas peržiūros bandymas prašome [[Special:UserLogin|prisijungti]] ir bandyti vėl.</strong>",
+       "expand_templates_input_missing": "Turite pateikti bent truputį įvesties teksto.",
        "pagelanguage": "Puslapio kalbos pasirinkimas",
        "pagelang-name": "Puslapis",
        "pagelang-language": "Kalba",
        "pagelang-use-default": "Naudoti numatytąją kalbą",
        "pagelang-select-lang": "Pasirinkite kalbą",
+       "pagelang-submit": "Pateikti",
        "right-pagelang": "Keisti puslapio kalbą",
        "action-pagelang": "keisti puslapio kalbą",
        "log-name-pagelang": "Keisti kalbos žurnalą",
        "mediastatistics": "Daugialypės terpės statistika",
        "mediastatistics-summary": "Statistika apie įkeltus failo tipus. Tai apima tik naujausias failo versijas. Senos ar ištrintos failų versijos nėra įtrauktos.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bitas|$1 bitai}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Visas failo dydis šiai sekcijai: {{PLURAL:$1|$1 baitas|$1 baitai}} ($2; $3%).",
+       "mediastatistics-allbytes": "Visas failo dydis visiems failams: {{PLURAL:$1|$1 baitas|$1 baitai}} ($2).",
        "mediastatistics-table-mimetype": "MIME tipas",
        "mediastatistics-table-extensions": "Galimi plėtiniai",
        "mediastatistics-table-count": "Failų skaičius",
        "mediastatistics-header-text": "Tekstinis",
        "mediastatistics-header-executable": "Vykdomieji",
        "mediastatistics-header-archive": "Suspausti formatai",
+       "mediastatistics-header-total": "Visi failai",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|galinis kablelis buvo|galiniai kableliai buvo}} buvo pašalinti iš JSON",
        "json-error-unknown": "Iškilo problema su JSON. Klaida: $1",
        "json-error-depth": "Maksimalus krūvos dydis buvo viršytas",
        "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ę."
+       "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"
 }
index 0b07200..47bf92b 100644 (file)
        "morenotlisted": "Šis saraksts nav pilnīgs.",
        "mypage": "Lapa",
        "mytalk": "Diskusijas",
-       "anontalk": "Šīs IP adreses diskusija",
+       "anontalk": "Diskusijas",
        "navigation": "Navigācija",
        "and": "&#32;un",
        "qbfind": "Meklēšana",
        "laggedslavemode": "Uzmanību: Iespējams, šajā lapā nav redzami nesen izdarītie papildinājumi.",
        "readonly": "Datubāze bloķēta",
        "enterlockreason": "Ievadiet bloķēšanas iemeslu, ieskaitot aplēses, kad bloķēšana tiks beigta.",
-       "readonlytext": "Datubāze šobrīd ir bloķēta jaunu ierakstu izveidošanai un citām izmaiņām, visticamāk, kārtējā datubāzes uzturēšanas pasākuma dēļ, pēc kura tā tiks atjaunota normālā stāvoklī.\n\nAdministrators, kurš nobloķēja datubāzi, norādīja šādu iemeslu: $1",
+       "readonlytext": "Datubāze šobrīd ir bloķēta jaunu ierakstu izveidošanai un citām izmaiņām, visticamāk, kārtējā datubāzes uzturēšanas pasākuma dēļ, pēc kura tā tiks atjaunota normālā stāvoklī.\n\nSistēmas administrators, kurš nobloķēja datubāzi, norādīja šādu iemeslu: $1",
        "missing-article": "Teksts lapai ar nosaukumu \"$1\" $2 datubāzē nav atrodams.\n\nTas parasti notiek novecojušu saišu gadījumā: pieprasot izmaiņas vai hronoloģiju lapai, kas ir izdzēsta.\n\nJa lapai ir jābūt, tad, iespējams, ir kļūda programmā.\nPar to varat ziņot [[Special:ListUsers/sysop|kādam administratoram]], norādot arī URL.",
        "missingarticle-rev": "(Pārskatīšana #: $1)",
        "missingarticle-diff": "(Salīdz.: $1, $2)",
        "title-invalid-interwiki": "Pieprasītais lapas nosaukums satur interwiki saiti, bet tās nevar lietot nosaukumos.",
        "title-invalid-talk-namespace": "Pieprasītais lapas nosaukums norāda uz diskusijas lapu, kas nevar pastāvēt.",
        "title-invalid-characters": "Pieprasītais lapas nosaukums satur nederīgus simbolus: \"$1\".",
+       "title-invalid-leading-colon": "Pieprasītās lapas nosaukums satur neatļautu kolu tā sākumā.",
        "perfcached": "Šie dati ir no servera kešatmiņas un var būt novecojuši. A maximum of {{PLURAL:$1|one result is|$1 results are}} available in the cache.",
        "perfcachedts": "Šie dati ir no servera kešatmiņas (''cache''), kas pēdējo reizi bija atjaunota $1. Kešatmiņā {{PLURAL:$4|pieejami|pieejams|pieejami}} ne vairāk kā {{PLURAL:$4|$4 rezultāti|viens rezultāts|$4 rezultāti}}.",
        "querypage-no-updates": "Šīs lapas atjaunošana pagaidām ir atslēgta. Te esošie dati tuvākajā laikā netiks atjaunoti.",
        "viewsource-title": "Apskatīt $1 izejas kodu",
        "actionthrottled": "Darbība netika atļauta",
        "protectedpagetext": "Šī lapa ir aizsargāta, lai novērstu tās izmainīšanu vai citas darbības.",
-       "viewsourcetext": "Tu vari apskatīt un nokopēt šīs lapas vikitekstu:",
+       "viewsourcetext": "Tu vari apskatīt un nokopēt šīs lapas vikitekstu.",
        "protectedinterface": "Šī lapa satur programmatūras interfeisā lietotu tekstu un ir bloķēta pret izmaiņām, lai pasargātu no bojājumiem.\nLai pievienotu izmaiņas tulkojumā visās ''wiki'', lūdzam izmantot ''MediaWiki'' lokalizēšanas projektu [//translatewiki.net/ translatewiki.net].",
        "editinginterface": "'''Brīdinājums:''' Tu izmaini lapu, kuras saturu izmanto wiki programmatūras lietotāja saskarnē (''interfeisā''). Šīs lapas izmaiņas ietekmēs lietotāja saskarni citiem lietotājiem. Pēc modificēšanas šīs izmaiņas būtu lietderīgi pievienot arī ''MediaWiki'' lokalizēšanas projektā [//translatewiki.net/ translatewiki.net].",
        "namespaceprotected": "Tev nav atļaujas izmainīt lapas, kas atrodas '''$1''' ''namespacē''.",
        "throttled-mailpassword": "Paroles atgādinājums jau ir ticis nosūtīts {{PLURAL:$1|pēdējās stundas|pēdējo $1 stundu}} laikā.\nLai novērstu šīs funkcijas ļaunprātīgu izmantošanu, iespējams nosūtīt tikai vienu paroles atgādinājumu, {{PLURAL:$1|katru stundu|katras $1 stundas}}.",
        "mailerror": "E-pasta sūtīšanas kļūda: $1",
        "acct_creation_throttle_hit": "Lietotāji no tavas IP adreses šajā viki pēdējo 24 stundu laikā jau ir izveidojuši {{PLURAL:$1|$1 kontus|1 kontu|$1 kontus}}, kas ir maksimālais atļautais skaits šajā laika periodā.\nTādēļ šobrīd no šīs IP adreses vairs nevar izveidot jaunus kontus.",
-       "emailauthenticated": "Tava e-pasta adrese tika apstiprināta $2, $3.",
-       "emailnotauthenticated": "Tava e-pasta adrese <strong>vēl nav apstiprināta</strong> un zemāk norādītās iespējas nav pieejamas.",
+       "emailauthenticated": "Tava e-pasta adrese tika apstiprināta $2 $3.",
+       "emailnotauthenticated": "Tava e-pasta adrese vēl nav apstiprināta.\nE-pasts netiks sūtīts, izmantojot šīs funkcijas.",
        "noemailprefs": "Norādi e-pasta adresi, lai lietotu šīs iespējas.",
        "emailconfirmlink": "Apstiprināt tavu e-pasta adresi",
        "invalidemailaddress": "E-pasta adrese nevar tikt apstiprināta, jo izskatās nederīga. Lūdzu ievadi korekti noformētu e-pasta adresi, vai arī atstāj to lauku tukšu.",
        "retypenew": "Atkārto jauno paroli",
        "resetpass_submit": "Uzstādīt paroli un ieiet",
        "changepassword-success": "Jūsu parole tika nomainīta veiksmīgi!",
+       "botpasswords-label-appid": "Bota nosaukums:",
+       "botpasswords-label-create": "Izveidot",
+       "botpasswords-label-update": "Atjaunināt",
+       "botpasswords-label-cancel": "Atcelt",
+       "botpasswords-label-delete": "Dzēst",
+       "botpasswords-label-resetpassword": "Atiestatīt paroli",
+       "botpasswords-label-restrictions": "Lietošanas ierobežojumi:",
+       "botpasswords-label-grants-column": "Piešķirts",
        "resetpass_forbidden": "Paroles nav iespējams nomainīt",
        "resetpass-no-info": "Jums ir nepieciešams ieiet, lai tūlīt piekļūtu šai lapai.",
        "resetpass-submit-loggedin": "Mainīt paroli",
        "right-blockemail": "Bloķēt citus dalībniekus (iespēja sūtīt e-pastu)",
        "right-hideuser": "Bloķēt lietotājvārdu, slēpjot to no citiem dalībniekiem",
        "right-ipblock-exempt": "Apiet IP bloķēšanu, automātisku bloķēšanu un IP apgabalu bloķēšanu",
-       "right-proxyunbannable": "Apiet ''proxy'' automātiskos blokus",
        "right-unblockself": "Atbloķēt sevi",
        "right-protect": "Izmainīt aizsargātās lapas un to aizsardzības līmeni",
        "right-editprotected": "Labot aizsargātās lapas (bez kaskādes aizsardzības)",
        "recentchanges-label-plusminus": "Par tik baitiem tika izmainīts lapas izmērs",
        "recentchanges-legend-heading": "'''Apzīmējumi:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (skatīt arī [[Special:NewPages|jaunās lapas]])",
+       "recentchanges-submit": "Rādīt",
        "rcnotefrom": "Šobrīd redzamas izmaiņas kopš '''$2''' (parādītas ne vairāk par '''$1''').",
        "rclistfrom": "Parādīt jaunas izmaiņas kopš $3 $2",
        "rcshowhideminor": "$1 maznozīmīgos",
        "mostrevisions": "Raksti, kuriem ir visvairāk iepriekšēju versiju",
        "prefixindex": "Meklēt pēc virsraksta pirmajiem burtiem",
        "prefixindex-namespace": "Visas lapas ar prefiksu ($1 vārdtelpa)",
+       "prefixindex-submit": "Rādīt",
        "shortpages": "Īsākās lapas",
        "longpages": "Garākās lapas",
        "deadendpages": "Lapas bez izejošām saitēm",
        "usereditcount": "$1 {{PLURAL:$1|izmaiņas|izmaiņa|izmaiņas}}",
        "usercreated": "{{GENDER:$3|Izveidoja}} $1 plkst. $2",
        "newpages": "Jaunas lapas",
+       "newpages-submit": "Rādīt",
        "newpages-username": "Lietotājs:",
        "ancientpages": "Vecākās lapas",
        "move": "Pārvietot",
        "specialloguserlabel": "Izpildītājs:",
        "speciallogtitlelabel": "Mērķis (nosaukums vai lietotājs):",
        "log": "Reģistri",
+       "logeventslist-submit": "Rādīt",
        "all-logs-page": "Visi publiski pieejamie reģistri",
        "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.",
        "allpages-hide-redirects": "Paslēpt pāradresācijas",
        "cachedspecial-refresh-now": "Skatīt jaunāko.",
        "categories": "Kategorijas",
+       "categories-submit": "Rādīt",
        "categoriespagetext": "{{PLURAL:$1|Šīs kategorijas|Šī kategorija|Šīs kategorijas}} satur lapas vai failus.\nŠeit nav parādītas [[Special:UnusedCategories|neizmantotās kategorijas]].\nSkatīt arī [[Special:WantedCategories|''sarkanās'' kategorijas]].",
        "categoriesfrom": "Parādīt kategorijas sākot ar:",
        "special-categories-sort-count": "kārtot pēc skaita",
        "delete-confirm": "Dzēst \"$1\"",
        "delete-legend": "Dzēšana",
        "historywarning": "'''Brīdinājums:''' Lapai, ko tu gatavojies dzēst, ir vēsture ar aptuveni $1 {{PLURAL:$1|versijām|versiju|versijām}}:",
+       "historyaction-submit": "Rādīt",
        "confirmdeletetext": "Tu tūlīt no datubāzes dzēsīsi lapu vai attēlu, kā arī to iepriekšējās versijas. Lūdzu, apstiprini, ka tu tiešām to vēlies darīt, ka tu apzinies sekas un ka tu to dari saskaņā ar [[{{MediaWiki:Policy-url}}|vadlīnijām]].",
        "actioncomplete": "Darbība pabeigta",
        "actionfailed": "Darbība neizdevās",
        "tooltip-pt-anontalk": "Diskusija par labojumiem, kas izdarīti no šīs IP adreses",
        "tooltip-pt-preferences": "Tavas izvēles",
        "tooltip-pt-watchlist": "Tevis uzraudzītās lapas",
-       "tooltip-pt-mycontris": "Tavs devums",
+       "tooltip-pt-mycontris": "{{GENDER:|Tavs}} devums",
        "tooltip-pt-anoncontribs": "Labojumi, kas veikti no šīs IP adreses",
        "tooltip-pt-login": "Aicinām tevi ieiet {{grammar:lokatīvs|{{SITENAME}}}}, tomēr tas nav obligāti.",
        "tooltip-pt-logout": "Iziet",
        "tooltip-t-recentchangeslinked": "Izmaiņas, kas nesen izdarītas lapās, kurās ir saites uz šo lapu",
        "tooltip-feed-rss": "Šīs lapas RSS barotne",
        "tooltip-feed-atom": "Šīs lapas Atom barotne",
-       "tooltip-t-contributions": "Apskatīt šā lietotāja ieguldījumu uzskaitījumu.",
-       "tooltip-t-emailuser": "Sūtīt e-pastu šim lietotājam",
+       "tooltip-t-contributions": "{{GENDER:$1|Šī dalībnieka|Šīs dalībnieces}} ieguldījumu uzskaitījums",
+       "tooltip-t-emailuser": "Sūtīt e-pastu {{GENDER:$1|šim dalībniekam|šai dalībniecei}}",
        "tooltip-t-upload": "Augšupielādēt failus",
        "tooltip-t-specialpages": "Visu īpašo lapu uzskaitījums",
        "tooltip-t-print": "Drukājama lapas versija",
        "compare-revision-not-exists": "Norādītā versija neeksistē.",
        "dberr-problems": "Atvainojiet!\nŠai vietnei ir radušās tehniskas problēmas.",
        "dberr-again": "Uzgaidiet dažas minūtes un pārlādējiet šo lapu.",
-       "dberr-info": "(Nevar sazināties ar datubāzes serveri: $1)",
-       "dberr-info-hidden": "(Nevar sazināties ar datubāzes serveri)",
+       "dberr-info": "(Nevar piekļūt datubāzei: $1)",
+       "dberr-info-hidden": "(Nevar piekļūt datubāzei)",
        "dberr-usegoogle": "Pa to laiku Jūs varat izmantot Google meklēšanu.",
        "dberr-outofdate": "Ņemiet vērā, ka mūsu satura indeksācija var būt novecojusi.",
        "dberr-cachederror": "Šī ir lapas agrāk saglabātā kopija, tā var nebūt atjaunināta.",
index 9e1c5cb..a92dfea 100644 (file)
        "march": "मार्च",
        "april": "अप्रैल",
        "may_long": "मई",
-       "june": "à¤\9cà¥\82न",
-       "july": "à¤\9cà¥\81लाà¤\87",
+       "june": "à¤\9cà¥\81न",
+       "july": "à¤\9cà¥\81लाà¤\88",
        "august": "अगस्त",
        "september": "सितम्बर",
-       "october": "à¤\85à¤\95à¥\8dà¤\9fà¥\82बर",
-       "november": "नवà¤\82बर",
-       "december": "दिसà¤\82बर",
+       "october": "à¤\85à¤\95à¥\8dà¤\9fà¥\81बर",
+       "november": "नवमà¥\8dबर",
+       "december": "दिसमà¥\8dबर",
        "january-gen": "जनवरी",
        "february-gen": "फरबरी",
        "march-gen": "मार्च",
        "mar": "मा.",
        "apr": "अप.",
        "may": "मई",
-       "jun": "à¤\9cà¥\82.",
+       "jun": "à¤\9cà¥\81न.",
        "jul": "जु.",
        "aug": "अग.",
        "sep": "सित.",
-       "oct": "à¤\85à¤\95à¥\8dà¤\9fà¥\82.",
+       "oct": "à¤\85à¤\95à¥\8dà¤\9fà¥\81.",
        "nov": "नव.",
        "dec": "दिस.",
        "january-date": "जनवरी $1",
        "october-date": "अक्टूबर $1",
        "november-date": "नवम्बर $1",
        "december-date": "दिसम्बर $1",
-       "pagecategories": "{{PLURAL:$1|à¤\96ाढà¥\80|à¤\95à¤\8fà¤\9fा à¤\96ाढी}}",
+       "pagecategories": "{{PLURAL:$1|शà¥\8dरà¥\87णà¥\80|à¤\95à¤\8fà¤\9fा à¤¶à¥\8dरà¥\87णी}}",
        "category_header": "संवर्ग \"$1\" मे पन्ना सभ",
        "subcategories": "उपसंवर्ग",
        "category-media-header": "संवर्ग \"$1\" मे मीडिया",
        "category-empty": "''ऐ संवर्गमे अखन कोनो पन्ना वा मीडिया नै अछि।''",
-       "hidden-categories": "{{PLURAL:$1|नुकाएल वर्ग|नुकाएल वर्ग }}",
+       "hidden-categories": "{{PLURAL:$1|नुकाएल वर्ग|नुकाएल वर्गसभ}}",
        "hidden-category-category": "नुकाएल संवर्ग सभ",
        "category-subcat-count": "{{PLURAL:$2| ऐ संवर्गक खाली ई सभ उप संवर्ग अछिइ।.|ऐ संवर्गमे ई सभ {{PLURAL:$1| उपसंवर्ग|$1 उपसंवर्ग सभ}}, ऐमे सँ $2 सभटा।}}",
        "category-subcat-count-limited": "ऐ संवर्गमे अछि {{PLURAL:$1|उपसंवर्ग|$1उपसंवर्ग सभ}}",
        "mypage": "पन्ना",
        "mytalk": "वार्त्ता",
        "anontalk": "ऐ अनिकेत पता लेल विमर्श",
-       "navigation": "सà¤\82चार",
+       "navigation": "सà¤\9eà¥\8dचार",
        "and": "&#32;आर",
        "qbfind": "ताकू",
        "qbbrowse": "गवेषण करू",
        "faq": "त्वरित प्रश्नोत्तरी",
        "faqpage": "Project: त्वरित प्रश्नोत्तरी",
        "actions": "क्रिया सभ",
-       "namespaces": "चेन्हासी समूह सभ",
-       "variants": "प्रकार सभ",
+       "namespaces": "चेन्हासी समूहसभ",
+       "variants": "प्रकारसभ",
        "navigation-heading": "दिक्चालन सूची",
        "errorpagetitle": "गलती",
        "returnto": "$1 पर आउ।",
-       "tagline": "à¤\95तयसà¤\81 {{SITENAME}}",
+       "tagline": "मà¥\88थिलà¥\80 {{SITENAME}}सà¤\81",
        "help": "मदति",
-       "search": "ताà¤\95à¥\82",
-       "searchbutton": "ताà¤\95à¥\82",
+       "search": "ताà¤\95à¥\80",
+       "searchbutton": "ताà¤\95à¥\80",
        "go": "जाऊ",
-       "searcharticle": "à¤\9cाà¤\8a",
+       "searcharticle": "à¤\9cाà¤\87",
        "history": "पन्नाक इतिहास",
        "history_short": "इतिहास",
        "updatedmarker": "हमर अन्तिम आगमनसँ पहिने अद्यतन कएल",
        "printableversion": "प्रिन्ट करबा योग्य",
-       "permalink": "सà¥\8dथायà¥\80 à¤²à¤¿à¤\82क",
+       "permalink": "सà¥\8dथायà¥\80 à¤²à¤¿à¤\99à¥\8dक",
        "print": "छापू",
-       "view": "दà¥\87à¤\96à¥\82",
+       "view": "दà¥\87à¤\96à¥\80",
        "view-foreign": "$1 पर देखु",
-       "edit": "सà¤\82पादन",
+       "edit": "समà¥\8dपादन",
        "edit-local": "स्थानीय विवरण संपादन",
        "create": "बनाउ",
        "create-local": "स्थानीय विवरण निर्माण",
        "unprotectthispage": "ऐ पन्नाक रक्षा कवच बदलू",
        "newpage": "नवका पन्ना",
        "talkpage": "एहि पृष्ठ पर वार्त्तालाप",
-       "talkpagelinktext": "à¤\95हà¥\82",
+       "talkpagelinktext": "वारà¥\8dता",
        "specialpage": "विशेष पन्ना",
-       "personaltools": "व्यक्तिगत उपकरण",
+       "personaltools": "व्यक्तिगत उपकरणसभ",
        "articlepage": "विषय-सूची पन्ना देखू",
        "talk": "वार्तालाप",
-       "views": "दृष्टि",
-       "toolbox": "उपकरण-बक्सा",
+       "views": "दृष्टिसभ",
+       "toolbox": "उपकरणसभ",
        "userpage": "प्रयोक्ता पन्ना देखू",
        "projectpage": "परियोजना पन्ना देखू",
        "imagepage": "पन्नाक पृष्ठ देखू",
        "redirectedfrom": "(एतयसँ बहटारल $1)",
        "redirectpagesub": "पन्नाकेँ पठाउ",
        "redirectto": "मे पुनर्निर्देश:",
-       "lastmodifiedat": "à¤\88 à¤ªà¤¨à¥\8dना à¤\85à¤\82तिम बेर संवर्धित भेल $1, केँ  $2 बजे।",
+       "lastmodifiedat": "à¤\88 à¤ªà¤¨à¥\8dना à¤\85नà¥\8dतिम बेर संवर्धित भेल $1, केँ  $2 बजे।",
        "viewcount": "ई पन्ना देखल गेल {{PLURAL:$1|एक बेर|$1 एतेक बेर}}",
        "protectedpage": "संरक्षित पन्ना",
        "jumpto": "जाऊ:",
        "jumptonavigation": "हेलू",
-       "jumptosearch": "ताà¤\95à¥\82",
+       "jumptosearch": "ताà¤\95à¥\80",
        "view-pool-error": "दुखी छी, वितरक सभ एखन व्यस्त अछि।\nबड्ड बेशी लोक ऐ पन्नाकेँ देखबामे लागल छथि।\nऐ पन्नाकेँ फेरसँ देखबा लेल कनी बिलमू। \n$1",
        "generic-pool-error": "दुखी छी, वितरक सभ एखन व्यस्त अछि।\nबड्ड बेशी लोक ऐ पन्नाकेँ देखबामे लागल छथि।\nऐ पन्नाकेँ फेरसँ देखबा लेल कनी बिलमू। \n$1",
        "pool-timeout": "प्रतीक्षा निगृहीत कालावसान",
        "aboutpage": "Project:विवरण",
        "copyright": "$1क अंतर्गत विषय सूची उपलब्ध अछि",
        "copyrightpage": "{{ns:project}}:सर्वाधिकार",
-       "currentevents": "आइ-काल्हिक घटना सभ",
-       "currentevents-url": "Project: आइ-काल्हिक घटना सभ",
+       "currentevents": "आइ-काल्हिक घटनासभ",
+       "currentevents-url": "Project:आइ-काल्हिक घटनासभ",
        "disclaimers": "अनाधिकार घोषणा",
        "disclaimerpage": "Project:अनाधिकार घोषणा",
        "edithelp": "संपादन सहयोग",
        "newmessageslinkplural": "{{PLURAL:$1|एगो नयाँ पत्र|999=नयाँ पत्र}}",
        "newmessagesdifflinkplural": "अन्तिम {{PLURAL:$1|परिवर्त्तन|999=परिवर्त्तन सभ}}",
        "youhavenewmessagesmulti": "$1 पर अहाँ लेल नव पत्र अछि",
-       "editsection": "समà¥\8dपादन à¤\95रà¥\82",
+       "editsection": "समà¥\8dपादन à¤\95रà¥\80",
        "editold": "सम्पादित करू",
        "viewsourceold": "जड़ि देखू",
        "editlink": "सम्पादन करू",
-       "viewsourcelink": "à¤\9cड़ि à¤¦à¥\87à¤\96à¥\82",
+       "viewsourcelink": "à¤\9cडà¥\80 à¤¦à¥\87à¤\96à¥\80",
        "editsectionhint": "सम्पादन शाखा: $1",
-       "toc": "विषय-सूची",
+       "toc": "विषय सूचीसभ",
        "showtoc": "देखाऊ",
        "hidetoc": "नुकाऊ",
        "collapsible-collapse": "भखड़ाउ",
        "nstab-media": "मिडिया पृष्ठ",
        "nstab-special": "विशेष पृष्ठ",
        "nstab-project": "परियोजना पृष्ठ",
-       "nstab-image": "सà¤\82चिका",
+       "nstab-image": "सà¤\9eà¥\8dचिका",
        "nstab-mediawiki": "पत्र",
        "nstab-template": "आकृति",
        "nstab-help": "सहायता पृष्ठ",
        "nstab-category": "संवर्ग",
+       "mainpage-nstab": "सम्मुख पन्ना",
        "nosuchaction": "एहेन कोनो क्रिया नै अछि",
        "nosuchactiontext": "ऐ सार्वत्रिक विभव संकेत द्वारा निर्दिष्ट क्रिया अमान्य अछि।\nअहाँ सार्वत्रिक विभव संकेतक गलत टंकण केने हएब, वा कोनो गलत लिंकक पाछाँ गेल हएब।\nई {{अन्तर्जाल}} प्रयोक्ता द्वारा प्रयुक्त तंत्रांशमे स्थित कोनो दोषक संकेत सेहो कऽ सकैए।",
        "nosuchspecialpage": "एहेन कोनो विशेष पृष्ठ नै अछि",
        "createaccountreason": "कारण:",
        "createacct-reason": "कारण:",
        "createacct-reason-ph": "अहा इगो आर दोसर खाता कियाक बनउने जा रहल छि",
-       "createacct-captcha": "सुरक्षा जाँच",
-       "createacct-imgcaptcha-ph": "उपरोक्त पाठ लिखु",
        "createacct-submit": "अपन खाता बनाउ",
        "createacct-another-submit": "दोसर खाता बनाउ",
        "createacct-benefit-heading": "{{SITENAME}} अहि जोका लोकनिसभ द्वारा बनावल गेल अछि।",
        "passwordreset-emailtext-ip": "कियो (सम्भवतः अहाँ, अन्तर्जाल सेवा कल्पक $1 सँ) अपन लेखा विवरणक पुनःस्मरणक लेल अनुरोध केलहुँ ऐ लेल {{ अन्तर्जालक नाम}} ($4). ई प्रयोक्ता {{PLURAL:$3|लेखा अछि| लेखा सभ अछि}}\nऐ ई-पत्र संकेतसँ सम्बन्धित:\n\n$2\n\n{{PLURAL:$3|ई अल्पकालक कूटशब्द| ई सभ अल्पकालक कूटशब्द}} खतम भऽ जाएत {{PLURAL:$5|एक दिन|$5 पाँच दिन}}.\nअहाँ सम्प्रवेश करू आ एकटा नव कूटशब्द चुनू।. जौं कियो आन ई आग्रह केने अछि, वा अहाँकेँ अपन पुरान कूटशब्द मोन पड़ि गेल अछि , आ आब एकरा बदलबाक इच्छा नै राखै छी तँ अहाँ ऐ संदेशकेँ बिसरि जाउ आ अपन पुरान कूटशब्दक प्रयोग करैत रहू।",
        "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-emailsent": "एकटा ई-पत्र मोन पाड़बा लेल पठाओल गेल अछि।",
+       "passwordreset-emailsentemail": "एकटा ई-पत्र मोन पाड़बा लेल पठाओल गेल अछि।",
        "passwordreset-emailsent-capture": "एकटा स्मरण ई-पत्र पठाएल गेल अछि, जे नीचाँ देखाएल अछि।",
        "passwordreset-emailerror-capture": "एकटा स्मरण ई-पत्र बनाएल गेल अछि, जे नीचाँ देखाएल अछि, मुदा प्र्योक्ताकेँ एकरा पठेबाक प्रयास विफल भेल: $1",
        "changeemail": "ई-पत्र संकेत बदलू",
-       "changeemail-text": "अपन ई-पत्र संकेत बदलबा लेल ऐ आवेदनकेँ भरू। अहाँकेँ ऐ परिवर्तनक अनुमोदन लेल अपन कूटशब्द भरए पड़त।",
+       "changeemail-header": "ई-पत्र पता खाता बदलू",
        "changeemail-no-info": "अहाँकेँ ऐ पन्नाकेँ सोझे देखबाले सम्प्रवेशित हुअए पड़त।",
        "changeemail-oldemail": "अखुनका ई-पत्र संकेत:",
        "changeemail-newemail": "नव ई-पत्र संकेत:",
        "nohistory": "ऐ पन्ना लेल कोनो सम्पादन इतिहास नै अछि।",
        "currentrev": "नूतन संशोधन",
        "currentrev-asof": "$1 क समकालिक तखुनका संशोधन",
-       "revisionasof": "à¤\85à¤\82तिम परिवर्त्तन  $1",
+       "revisionasof": "à¤\85नà¥\8dतिम परिवर्त्तन  $1",
        "revision-info": "$2 द्वारा कएल संशोधन अछि $1",
-       "previousrevision": "â\86\90पà¥\81रान à¤ªà¤°à¤¿à¤µà¤°à¥\8dतà¥\8dतन",
+       "previousrevision": "←पुरान परिवर्तन",
        "nextrevision": "नूतन संशोधन →",
        "currentrevisionlink": "नूतन संशोधन",
        "cur": "हीन",
        "lineno": "पंक्त्ति $1:",
        "compareselectedversions": "चयन कएल संशोधन सभक तुलना करू",
        "showhideselectedversions": "देखाउ/ नुकाउ चयनित संशोधन सभ",
-       "editundo": "à¤\85सà¤\82पादन",
+       "editundo": "à¤\85समà¥\8dपादन",
        "diff-empty": "(कोनो अंतर नै)",
        "diff-multi-sameuser": "(इ प्रयोक्ताद्वारा {{PLURAL:$1|कएल गेल बीचके एक अवतरण नै देखाओल गेल |कएल गेल बीचके $1 अवतरण नै देखाओल गेल}})",
        "diff-multi-otherusers": "({{PLURAL:$1|एकटा मध्यस्थ संशोधन|$1 मध्यस्थ संशोधन सभ}} $2 सँ बेसी {{PLURAL:$2|प्रयोक्ता|प्रयोक्ता सभ}} नै देखाएल)",
        "next-page": "अगला पृष्ठ",
        "prevn-title": "पहिलुका $1 {{PLURAL:$1|परिणाम|परिणाम सभ}}",
        "nextn-title": "आगाँ $1 {{PLURAL:$1|परिणाम|परिणाम सभ}}",
-       "shown-title": "प्रति पन्ना $1 {{PLURAL:$1|परिणाम|परिणाम सभ}} देखाउ",
+       "shown-title": "प्रति पन्ना $1 {{PLURAL:$1|परिणाम|परिणामसभ}} देखाउ",
        "viewprevnext": "देखू  ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''ऐ विकीपर एकटा पन्ना अछि \"[[:$1]]\" नामसँ।'''",
        "searchmenu-new": "''' पन्ना निर्माण \"[[:$1]]\" ऐ विकीपर !'''",
        "searchprofile-everything": "सभटा",
        "searchprofile-advanced": "विशिष्ट",
        "searchprofile-articles-tooltip": "$1 मे ताकू",
-       "searchprofile-images-tooltip": "सà¤\82à¤\9aिà¤\95ा à¤¸à¤­ à¤²à¥\87ल à¤¤à¤¾à¤\95à¥\82",
+       "searchprofile-images-tooltip": "सà¤\9eà¥\8dà¤\9aिà¤\95ासभ à¤²à¥\87ल à¤¤à¤¾à¤\95à¥\80",
        "searchprofile-everything-tooltip": "सभटा सामिग्रीमे ताकू (चर्चा पन्ना सहित)",
        "searchprofile-advanced-tooltip": "बनाएल नामस्थान सभमे ताकू",
-       "search-result-size": "$1 ({{PLURAL:$2|1 शब्द|$2 शब्द सभ}})",
+       "search-result-size": "$1 ({{PLURAL:$2|1 शब्द|$2 शब्दसभ}})",
        "search-result-category-size": "{{PLURAL:$1|1 सदस्य|$1 सदस्य}} ({{PLURAL:$2|1 उपसंवर्ग|$2 उपसंवर्ग}}, {{PLURAL:$3|1 संचिका|$3 संचिका}})",
        "search-redirect": "(रस्ता बदलेन $1)",
        "search-section": "(शाखा $1)",
        "prefs-diffs": "अन्तर निर्धारक सभ",
        "prefs-help-prefershttps": "इ प्राथमिकता अहाँके फेर स सम्प्रवेश करलाक बाद प्रभाव पडत।",
        "prefs-tabs-navigation-hint": "सुझाव: अहाँ टैब्स सूचीमे टैब्सके बीच आवागमन करवाक लेल बाम आर दाहिना बागलके कुंजिसभके उपयोग कइर सकैत छी।",
-       "email-address-validity-valid": "ई-पत्र संकेत मान्य बुझाइत अछि",
-       "email-address-validity-invalid": "एकटा मान्य ई-पत्र संकेत लिखू",
        "userrights": "प्रयोक्ता अधिकारक प्रबन्धन",
        "userrights-lookup-user": "प्रयोक्ता संवर्ग सभक प्रबन्ध करू",
        "userrights-user-editname": "एकटा प्रयोक्तानाम लिखू:",
        "right-blockemail": "प्रयोक्ताकेँ ई-पत्र पठेबासँ रोकू",
        "right-hideuser": "एकटा प्रयोक्तानामकेँ प्रतिबन्धित करू, लोकसँ एकरा नुका कऽ",
        "right-ipblock-exempt": "अनिकेत प्रतिबन्ध, स्वचालित प्रतिबन्ध आ परिक्षेत्र प्रतिबन्धकेँ नै मानू",
-       "right-proxyunbannable": "दोसराइतक स्वचालित प्रतिबन्धकेँ नै मानू",
        "right-unblockself": "स्वयंकेँ प्रतिबन्धसँ हटाउ",
        "right-protect": "सुरक्षा स्तर बदलू आ संरक्षित पन्ना सम्पादित करू",
        "right-editprotected": "संरक्षित पन्ना सम्पादित करू (बिना तराउपड़ी सुरक्षाक)",
        "nchanges": "$1 {{PLURAL:$1|परिवर्त्तन|परिवर्त्तन}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|अंतिम बेर देखला के बाद स}}",
        "enhancedrc-history": "इतिहास",
-       "recentchanges": "लगक परिवर्तन सभ",
+       "recentchanges": "लगक परिवर्तनसभ",
        "recentchanges-legend": "नव परिवर्तन सभक विकल्प सभ",
        "recentchanges-summary": "ई पन्नापर विकीमे भेल सभसँ अद्यतन परिवर्तनपर नजरि राखू।",
        "recentchanges-noresult": "इ अवधिके दौरान इ मापदंडके पूर्ण करेत समय कोनो परिवर्तन नै केएल गेल अछि।",
        "rcshowhidemine": "$1 हमर सम्पादन सभ",
        "rcshowhidemine-show": "देखाउ",
        "rcshowhidemine-hide": "नुकाऊ",
+       "rcshowhidecategorization-show": "देखाऊ",
+       "rcshowhidecategorization-hide": "नुकाऊ",
        "rclinks": "देखाऊ अंतिम $1 परिवर्त्तन अंतिम $2 दिनमे<br />$3",
-       "diff": "à¤\85à¤\82तर",
+       "diff": "à¤\85नà¥\8dतर",
        "hist": "इति.",
        "hide": "नुकाऊ",
        "show": "देखाउ",
        "rc_categories": "संवर्ग सीमित (\"|\" सँ हटाउ)",
        "rc_categories_any": "कोनो",
        "rc-change-size": "$1",
-       "rc-change-size-new": "बदललाक बाद $1 {{PLURAL:$1|बाइट}}",
+       "rc-change-size-new": "बदललाक बाद $1 {{PLURAL:$1|बाइट}}",
        "newsectionsummary": "/* $1 */ नव संवर्ग",
        "rc-enhanced-expand": "वर्णन देखाउ (जावास्क्रिप्ट चाही)",
        "rc-enhanced-hide": "वर्णन नुकाउ",
        "rc-old-title": "मूल रूप स \"$1\" नाम स बनाएल गेल रह",
        "recentchangeslinked": "संबंधित परिवर्त्तन",
        "recentchangeslinked-feed": "संबंधित परिवर्त्तन",
-       "recentchangeslinked-toolbox": "सà¤\82बà¤\82धित à¤ªà¤°à¤¿à¤µà¤°à¥\8dत्तन",
+       "recentchangeslinked-toolbox": "समà¥\8dबनà¥\8dधित à¤ªà¤°à¤¿à¤µà¤°्तन",
        "recentchangeslinked-title": "\"$1\" मे भेल परिवर्तन",
        "recentchangeslinked-summary": "ई विशेष पन्नासँ सम्बद्ध पन्ना सभमे (आकि कोनो विशेष वर्गक समूहमे) भेल परिवर्तनक सूची छी ।\n[[Special:Watchlist|your watchlist]]  पर पन्नासभ '''गाढ़''' अछि।",
        "recentchangeslinked-page": "पन्नाक नाम",
        "upload-too-many-redirects": "ई सार्वत्रिक विभव संकेत बड्ड बेसी घुमौआ लागिक संग अछि।",
        "upload-http-error": "परिसंविद भ्रम आएल:$1",
        "upload-copy-upload-invalid-domain": "कपि अपलोड इ डोमेन स उपलब्ध नै अछि।",
+       "upload-dialog-title": "फाइल अपलोड करी",
+       "upload-dialog-button-cancel": "रद्द करी",
        "backend-fail-stream": "\"$1\" केँ नै स्ट्रिम क सकल।",
        "backend-fail-backup": "\"$1\" केँ नै ब्याकअप क सकल।",
        "backend-fail-notexists": "फाइल $1 नै अछि।",
        "listfiles-latestversion": "बर्तमान भर्जन",
        "listfiles-latestversion-yes": "हँ",
        "listfiles-latestversion-no": "नै",
-       "file-anchor-link": "सà¤\82चिका",
+       "file-anchor-link": "सà¤\9eà¥\8dचिका",
        "filehist": "फाइल इतिहास",
-       "filehist-help": "तà¤\96à¥\81नà¤\95ा à¤¤à¤¿à¤¥à¤¿/ à¤¸à¤®à¤\8f à¤ªà¤° à¤\95à¥\8dलिà¤\95 à¤\95रà¥\82 जखुनका फाइल देखबाक अछि",
+       "filehist-help": "तà¤\96à¥\81नà¤\95ा à¤¤à¤¿à¤¥à¤¿/ à¤¸à¤®à¤\8f à¤ªà¤° à¤\95à¥\8dलिà¤\95 à¤\95रà¥\80 जखुनका फाइल देखबाक अछि",
        "filehist-deleteall": "सभटाकेँ मेटाउ",
        "filehist-deleteone": "मेटाउ",
        "filehist-revert": "फेरसँ वएह",
        "filehist-thumbtext": "तखुनका लघुचित्र $1",
        "filehist-nothumb": "कोनो लघुचित्र नै",
        "filehist-user": "प्रयोक्ता",
-       "filehist-dimensions": "बीम",
+       "filehist-dimensions": "बीमसभ",
        "filehist-filesize": "संचिका आकार",
        "filehist-comment": "समीक्षा",
        "imagelinks": "फाइलक उपयोग",
        "duplicatesoffile": "ऐ संचिकाक {{PLURAL:$1|file is a duplicate|$1 संचिका सभ द्वितीयक अछि}} अछि ([[Special:FileDuplicateSearch/$2|आर वर्णन]]):",
        "sharedupload": "ई फाइल $1 सँ अछि आ दोसर प्रकल्प लेल प्रयोग कएल जा सकैए।",
        "sharedupload-desc-there": "ई संचिका $1 सँ अछि आ दोसर परियोजना लेल प्रयोग कएल जा सकैए।\nकृपा कऽ देखू [$2 संचिका वर्णन पन्ना] विशेष सूचना लेल।",
-       "sharedupload-desc-here": "à¤\88 à¤¸à¤\82à¤\9aिà¤\95ा $1 à¤¸à¤\81 à¤\85à¤\9bि à¤\86 à¤\88 à¤¦à¥\8bसर à¤ªà¤°à¤¿à¤¯à¥\8bà¤\9cना à¤¦à¥\8dवारा à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95à¤\8fल à¤\9cा à¤¸à¤\95à¥\88à¤\8f।\nà¤\90 à¤ªà¤° à¤µà¤¿à¤µà¤°à¤£ [$2 à¤¸à¤\82चिका विवरण पन्ना] ओइपर नीचाँ देखाएल अछि।",
+       "sharedupload-desc-here": "à¤\88 à¤¸à¤\9eà¥\8dà¤\9aिà¤\95ा $1सà¤\81 à¤\85à¤\9bि à¤\86 à¤\88 à¤¦à¥\8bसर à¤ªà¤°à¤¿à¤¯à¥\8bà¤\9cनादà¥\8dवारा à¤ªà¥\8dरयà¥\8bà¤\97 à¤\95à¤\8fल à¤\9cा à¤¸à¤\95à¥\88à¤\8f।\nà¤\8fतà¤\8f à¤°à¤¹à¤² à¤µà¤¿à¤µà¤°à¤£ [$2 à¤¸à¤\9eà¥\8dचिका विवरण पन्ना] ओइपर नीचाँ देखाएल अछि।",
        "sharedupload-desc-edit": "ई फ़ाइल $1 से छी आर अन्य परियोजना द्वारा सेहो प्रयोग भ्या रहल अछि\nशायद आहाँ [$2 पे एकर फ़ाइल विवरण पन्ना] के सम्पादन करइल चाहए छी।",
        "sharedupload-desc-create": "ई फ़ाइल $1 से अछि आर अन्य परियोजनासभ द्वारा से प्रयोग भऽ रहल अछि\nशायद आहाँ [$2 पे एकर फ़ाइल विवरण पन्ना] के सम्पादन करइल चाहए छी ।",
        "filepage-nofile": "ऐ नामक कोनो संचिका उपलब्ध नै अछि।",
        "listusers-desc": "अवरोही क्रम में क्रमबद्ध करी",
        "usereditcount": "$1 {{PLURAL:$1|सम्पादन|सम्पादन सभ}}",
        "usercreated": "{{GENDER:$3| बनैलक|बनैलकिन्ह}} $1 तिथि पर $2 काले",
-       "newpages": "नव पन्ना सभ",
+       "newpages": "नव पन्नासभ",
        "newpages-username": "प्रयोक्तानाम:",
        "ancientpages": "सभसँ पुरान पन्ना सभ",
        "move": "हटाउ",
        "allpagesto": "एतऽ खतम होमएबला पन्नाक प्रदर्शन करू:",
        "allarticles": "सभटा लेख",
        "allinnamespace": "सभटा पन्ना ($1 नामगाम)",
-       "allpagessubmit": "à¤\9cाà¤\89",
+       "allpagessubmit": "à¤\9cाà¤\8f",
        "allpagesprefix": "उपसर्गक संग दृश्य पन्ना सभ:",
        "allpagesbadtitle": "देल पन्नाक शीर्षक गलत, गलत सम्बन्धित अन्तर-भाषा अन्तर विकी शीर्षक छी। ई एक वा बेशी कलाकार युक्त भऽ सकैए जे शीर्षकमे प्रयुक्त नै कएल जा सकैए।",
        "allpages-bad-ns": "{{जालस्थल}} मे \"$1\" नामगाम नै अछि।",
        "wlheader-showupdated": "पन्ना सभ जे अहाँक एतए अन्तिम बेर अएलाक बाद बदलल अछि तकर सूची देल अछि '''गाढ़''' मे",
        "wlnote": "नीचाँ {{PLURAL:$1|is the last change|are the last '''$1''' changes}} अन्तिम {{PLURAL:$2|hour|'''$2''' hours}} $3, $4 जेना।",
        "wlshowlast": "देखाउ अन्तिम $1 घण्टा $2 दिन",
+       "watchlistall2": "सभ",
        "watchlist-options": "साकांक्षसूचीक विकल्प सभ",
        "watching": "ताकिमे...",
        "unwatching": "छोड़ल ...",
        "undelete-error-long": "संचिका अनबामे भ्रम संकेत भेटल:\n$1",
        "undelete-show-file-confirm": "की अहाँ आश्वस्त छी जे अहाँ संचिकाक मेटाएल संशोधन देखऽ चाहै छी \"<nowiki>$1</nowiki>\" सँ $2 पर $3?",
        "undelete-show-file-submit": "हँ",
-       "namespace": "चेन्हासी समूह",
+       "namespace": "चेन्हासी समूह:",
        "invert": "उनटा चयन",
        "tooltip-invert": "ऐ बक्साकेँ सही करू पन्ना परिवर्तनकेँ नुकेबा लेल चयनित नामस्थानक भीतर (आ संग लागल नामस्थान जँ सही कएल अछि तखन)",
        "namespace_association": "सम्बद्ध चेन्हासी",
        "sp-contributions-toponly": "मात्र ओइ सम्पादनकेँ देखाउ जे अद्यतन संशोधन छी।",
        "sp-contributions-newonly": "मात्र ओइ सम्पादन देखाउ जे पृष्ठ निर्मित भेल अछि",
        "sp-contributions-submit": "ताकू",
-       "whatlinkshere": "à¤\8fतय à¤\95à¥\8bन à¤²à¤¿à¤\82क अछि",
+       "whatlinkshere": "à¤\8fतय à¤\95à¥\8bन à¤²à¤¿à¤\99à¥\8dक अछि",
        "whatlinkshere-title": "\"$1\" सँ सम्बन्धित पन्ना सभ",
        "whatlinkshere-page": "पन्ना:",
        "linkshere": "ई सभ पन्ना सम्बन्धित अछि '''[[:$1]]''':",
        "movenosubpage": "अहि पन्ना कऽ कोनो उप पन्ना नहि अछि।",
        "movereason": "कारण:",
        "revertmove": "फेरसँ वएह",
-       "delete_and_move": "मेटाउ आ हटू",
        "delete_and_move_text": "==हटाबैक जरूरत==\nलक्ष्य पृष्ठ \"[[:$1]]\" पहिने सें अस्तित्व में अछि. \nनाम के बदलहि ले की अहां एकरा हटाबय चाहैत छी ?",
        "delete_and_move_confirm": "हँ, पन्ना मेटाउ",
        "delete_and_move_reason": "\"[[$1]]\" सँ घसकेबा लेल जगह बनेबा लेल मेटाएल गेल",
        "allmessages-language": "भाषा:",
        "allmessages-filter-submit": "चलू",
        "allmessages-filter-translate": "अनुवाद करु",
-       "thumbnail-more": "पà¥\88à¤\98",
+       "thumbnail-more": "पà¥\88à¤\97",
        "filemissing": "संचिका हेराएल",
        "thumbnail_error": "लघुचित्र निर्माण कालमे भ्रम:$1",
        "thumbnail_error_remote": "$1 सँ त्रुटि सन्देश: $2",
        "tooltip-pt-preferences": "हमर मोनपसंद",
        "tooltip-pt-watchlist": "पन्ना सभ जकर परिवर्त्तन पर अहाँक नजरि अछि",
        "tooltip-pt-mycontris": "अहाँक योगदानक सूची",
-       "tooltip-pt-login": "अहाँ कें खाता खोलक लेल प्रोत्साहित कल जाएत अछि; मुदा इ अनिवार्य नै छै",
+       "tooltip-pt-login": "अहाँक खाता खोलक लेल प्रोत्साहित केल जाएत अछि; मुदा ई अनिवार्य नै अछि",
        "tooltip-pt-logout": "फेर आयब",
-       "tooltip-pt-createaccount": "अहाँ कें खाता खोलक लेल प्रोत्साहित कल जाएत अछि; मुदा इ अनिवार्य नै छै",
-       "tooltip-ca-talk": "विषयसà¥\82à¤\9aà¥\80à¤\95 à¤ªà¤¨à¥\8dनाà¤\95 à¤¸à¤\82बà¤\82धमे वर्त्तालाप",
-       "tooltip-ca-edit": "à¤\85हाà¤\81 à¤\8fहि à¤ªà¤¨à¥\8dनाà¤\95à¥\87à¤\81 à¤¸à¤\82पादित à¤\95à¤\8f à¤¸à¤\95à¥\88त à¤\9bà¥\80। à¤\95à¥\83पया à¤¸à¥\81रà¤\95à¥\8dषित à¤\95रबासà¤\81 à¤ªà¤¹à¤¿à¤¨à¥\87 à¤ªà¥\82रà¥\8dवपà¥\8dरदरà¥\8dशन à¤¬à¤\9fम à¤\89पयà¥\8bà¤\97 à¤\95रà¥\82।",
+       "tooltip-pt-createaccount": "अहाँक खाता खोलक लेल प्रोत्साहित केल जाएत अछि; मुदा ई अनिवार्य नै छै",
+       "tooltip-ca-talk": "विषयसà¥\82à¤\9aà¥\80à¤\95 à¤ªà¤¨à¥\8dनाà¤\95 à¤¸à¤®à¥\8dबनà¥\8dधमे वर्त्तालाप",
+       "tooltip-ca-edit": "à¤\88 à¤ªà¤¨à¥\8dनाà¤\95 à¤¸à¤®à¥\8dपादित à¤\95रà¥\80",
        "tooltip-ca-addsection": "नव खण्ड शुरू करू",
        "tooltip-ca-viewsource": "ऐ पन्नापर वरदहस्त छै।\nअहाँ एकर जड़ि देख सकै छी।",
-       "tooltip-ca-history": "à¤\90 à¤ªà¥\83षà¥\8dठà¤\95 à¤ªà¤¹à¤¿à¤²à¥\81à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन सभ",
+       "tooltip-ca-history": "à¤\88 à¤ªà¥\83षà¥\8dठà¤\95 à¤ªà¤¹à¤¿à¤²à¥\81à¤\95ा à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनसभ",
        "tooltip-ca-protect": "ऐ पन्नाकेँ बचाउ",
        "tooltip-ca-unprotect": "ऐ पन्नाक रक्षा कवच बदलू",
        "tooltip-ca-delete": "ऐ पन्नाकेँ मेटाउ",
        "tooltip-ca-undelete": "ई पन्ना मेटेबासँ पहिने भेल सम्पादन वापस करू",
        "tooltip-ca-move": "ऐ पृष्ठकेँ हटाउ",
-       "tooltip-ca-watch": "à¤\86à¤\87 à¤ªà¤¨à¥\8dनाà¤\95à¥\87à¤\81 à¤\85पन à¤¸à¤¾à¤\95ाà¤\82à¤\95à¥\8dषसà¥\82à¤\9aà¥\80मà¥\87 à¤°à¤¾à¤\96à¥\82",
+       "tooltip-ca-watch": "à¤\88 à¤ªà¤¨à¥\8dनाà¤\95à¥\87à¤\81 à¤\85पन à¤¸à¤¾à¤\95ाà¤\82à¤\95à¥\8dषसà¥\82à¤\9aà¥\80मà¥\87 à¤°à¤¾à¤\96à¥\80",
        "tooltip-ca-unwatch": "ऐ पन्नाकेँ हमर साकांक्ष सूचीसँ हटाउ",
-       "tooltip-search": "ताà¤\95à¥\82 {{SITENAME}}",
-       "tooltip-search-go": "पà¥\83षà¥\8dठपर à¤\9cाà¤\8a जौं एनमेन पृष्ठ रहए",
-       "tooltip-search-fulltext": "à¤\90 à¤\9cानà¤\95ारà¥\80लà¥\87 à¤¤à¤¾à¤\95à¥\82 à¤ªà¥\83षà¥\8dठ à¤¸à¤­à¤®à¥\87 à¤¤à¤¾à¤\95à¥\82",
-       "tooltip-p-logo": "समà¥\8dमà¥\81à¤\96 à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\82",
-       "tooltip-n-mainpage": "मुख्य-पृष्ठ केँ देखू",
-       "tooltip-n-mainpage-description": "मुख्या पन्नापर जाउ",
-       "tooltip-n-portal": "पà¥\8dरà¥\8bà¤\9cà¥\87à¤\95à¥\8dà¤\9fà¤\95 à¤µà¤¿à¤·à¤¯à¤®à¥\87,à¤\85हाà¤\81 à¤\95à¥\80 à¤\95à¤\8f à¤¸à¤\95à¥\88त à¤\9bà¥\80,वस्तु प्राप्ति स्थल",
-       "tooltip-n-currentevents": "लà¤\97à¤\95 à¤\98à¤\9fनाà¤\95 à¤µà¤¿à¤·à¤¯à¤®à¥\87 à¤\86धार à¤¸à¥\82à¤\9aना à¤ªà¥\8dरापà¥\8dत à¤\95रà¥\82।",
-       "tooltip-n-recentchanges": "विà¤\95à¥\80मà¥\87 à¤²à¤\97à¤\95 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतà¥\8dतनà¤\95 à¤¸à¥\82à¤\9aà¥\80.",
-       "tooltip-n-randompage": "à¤\95à¥\8bनà¥\8b à¤\85निरà¥\8dधारित à¤ªà¤¨à¥\8dना à¤²à¥\8bड à¤\95रà¥\82",
+       "tooltip-search": "ताà¤\95à¥\80 {{SITENAME}}",
+       "tooltip-search-go": "पà¥\83षà¥\8dठपर à¤ªà¤¹à¥\81à¤\81à¤\9aà¥\80 जौं एनमेन पृष्ठ रहए",
+       "tooltip-search-fulltext": "à¤\88 à¤ªà¤¾à¤ à¤\95 à¤²à¥\87ल à¤ªà¥\83षà¥\8dठसभमà¥\87 à¤¤à¤¾à¤\95à¥\80",
+       "tooltip-p-logo": "समà¥\8dमà¥\81à¤\96 à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\80",
+       "tooltip-n-mainpage": "मुख्य पृष्ठकेँ देखी",
+       "tooltip-n-mainpage-description": "मुख्य पन्नापर जाए",
+       "tooltip-n-portal": "परियà¥\8bà¤\9cनाà¤\95 à¤µà¤¿à¤·à¤¯à¤®à¥\87,à¤\85हाà¤\81 à¤\95à¥\80 à¤\95à¤\8f à¤¸à¤\95à¥\88त à¤\9bà¥\80वस्तु प्राप्ति स्थल",
+       "tooltip-n-currentevents": "लà¤\97à¤\95 à¤\98à¤\9fनाà¤\95 à¤µà¤¿à¤·à¤¯à¤®à¥\87 à¤\86धार à¤¸à¥\82à¤\9aना à¤ªà¥\8dरापà¥\8dत à¤\95रà¥\80।",
+       "tooltip-n-recentchanges": "विà¤\95िमà¥\87 à¤²à¤\97à¤\95 à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनà¤\95 à¤¸à¥\82à¤\9aà¥\80",
+       "tooltip-n-randompage": "à¤\95à¥\8bनà¥\8b à¤\85निरà¥\8dधारित à¤ªà¤¨à¥\8dना à¤²à¥\8bड à¤\95रà¥\80",
        "tooltip-n-help": "पता लगावए वाला स्थान",
-       "tooltip-t-whatlinkshere": "सभ à¤µà¤¿à¤\95à¥\80-पनà¥\8dनाà¤\95 à¤¸à¥\82à¤\9aà¥\80 à¤\9cà¤\95र à¤\8fतय à¤²à¤¿à¤\82क अछि",
-       "tooltip-t-recentchangeslinked": "à¤\90 à¤ªà¥\83षà¥\8dठà¤\95 à¤²à¤¾à¤\97िà¤\95 à¤ªà¤¨à¥\8dनामà¥\87 à¤­à¥\87ल à¤¨à¤µ à¤ªà¤°à¤¿à¤µà¤°à¥\8dतन",
+       "tooltip-t-whatlinkshere": "सभ à¤µà¤¿à¤\95à¥\80-पनà¥\8dनाà¤\95 à¤¸à¥\82à¤\9aà¥\80 à¤\9cà¤\95र à¤\8fतय à¤²à¤¿à¤\99à¥\8dक अछि",
+       "tooltip-t-recentchangeslinked": "à¤\88 à¤ªà¥\83षà¥\8dठà¤\95 à¤²à¤\97à¤\95 à¤ªà¤¨à¥\8dनामà¥\87 à¤­à¥\87ल à¤¨à¤µ à¤ªà¤°à¤¿à¤µà¤°à¥\8dतनसभ",
        "tooltip-feed-rss": "ऐ पन्ना लेल आर.एस.एस. सूचना",
        "tooltip-feed-atom": "ऐ पन्ना लेल अणु समदिया",
-       "tooltip-t-contributions": "à¤\90 à¤ªà¥\8dरयà¥\8bà¤\95à¥\8dताà¤\95 à¤¯à¥\8bà¤\97दानà¤\95 à¤¸à¥\82à¤\9aà¥\80 à¤¦à¥\87à¤\96à¥\82",
+       "tooltip-t-contributions": "à¤\88 {{GENDER:$1|पà¥\8dरयà¥\8bà¤\95à¥\8dताà¤\95}} à¤¯à¥\8bà¤\97दानà¤\95 à¤¸à¥\82à¤\9aà¥\80 à¤¦à¥\87à¤\96à¥\80",
        "tooltip-t-emailuser": "ऐ प्रयोक्ताकेँ ई-पत्र पठाउ",
        "tooltip-t-info": "ई पृष्ठ के सम्बन्धमें आर बैंसी जानकारी",
        "tooltip-t-upload": "चित्र आकि मीडिया फाइलकेँ अपलोड करी",
        "tooltip-t-specialpages": "सभटा विशेष पन्नाक सूची",
-       "tooltip-t-print": "à¤\90 पृष्ठक छपैबला रूप",
-       "tooltip-t-permalink": "पनà¥\8dनाà¤\95 à¤\90 à¤¸à¤\82वरà¥\8dधनà¤\95 à¤¸à¥\8dथायà¥\80 à¤²à¤¿à¤\82क",
-       "tooltip-ca-nstab-main": "विषय à¤¸à¥\82à¤\9aà¥\80बला à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\82",
+       "tooltip-t-print": "à¤\88 पृष्ठक छपैबला रूप",
+       "tooltip-t-permalink": "पनà¥\8dनाà¤\95 à¤\90 à¤¸à¤\82वरà¥\8dधनà¤\95 à¤¸à¥\8dथायà¥\80 à¤²à¤¿à¤\99à¥\8dक",
+       "tooltip-ca-nstab-main": "विषय à¤¸à¥\82à¤\9aà¥\80बला à¤ªà¤¨à¥\8dना à¤¦à¥\87à¤\96à¥\80",
        "tooltip-ca-nstab-user": "प्रयोक्ता पन्नाकेँ देखू",
        "tooltip-ca-nstab-media": "मीडिया पृष्ठ देखू",
-       "tooltip-ca-nstab-special": "à¤\88 à¤\8fà¤\95à¤\9fा à¤µà¤¿à¤¶à¤¿à¤·à¥\8dà¤\9f à¤ªà¤¨à¥\8dना à¤\9bà¥\80, à¤\85हाà¤\81 à¤\85हà¥\80 à¤ªà¤¨à¥\8dनाà¤\95à¥\87à¤\81 à¤¸à¤\82पादित नै कऽ सकै छी",
+       "tooltip-ca-nstab-special": "à¤\88 à¤\8fà¤\95à¤\9fा à¤µà¤¿à¤¶à¤¿à¤·à¥\8dà¤\9f à¤ªà¤¨à¥\8dना à¤\9bà¥\80, à¤\86 à¤\85हाà¤\81 à¤\8fà¤\95रा à¤¸à¤®à¥\8dपादित नै कऽ सकै छी",
        "tooltip-ca-nstab-project": "परियोजना पन्ना देखू",
        "tooltip-ca-nstab-image": "पन्नाक पृष्ठ देखू",
        "tooltip-ca-nstab-mediawiki": "प्रणालीक संदेश देखू",
        "tooltip-watchlistedit-raw-submit": "साकांक्षसूची उद्दतन करू",
        "tooltip-recreate": "पन्ना फेरसँ बनाउ तखनो जँ ई मेटा देल गेल हुअए",
        "tooltip-upload": "उपारोपण करू",
-       "tooltip-rollback": "\"प्रत्यावर्तन\" ऐ पन्नाक अन्तिम योगदा करैबलाक सम्पादन (सम्पादन सभ) केँ एक क्लिकमे पुरान जगहपर लऽ जाउ",
+       "tooltip-rollback": "\"प्रत्यावर्तन\" ऐ पन्नाक अन्तिम योगदान करैबलाक सम्पादन (सम्पादनसभ)केँ एक क्लिकमे पुरान जगहपर लऽ जाए",
        "tooltip-undo": "\"फेरसँ वएह\" सम्पादनकेँ पूर्वस्थितिमे लऽ जाइए आ पूर्वावलोकन अवस्थामे सम्पादन फॉर्म खोलैए। ई सारांशमे कारण जोड़बाक विकल्प दैत अछि।",
        "tooltip-preferences-save": "मोनपसंद के सुरक्षित करू",
        "tooltip-summary": "छोट संक्षेप दिअ",
        "spam_reverting": "अन्तिम संशोधन लग घुरल जइमे $1 लागि नै अछि",
        "spam_blanking": "सभटा संशोधन $1 लागिसँ युक्त अि, खतम कऽ रहल छी",
        "spam_deleting": "सभटा संशोधन $1 लागिसँ युक्त अि, खतम कऽ रहल छी",
-       "simpleantispam-label": "à¤\90नà¥\8dà¤\9fà¥\80-सà¥\8dपà¥\88म à¤\9cाà¤\81à¤\9a।\nयà¥\80 à¤®à¤½ <strong>नà¥\88</strong> à¤­à¤°à¥\81!",
+       "simpleantispam-label": "à¤\90नà¥\8dà¤\9fà¥\80-सà¥\8dपà¥\8dयाम à¤\9cाà¤\81à¤\9a।\nà¤\8fà¤\95रा <strong>नà¥\88</strong> à¤­à¤°à¥\80!",
        "pageinfo-title": "\"$1\"पृष्ठक लेल नब गप",
        "pageinfo-not-current": "माफ करु, पुरान संशोधन के लेल ई जानकारी प्रदान करनाए संभव नै अछि ।",
        "pageinfo-header-basic": "न्यूनतम जानकारी",
        "pageinfo-hidden-categories": "नुकाएल {{PLURAL:$1|संवर्ग|संवर्ग सभ}} ($1)",
        "pageinfo-templates": "प्रयुक्त {{PLURAL:$1|आकृति|आकृति सभ}} ($1)",
        "pageinfo-transclusions": "$1 {{PLURAL:$1|पन्ना|पन्ना}} पर ट्रान्सक्ल्युडेड",
-       "pageinfo-toolboxlink": "à¤\90 à¤ªà¤¨à¥\8dना à¤ªà¤° जानकारी",
+       "pageinfo-toolboxlink": "पनà¥\8dना जानकारी",
        "pageinfo-redirectsto": "मे पुनर्निर्देश:",
        "pageinfo-redirectsto-info": "जानकारी",
        "pageinfo-contentpage": "सामग्री पृष्ठ सभ में गिनल जाएत अछि",
        "metadata-help": "ई फाइल अतिरिक्त सूचना दैत अछि, सम्भवतः ई अंकीय कैमरा वा स्कैनर द्वारा बनाएल वा अंकण कए जोड़ल गेल अछि।\nजौं फाइलकेँ मूल रूपसँ परिवर्धित कएल गेल हएत तँ किछु विवरण पूर्ण रूपसँ परिवर्धित फाइलमे नै देखाएल गेल हएत।",
        "metadata-expand": "बढ़ाओल विवरण देखाउ।",
        "metadata-collapse": "विस्तृत विवरण नुकाउ",
-       "metadata-fields": "à¤\9aितà¥\8dर à¤ªà¥\8dरदतà¥\8dताà¤\82श à¤\95à¥\8dषà¥\87तà¥\8dर à¤¸à¤­ à¤\9cà¥\87 à¤\90 à¤¸à¤\82दà¥\87शमà¥\87 à¤¸à¤\82कलित अछि चित्र पन्ना प्रदर्शनमे लेल जाएत जखन प्रदत्तांश सारणी क्षतिग्रस्त हएत।  \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",
+       "metadata-fields": "à¤\9aितà¥\8dर à¤ªà¥\8dरदतà¥\8dताà¤\82श à¤\95à¥\8dषà¥\87तà¥\8dर à¤¸à¤­ à¤\9cà¥\87 à¤\88 à¤¸à¤¨à¥\8dदà¥\87शमà¥\87 à¤¸à¤\99à¥\8dकलित अछि चित्र पन्ना प्रदर्शनमे लेल जाएत जखन प्रदत्तांश सारणी क्षतिग्रस्त हएत।  \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",
        "metadata-langitem": "'''$2:''' $1",
        "metadata-langitem-default": "$1",
        "exif-imagewidth": "चौड़ाई",
        "fileduplicatesearch-result-1": "संचिका \"$1\" लेल कोनो तादात्म्य द्वितीयक नै।",
        "fileduplicatesearch-result-n": "संचिका \"$1\" केँ छै {{PLURAL:$2|1 तादात्म्य द्वितीयक|$2तादात्म्य द्वितीयक}}.",
        "fileduplicatesearch-noresults": "कोनो \"$1\" नाम्ना संचिका नै।",
-       "specialpages": "विशेष पन्ना",
+       "specialpages": "विशेष पन्नासभ",
        "specialpages-note-top": "कुंजी",
        "specialpages-note": "* सामान्य विशिष्ट पन्ना।\n* <span class=\"mw-specialpagerestricted\">प्रतिबंधित विशिष्ट पन्ना।</span>\n* <span class=\"mw-specialpagecached\">उपस्मृतिक विशिष्ट पन्ना (पुरान भऽ सकैए)।</span>",
        "specialpages-group-maintenance": "सुस्थापन प्रतिवेदन",
        "tags": "मान्य परिवर्तन चेन्ह सभ",
        "tag-filter": "[[Special:Tags|Tag]] छन्ना:",
        "tag-filter-submit": "चलनी",
-       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ट्याग}}]]: $2)",
+       "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|ट्याग|ट्यागसभ}}]]: $2)",
        "tags-title": "चेन्ह सभ",
        "tags-intro": "ई पन्ना चेन्ह सभकेँ सूचित करैए जे तंत्रांश सम्पादनसँ चिन्हित करए, आ ओकर अर्थ सेहो।",
        "tags-tag": "चेन्हक नाम",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|चिन्हित}} संशोधन $4 $3 पन्नाक निरीक्षित",
        "logentry-patrol-patrol-auto": "$1 स्वतः {{GENDER:$2|चिन्हित}} संशोधन $4 $3 पन्नाक निरीक्षित",
        "logentry-newusers-newusers": "$1 {{लिंग:$2|बनाएल}} एकटा प्रयोक्ता खाता",
-       "logentry-newusers-create": "$1 {{लिà¤\82ग:$2|बनाएल}} एकटा प्रयोक्ता खाता",
+       "logentry-newusers-create": "$1 {{लिà¤\99à¥\8dग:$2|बनाएल}} एकटा प्रयोक्ता खाता",
        "logentry-newusers-create2": "$1 {{लिंग:$2|बनाएल}} {{लिंग:$4|एकटा प्रयोक्ता खाता}} $3",
        "logentry-newusers-byemail": "$1 द्वारा प्रयोक्ता खाता $3 {{GENDER:$2|बनाओल}} गेल आ कूटशब्द ई-पत्र द्वारा भेजल गेल",
        "logentry-newusers-autocreate": "खाता $1 छल {{लिंग:$2|बनाएल}} स्वतः",
        "feedback-submit": "दिअ",
        "feedback-thanks-title": "धन्यवाद!",
        "feedback-useragent": "सदस्य कर्ता:",
-       "searchsuggest-search": "ताà¤\95à¥\82",
+       "searchsuggest-search": "ताà¤\95à¥\80",
        "searchsuggest-containing": "...सऽ युक्त",
        "api-error-badaccess-groups": "अहि विकी सें अहां कोनो प्रारूप लोड नहि क सकब.",
        "api-error-badtoken": "आन्तरिक त्रुटि: खराब टोकन।",
index d81ccdf..aed8c33 100644 (file)
        "october-date": "$1 октомври",
        "november-date": "$1 ноември",
        "december-date": "$1 декември",
+       "period-am": "претпладне",
+       "period-pm": "попладне",
        "pagecategories": "{{PLURAL:$1|Категорија|Категории}}",
        "category_header": "Страници во категоријата „$1“",
        "subcategories": "Поткатегории",
        "passwordreset-emailtext-ip": "Некој (веројатно вие, од IP-адресата $1) побара измена на вашата\nлозинка за {{SITENAME}} ($4). Оваа е-поштенска адреса е наведена во\n{{PLURAL:$3|следнава корисничка сметка|следниве кориснички сметки}}:\n\n$2\n\n{{PLURAL:$3|Оваа привремена лозинка ќе истече|Овие привремени лозинки ќе истечат}} во рок од {{PLURAL:$5|еден ден|$5 дена}}.\nСега треба да се најавите и да внесете нова лозинка. Ако ова барање го\nпоставил некој друг, или пак во меѓувреме сте се сетиле на лозинката, и не сакате\nда ја менувате, тогаш слободно занемарете ја поракава и продолжете да ја користите старата.",
        "passwordreset-emailtext-user": "Корисникот $1 на {{SITENAME}} побара измена на вашата лозинка на {{SITENAME}}\n($4). Оваа е-поштенска адреса е наведена во {{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": "Ð\90ко Ð¾Ð²Ð° Ðµ Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80анаÑ\82а Ðµ-поÑ\88Ñ\82а Ð·Ð° вашата сметка, тогаш ќе ви биде испратено писмо за задавање на нова лозинка.",
-       "passwordreset-emailsentusername": "Ако има соодветна регистрирана е-пошта, тогаш ќе ви биде испратена порака за промена на лозинката.",
+       "passwordreset-emailsentemail": "Ð\90ко Ð¾Ð²Ð° Ðµ Ñ\80егиÑ\81Ñ\82Ñ\80иÑ\80анаÑ\82а Ðµ-поÑ\88Ñ\82а Ð¿Ð¾Ð²Ñ\80зана Ñ\81о вашата сметка, тогаш ќе ви биде испратено писмо за задавање на нова лозинка.",
+       "passwordreset-emailsentusername": "Ако има соодветна регистрирана е-пошта поврзана со ова корисничко име, тогаш ќе ви биде испратена порака за промена на лозинката.",
        "passwordreset-emailsent-capture": "Испратено е писмо за измена на лозинката (прикажано подолу).",
        "passwordreset-emailerror-capture": "Создадено е писмо за измена на лозинката (прикажано подолу), но не успеав да го испратам на {{GENDER:$2|корисникот}}: $1",
        "changeemail": "Смени или отстрани е-пошта",
        "diff-multi-otherusers": "({{PLURAL:$1|Не е прикажана една меѓувремена преработка|Не се прикажани $1 меѓувремени преработки}} од {{PLURAL:$2|еден друг корисник|$2 корисници}})",
        "diff-multi-manyusers": "({{PLURAL:$1|Не е прикажана една меѓувремена преработка направена|Не се прикажани $1 меѓувремени преработки направени}} од повеќе од $2 {{PLURAL:$2|корисник|корисници}})",
        "difference-missing-revision": "Не пронајдов {{PLURAL:$2|една преработка|$2 преработки}} од оваа разлика ($1).\n\nОва обично се должи на застарена врска за разлики што води кон избришана страница.\nПовеќе подробности ќе најдете во [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} дневникот на бришења].",
-       "searchresults": "РезÑ\83лÑ\82аÑ\82и од пребарувањето",
-       "searchresults-title": "РезÑ\83лÑ\82аÑ\82и од пребарувањето на „$1“",
+       "searchresults": "Ð\98Ñ\81Ñ\85од од пребарувањето",
+       "searchresults-title": "Ð\98Ñ\81Ñ\85од од пребарувањето на „$1“",
        "titlematches": "Совпаднати наслови",
        "textmatches": "Совпаднат текст во страниците",
        "notextmatches": "Ниеден текст во статиите не одговара",
        "shown-title": "Прикажи $1 {{PLURAL:$1|резултат|резултати}} на страница",
        "viewprevnext": "Погледајте ($1 {{int:pipe-separator}} $2) ($3).",
        "searchmenu-exists": "'''На ова вики има страница со наслов „[[:$1]]“'''",
-       "searchmenu-new": "<strong>СоздаÑ\98Ñ\82е Ñ\98а Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а â\80\9e[[:$1]]â\80\9c Ð½Ð° Ð¾Ð²Ð° Ð²Ð¸ÐºÐ¸!</strong> {{PLURAL:$2|0=|Ð\9fогледаÑ\98Ñ\82е Ñ\98а Ð¸ Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а Ð½Ð°Ñ\98дена Ñ\81о Ð¿Ñ\80ебаÑ\80Ñ\83ваÑ\9aеÑ\82о.|Ð\9fогледаÑ\98Ñ\82е Ð³Ð¸ Ð¸ Ð½Ð°Ñ\98дениÑ\82е Ñ\80езÑ\83лÑ\82аÑ\82и од пребарувањето.}}",
+       "searchmenu-new": "<strong>СоздаÑ\98Ñ\82е Ñ\98а Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а â\80\9e[[:$1]]â\80\9c Ð½Ð° Ð¾Ð²Ð° Ð²Ð¸ÐºÐ¸!</strong> {{PLURAL:$2|0=|Ð\9fогледаÑ\98Ñ\82е Ñ\98а Ð¸ Ñ\81Ñ\82Ñ\80аниÑ\86аÑ\82а Ð½Ð°Ñ\98дена Ñ\81о Ð¿Ñ\80ебаÑ\80Ñ\83ваÑ\9aеÑ\82о.|Ð\9fогледаÑ\98Ñ\82е Ð³Ð¾ Ð¸ Ð½Ð°Ñ\98деноÑ\82о од пребарувањето.}}",
        "searchprofile-articles": "Статии",
        "searchprofile-images": "Податотеки",
        "searchprofile-everything": "Сè",
        "right-blockemail": "Оневозможување корисници да праќаат е-пошта",
        "right-hideuser": "Блокирање корисници, сокривање од јавноста",
        "right-ipblock-exempt": "Заобиколување на IP блокирања, авто-блокирања и блокирања на IP рангови",
-       "right-proxyunbannable": "Заобиколување на автоматски блокирања на застапници",
        "right-unblockself": "Сопствено одблокирање",
        "right-protect": "Менување на степени на заштита и уредување на каскадно заштитени страници",
        "right-editprotected": "Уредување на страници заштитени како „{{int:protect-level-sysop}}“",
        "right-managechangetags": "Создавање3 или бришење на [[Special:Tags|ознаки]] од базата",
        "right-applychangetags": "Задавање на [[Special:Tags|ознаки]] заедно со направените измени",
        "right-changetags": "Додавате и отстранување на произволни [[Special:Tags|ознаки]] во поединечни преработки и дневнички записи",
+       "grant-generic": "Група права „$1“",
+       "grant-group-page-interaction": "Опходување со страници",
+       "grant-group-file-interaction": "Опходување со слики и снимки",
+       "grant-group-watchlist-interaction": "Опходување со набљудуваните",
+       "grant-group-email": "Испраќање на е-пошта",
+       "grant-group-high-volume": "Вршење на активности од голем обем",
+       "grant-group-customization": "Прилагодувања и поставки",
+       "grant-group-administration": "Вршење на административни дејства",
+       "grant-group-other": "Разни активности",
+       "grant-blockusers": "Блокирање и одблокирање корисници",
+       "grant-createaccount": "Направи сметки",
+       "grant-createeditmovepage": "Создавање, уредување и преместување страници",
+       "grant-delete": "Бришење на страници, преработки и дневнички записи",
+       "grant-editinterface": "Измена на именскиот простор „МедијаВики“ и кориснички CSS/JS",
+       "grant-editmycssjs": "Уредување на ваш кориснички CSS/JavaScript",
+       "grant-editmyoptions": "Уредете ги вашите кориснички нагодувања",
+       "grant-editmywatchlist": "Уреди мои набљудувани",
+       "grant-editpage": "Измени постоечки страници",
+       "grant-editprotected": "Уредување на заштитени страници",
+       "grant-highvolume": "Високообемно уредување",
+       "grant-oversight": "Скривање на корисници и преработки",
+       "grant-patrol": "Патрола на измени во страници",
+       "grant-protect": "Заштита на незаштитени страници",
+       "grant-rollback": "Отповикување на измени во страници",
+       "grant-sendemail": "Испраќање на е-пошта до други корисници",
+       "grant-uploadeditmovefile": "Подигање, замена и преместување на податотеки",
+       "grant-uploadfile": "Подигни нови податотеки",
+       "grant-viewdeleted": "Преглед на избришани податотеки и страници",
+       "grant-viewmywatchlist": "Преглед на вашите набљудувања",
        "newuserlogpage": "Дневник на регистрирања на корисници",
        "newuserlogpagetext": "Ова е дневник на регистрирани корисници.",
        "rightslog": "Дневник на корисничките права",
        "upload-form-label-select-file": "Одберете податотека",
        "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": "Накратко опишете го сето она што е значајно за делото. Ако е фотографија, споменете ги главните нешта што се прикажани на неа, настанот или местото.",
        "upload-form-label-usage-title": "Употреба",
        "upload-form-label-usage-filename": "Назив на податотеката",
        "foreign-structured-upload-form-label-own-work": "Ова е мое дело",
        "nrevisions": "$1 {{PLURAL:$1|измена|измени}}",
        "nimagelinks": "Се користи на $1 {{PLURAL:$1|страница|страници}}",
        "ntransclusions": "се користи на $1 {{PLURAL:$1|страница|страници}}",
-       "specialpage-empty": "Ð\9dема Ñ\80езÑ\83лÑ\82аÑ\82и Ð¾Ð´ Ð¿Ñ\80ебаÑ\80Ñ\83ваÑ\9aеÑ\82о Ð½Ð° Ð¾Ð²Ð¾Ñ\98 Ð¸Ð·Ð²ÐµÑ\88Ñ\82аÑ\98.",
+       "specialpage-empty": "Ð\9fÑ\80ебаÑ\80Ñ\83ваÑ\9aеÑ\82о Ð½Ð° Ð¾Ð²Ð¾Ñ\98 Ð¸Ð·Ð²ÐµÑ\88Ñ\82аÑ\98 Ð½Ðµ Ð´Ð°Ð´Ðµ Ð½Ð¸Ñ\88Ñ\82о.",
        "lonelypages": "Осамени страници",
        "lonelypagestext": "Следните страници не се поврзани од или трансклудирани во други страници на {{SITENAME}}.",
        "uncategorizedpages": "Некатегоризирани страници",
        "listgrouprights-namespaceprotection-header": "Ограничувања за именски простори",
        "listgrouprights-namespaceprotection-namespace": "Именски простор",
        "listgrouprights-namespaceprotection-restrictedto": "Права што им овозможуваат на корисниците да уредуваат",
+       "listgrants-summary": "Ова е список на доделувања на OAuth, секое со своите права. Корисниците можат да овластуваат извршници што ќе ги користат сметки, но со ограничувања во дозволите што им се доделени. Покрај ова, извршникот што делува во име на корисникот е ограничен на нештата на кои има права самиот корисник.\nМоже да најдете [[{{MediaWiki:Listgrouprights-helppage}}|уште информации]] за поединечните права.",
+       "listgrants-grant": "Доделување",
+       "listgrants-rights": "Права",
        "trackingcategories": "Следечки категории",
        "trackingcategories-summary": "На страницава се наведени следечки категории што автоматски се пополнуваат од програмот на МедијаВики. Нивните називи можат да се сменат со измена на соодветните системски пораки во именскиот простор {{ns:8}}.",
        "trackingcategories-msg": "Следечка категорија",
        "wlshowhideanons": "анонимни корисници",
        "wlshowhidepatr": "проверени уредувања",
        "wlshowhidemine": "моите уредувања",
+       "wlshowhidecategorization": "категоризација на страници",
        "watchlist-options": "Поставки за список на набљудувања",
        "watching": "Набљудување...",
        "unwatching": "Отстранувам од набљудувани...",
        "unblock": "Одблокирај корисник",
        "blockip": "Блокирај {{GENDER:$1|корисник}}",
        "blockip-legend": "Блокирај корисник",
-       "blockiptext": "Користете го долниот образец за да го забраните пристапот за пишување од одредена IP-адреса или корисничко име.\nОва единствено треба да се прави за да се спречи вандализам, во согласност со [[{{MediaWiki:Policy-url}}|правилата на Википедија]].\nИзберете конкретна причина подолу (на пр. наведувајќи ги страниците што биле вандализирани).",
+       "blockiptext": "Користете го долниот образец за да го забраните пристапот за пишување од одредена IP-адреса или корисничко име.\nОва единствено треба да се прави за да се спречи вандализам, во согласност со [[{{MediaWiki:Policy-url}}|правилата на Википедија]].\nИзберете конкретна причина подолу (на пр. наведувајќи ги страниците што биле вандализирани).\nМожете да блокирате IP-опсези со помош на [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing синтаксата на CIDR]; најголемиот допуштен опсег е /$1 за IPv4 и /$2 за IPv6.",
        "ipaddressorusername": "IP-адреса или корисничко име:",
        "ipbexpiry": "Истек на рокот:",
        "ipbreason": "Причина:",
        "export-download": "Зачувај како податотека",
        "export-templates": "Вклучи и шаблони",
        "export-pagelinks": "Вклучи поврзани страници до длабочина од:",
+       "export-manual": "Додајте страници рачно:",
        "allmessages": "Системски пораки",
        "allmessagesname": "Име",
        "allmessagesdefault": "Текст по основно",
        "javascripttest-pagetext-frameworks": "Изберете една од следниве рамки: $1",
        "javascripttest-pagetext-skins": "Одберете со кое руво да ја направите пробата:",
        "javascripttest-qunit-intro": "Вид. [$1 документација на испробувањето] на mediawiki.org.",
-       "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-mycontris": "Список на {{GENDER:|ваши}} придонеси",
        "tooltip-pt-anoncontribs": "Список на уредувања направени од оваа IP-адреса",
        "tooltip-pt-login": "Ви препорачуваме да се најавите, иако тоа не е задолжително.",
        "tooltip-pt-logout": "Одјавување",
        "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": "Подигни податотеки",
        "pageinfo-category-files": "Број на податотеки",
        "markaspatrolleddiff": "Означи како проверена верзија",
        "markaspatrolledtext": "Означи ја верзијата како проверена",
+       "markaspatrolledtext-file": "Означи ја верзијава како испатролирана",
        "markedaspatrolled": "Означено како проверено",
        "markedaspatrolledtext": "Избраната преработка на [[:$1]] е означена како испатролирана.",
        "rcpatroldisabled": "Оневозможено проверка на скорешни промени",
        "newimages-legend": "Филтрирај",
        "newimages-label": "Име на податотека (или дел од името):",
        "newimages-showbots": "Прикажувај подигања од ботови",
+       "newimages-hidepatrolled": "Сокриј испатролриани подигања",
        "noimages": "Нема ништо.",
        "ilsubmit": "Барај",
        "bydate": "по датум",
        "size-zetapixel": "$1 ЗП",
        "size-yottapixel": "$1 ЈП",
        "bitrate-bits": "$1 б/с",
-       "bitrate-kilobits": "$1 Ðºб/с",
+       "bitrate-kilobits": "$1 Ð\9aб/с",
        "bitrate-megabits": "$1 Мб/с",
        "bitrate-gigabits": "$1 Гб/с",
        "bitrate-terabits": "$1 Тб/с",
        "hebrew-calendar-m11-gen": "ав",
        "hebrew-calendar-m12-gen": "елул",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|разговор]])",
+       "timezone-local": "Месно",
        "duplicate-defaultsort": "Предупредување: Основниот клуч за подредување „$2“ го поништува претходниот основен клуч за подредување „$1“.",
        "duplicate-displaytitle": "<strong>Предупредување:</strong> Приказниот наслов „$2“ го заменува претходнито приказен наслов „$1“.",
        "invalid-indicator-name": "<strong>Грешка:</strong> Атрибутот <code>name</code> што го покажува статусот на страницата не може да биде празен.",
        "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>Ако ова е е легитимен обид за преглед, тогаш обидете се повторно.</strong>\nАко не работи и тогаш, [[Special:UserLogout|одјавете се]] и повторно најавете се.",
+       "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 бајти}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Вкупен обем на пасусот: {{PLURAL:$1|$1 бајт|$1 бајти}} ($2; $3%).",
+       "mediastatistics-allbytes": "Вкупен обем на сите податотеки: {{PLURAL:$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": "{{PLURAL:$1|Отстранета е една завршна запирка|Отстранети се $1 завршни запирки}} од JSON",
        "json-error-unknown": "Се јави проблем со JSON. Грешка: $1.",
        "json-error-depth": "Надмината е максималната дозволена длабочина на пластот",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страницата сè уште не постои",
        "mw-widgets-titleinput-description-redirect": "пренасочување кон $1",
-       "api-error-blacklisted": "Одберете поинаков, описен наслов."
+       "api-error-blacklisted": "Одберете поинаков, описен наслов.",
+       "randomrootpage": "Случајна основна страница"
 }
index 880e386..8a2845f 100644 (file)
        "october-date": "ഒക്ടോബർ $1",
        "november-date": "നവംബർ $1",
        "december-date": "ഡിസംബർ $1",
+       "period-am": "എ.എം.",
+       "period-pm": "പി.എം.",
        "pagecategories": "{{PLURAL:$1|വർഗ്ഗം|വർഗ്ഗങ്ങൾ}}",
        "category_header": "\"$1\" എന്ന വർഗ്ഗത്തിലെ താളുകൾ",
        "subcategories": "ഉപവർഗ്ഗങ്ങൾ",
        "databaseerror-query": "ക്വറി: $1",
        "databaseerror-function": "ഫങ്ഷൻ: $1",
        "databaseerror-error": "പിഴവ്: $1",
+       "transaction-duration-limit-exceeded": "വലിയ റെപ്ലിക്കേഷൻ ലാഗ് തടയുന്നതിനായി, എഴുതുന്നതിനുള്ള താമസം ($1) $2 {{PLURAL:$2|സെക്കൻഡിലും}} അധികമായതിനാൽ ഈ കൈമാറ്റം റദ്ദാക്കിയിരിക്കുന്നു.\nതാങ്കൾ നിരവധി കാര്യങ്ങൾ ഒറ്റയടിക്ക് ചെയ്യാനാണ് ശ്രമിക്കുന്നതെങ്കിൽ, ചെറിയ എണ്ണം പരീക്ഷിച്ചു നോക്കുക.",
        "laggedslavemode": "മുന്നറിയിപ്പ്: താളിൽ അടുത്തകാലത്ത് വരുത്തിയ പുതുക്കലുകൾ ഉണ്ടാവണമെന്നില്ല.",
        "readonly": "ഡാറ്റാബേസ് ബന്ധിച്ചിരിക്കുന്നു",
        "enterlockreason": "ഡാറ്റാബേസ് ബന്ധിക്കുവാനുള്ള കാരണം സൂചിപ്പിക്കുക. അതോടൊപ്പം എപ്പോഴാണ്‌ ബന്ധനം അഴിക്കുവാൻ ഉദ്ദേശിക്കുന്നതെന്നും രേഖപ്പെടുത്തുക.",
-       "readonlytext": "പുതിയ തിരുത്തുകളും മറ്റ് മാറ്റങ്ങളും അനുവദനീയമല്ലാത്ത വിധത്തിൽ ഡാറ്റാബേസ് ബന്ധിച്ചിരിക്കുകയാണ്‌. ക്രമപ്രകാരമുള്ള വൃത്തിയാക്കലിനു വേണ്ടി ബന്ധിച്ച ഡാറ്റാബേസ് താമസിയാതെ തന്നെ സാധാരണ നില കൈവരിക്കും.\n\nഡാറ്റാബേസ് ബന്ധിച്ച കാര്യനിർവാഹകൻ അതിനു സൂചിപ്പിച്ച കാരണം: $1",
+       "readonlytext": "à´ªàµ\81തിയ à´¤à´¿à´°àµ\81à´¤àµ\8dà´¤àµ\81à´\95à´³àµ\81à´\82 à´®à´±àµ\8dà´±àµ\8d à´®à´¾à´±àµ\8dà´±à´\99àµ\8dà´\99à´³àµ\81à´\82 à´\85à´¨àµ\81വദനàµ\80യമലàµ\8dലാതàµ\8dà´¤ à´µà´¿à´§à´¤àµ\8dതിൽ à´¡à´¾à´±àµ\8dറാബàµ\87à´¸àµ\8d à´¬à´¨àµ\8dധിà´\9aàµ\8dà´\9aà´¿à´°à´¿à´\95àµ\8dà´\95àµ\81à´\95യാണàµ\8dâ\80\8c. à´\95àµ\8dരമപàµ\8dà´°à´\95ാരമàµ\81à´³àµ\8dà´³ à´µàµ\83à´¤àµ\8dതിയാà´\95àµ\8dà´\95ലിനàµ\81 à´µàµ\87à´£àµ\8dà´\9fà´¿ à´¬à´¨àµ\8dധിà´\9aàµ\8dà´\9a à´¡à´¾à´±àµ\8dറാബàµ\87à´¸àµ\8d à´¤à´¾à´®à´¸à´¿à´¯à´¾à´¤àµ\86 à´¤à´¨àµ\8dà´¨àµ\86 à´¸à´¾à´§à´¾à´°à´£ à´¨à´¿à´² à´\95àµ\88വരിà´\95àµ\8dà´\95àµ\81à´\82.\n\nഡാറàµ\8dറാബàµ\87à´¸àµ\8d à´¬à´¨àµ\8dധിà´\9aàµ\8dà´\9a à´¸à´¿à´¸àµ\8dà´±àµ\8dà´±à´\82 à´\95ാരàµ\8dയനിർവാഹà´\95ൻ à´\85തിനàµ\81 à´¸àµ\82à´\9aà´¿à´ªàµ\8dപിà´\9aàµ\8dà´\9a à´\95ാരണà´\82: $1",
        "missing-article": "താളിൽ ഉണ്ടായിരിക്കേണ്ട വിവരങ്ങൾ (\"$1\" $2), വിവരശേഖരത്തിൽ കണ്ടെത്താനായില്ല.\n\nനീക്കം ചെയ്യപ്പെട്ട ഒരു താളിലെ നാൾവഴിയുടേയോ മാറ്റത്തിന്റേയോ കണ്ണി പിന്തുടർന്നതിനാലായിരിക്കാം മിക്കവാറൂം ഇത് സംഭവിച്ചത്.\n\nഅല്ലെങ്കിൽ ഇത് ഒരു സോഫ്റ്റ്‌വെയർ ബഗ്ഗ് ആയിരിക്കാം.\nദയവായി താളിന്റെ യു.ആർ.എൽ സഹിതം ഒരു [[Special:ListUsers/sysop|കാര്യനിർവാഹകയെ(നെ)]] ഇത് അറിയിക്കുക.",
        "missingarticle-rev": "(മാറ്റം#: $1)",
        "missingarticle-diff": "(വ്യത്യാസം: $1, $2)",
        "mypreferencesprotected": "താങ്കൾക്ക് സ്വന്തം ക്രമീകരണങ്ങൾ തിരുത്താനുള്ള അനുമതിയില്ല.",
        "ns-specialprotected": "പ്രത്യേകം എന്ന നാമമേഖലയിൽ വരുന്ന താളുകൾ തിരുത്താനാവുന്നവയല്ല.",
        "titleprotected": "[[User:$1|$1]] എന്ന ഉപയോക്താവ് ഈ താൾ ഉണ്ടാക്കുന്നതു നിരോധിച്ചിരിക്കുന്നു.\n''$2'' എന്നതാണു അതിനു കാണിച്ചിട്ടുള്ള കാരണം.",
-       "filereadonlyerror": "പ്രമാണ ശേഖരണി \"$2\" ഇപ്പോൾ \"കാണൽ-മാത്രം\" വിധത്തിൽ ക്രമീകരിച്ചിരിക്കുന്നതിനാൽ \"$1\" എന്ന പ്രമാണത്തിൽ മാറ്റം വരുത്താനാകില്ല.\n\nബന്ധിച്ച കാര്യ‌നിർവാഹക(ൻ) നൽകിയിരിക്കുന്ന കാരണം \"''$3''\" എന്നാണ്.",
+       "filereadonlyerror": "à´ªàµ\8dരമാണ à´¶àµ\87à´\96രണി \"$2\" à´\87à´ªàµ\8dà´ªàµ\8bൾ \"à´\95ാണൽ-മാതàµ\8dà´°à´\82\" à´µà´¿à´§à´¤àµ\8dതിൽ à´\95àµ\8dà´°à´®àµ\80à´\95à´°à´¿à´\9aàµ\8dà´\9aà´¿à´°à´¿à´\95àµ\8dà´\95àµ\81à´¨àµ\8dനതിനാൽ \"$1\" à´\8eà´¨àµ\8dà´¨ à´ªàµ\8dരമാണതàµ\8dതിൽ à´®à´¾à´±àµ\8dà´±à´\82 à´µà´°àµ\81à´¤àµ\8dതാനാà´\95à´¿à´²àµ\8dà´².\n\nബനàµ\8dധിà´\9aàµ\8dà´\9a à´¸à´¿à´¸àµ\8dà´±àµ\8dà´±à´\82 à´\95ാരàµ\8dà´¯â\80\8cനിർവാഹà´\95(ൻ) à´¨àµ½à´\95ിയിരിà´\95àµ\8dà´\95àµ\81à´¨àµ\8dà´¨ à´\95ാരണà´\82 \"''$3''\" à´\8eà´¨àµ\8dനാണàµ\8d.",
        "invalidtitle-knownnamespace": "നാമമേഖല \"$2\", എഴുത്ത് \"$3\" എന്നിവ ഉപയോഗിച്ചുള്ള അസാധുവായ തലക്കെട്ട്",
        "invalidtitle-unknownnamespace": "അപരിചിതമായ നാമമേഖലാ സംഖ്യ $1, എഴുത്ത് \"$2\" എന്നിവ ഉപയോഗിച്ചുള്ള അസാധുവായ തലക്കെട്ട്",
        "exception-nologin": "ലോഗിൻ ചെയ്തിട്ടില്ല",
        "virus-scanfailed": "വൈറസ് സ്കാനിങ് പരാജയപ്പെട്ടു (code $1)",
        "virus-unknownscanner": "തിരിച്ചറിയാനാകാത്ത ആന്റിവൈറസ്:",
        "logouttext": "'''താങ്കൾ ഇപ്പോൾ {{SITENAME}} സംരംഭത്തിൽനിന്നും ലോഗൗട്ട് ചെയ്തിരിക്കുന്നു'''\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": "ഒന്നുകിൽ ഡേറ്റാബേസ് സാധൂകരണത്തിൽ പ്രശ്നം ഉണ്ടായിരുന്നു അല്ലെങ്കിൽ നവീകരിക്കുവാൻ താങ്കളുടെ ബാഹ്യ അംഗത്വം താങ്കളെ അനുവദിക്കുന്നില്ല.",
        "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": "രഹസ്യവാക്ക് പുനഃക്രമീകരിക്കുക",
+       "botpasswords-label-grants": "ബാധകമായ അനുമതികൾ:",
+       "botpasswords-label-restrictions": "ഉപയോഗത്തിന്റെ പരിമിതപ്പെടുത്തലുകൾ:",
+       "botpasswords-label-grants-column": "അനുവദിച്ചിരിക്കുന്നവ",
        "resetpass_forbidden": "രഹസ്യവാക്കുകൾ മാറ്റുന്നത് അനുവദിക്കുന്നില്ല",
        "resetpass-no-info": "ഈ താൾ നേരിട്ടു കാണുന്നതിന് താങ്കൾ ലോഗിൻ ചെയ്തിരിക്കണം.",
        "resetpass-submit-loggedin": "രഹസ്യവാക്ക് മാറ്റുക",
        "passwordreset-emailtext-ip": "ആരോ ഒരാൾ (മിക്കവാറും താങ്കളായിരിക്കും, $1 എന്ന ഐ.പി. വിലാസത്തിൽ നിന്നും) {{SITENAME}} സംരംഭത്തിലെ ($4) താങ്കളുടെ രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാൻ അഭ്യർത്ഥിച്ചിരിക്കുന്നു. ഈ ഇമെയിൽ വിലാസവുമായി ബന്ധപ്പെട്ടിരിക്കുന്ന {{PLURAL:$3|അംഗത്വം|അംഗത്വങ്ങൾ}} താഴെക്കൊടുത്തിരിക്കുന്നു:\n\n$2\n\n\nഈ {{PLURAL:$3|താത്കാലിക രഹസ്യവാക്ക്|താത്കാലിക രഹസ്യവാക്കുകൾ}} {{PLURAL:$5|ഒരു ദിവസം|$5 ദിവസങ്ങൾ}} കൊണ്ട് കാലഹരണപ്പെട്ട് പോകുന്നവയാണ്.\nതാങ്കൾ ഇപ്പോൾ തന്നെ പ്രവേശിച്ച് രഹസ്യവാക്ക് മാറ്റുന്നതാണ് ഉചിതം. ഈ അഭ്യർത്ഥന മറ്റാരോ ആണ് നടത്തിയത് അല്ലെങ്കിൽ, യഥാർത്ഥ രഹസ്യവാക്ക് താങ്കൾ ഓർമ്മിക്കുകയും അത് മാറ്റാൻ ആഗ്രഹിക്കാതിരിക്കുകയും ആണെങ്കിൽ, ഈ സന്ദേശം അവഗണിച്ച് താങ്കളുടെ പഴയ രഹസ്യവാക്ക് തുടർന്നും ഉപയോഗിക്കാവുന്നതാണ്.",
        "passwordreset-emailtext-user": "{{SITENAME}} സംരംഭത്തിലെ ഉപയോക്താവായ $1 {{SITENAME}} സംരംഭത്തിലെ ($4) രഹസ്യവാക്ക് പുനർസജ്ജീകരിക്കാൻ അഭ്യർത്ഥിച്ചിരിക്കുന്നു. ഈ ഇമെയിൽ വിലാസവുമായി ബന്ധപ്പെട്ടിരിക്കുന്ന {{PLURAL:$3|അംഗത്വം|അംഗത്വങ്ങൾ}} താഴെക്കൊടുത്തിരിക്കുന്നു:\n\n$2\n\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": "ഇമെയിൽ വിലാസം മാറ്റുക അല്ലെങ്കിൽ നീക്കംചെയ്യുക",
        "copyrightwarning2": "{{SITENAME}} സംരംഭത്തിൽ താങ്കൾ എഴുതി ചേർക്കുന്നതെല്ലാം മറ്റുപയോക്താക്കൾ തിരുത്തുകയോ, മാറ്റം വരുത്തുകയോ, ഒഴിവാക്കുകയോ ചെയ്തേക്കാം. താങ്കൾ എഴുതി ചേർക്കുന്നതു മറ്റ് ഉപയോക്താക്കൾ തിരുത്തുന്നതിലോ ഒഴിവാക്കുന്നതിലോ താങ്കൾക്ക് എതിർപ്പുണ്ടെങ്കിൽ ദയവായി ലേഖനമെഴുതാതിരിക്കുക.\nഇതു താങ്കൾത്തന്നെ എഴുതിയതാണെന്നും, അതല്ലെങ്കിൽ പകർപ്പവകാശ നിയമങ്ങളുടെ പരിധിയിലില്ലാത്ത ഉറവിടങ്ങളിൽനിന്നും പകർത്തിയതാണെന്നും ഉറപ്പാക്കുക (കുടുതൽ വിവരത്തിനു $1 കാണുക).\n'''പകർപ്പവകാശ സംരക്ഷണമുള്ള സൃഷ്ടികൾ ഒരു കാരണവശാലും ഇവിടെ പ്രസിദ്ധീകരിക്കരുത്!'''",
        "editpage-cannot-use-custom-model": "ഈ താളിന്റെ ഉള്ളടക്ക മാതൃക മാറ്റാൻ കഴിയില്ല.",
        "longpageerror": "'''പിഴവ്: താങ്കൾ സമർപ്പിച്ച എഴുത്തുകൾക്ക് {{PLURAL:$1|ഒരു കിലോബൈറ്റ്|$1 കിലോബൈറ്റ്സ്}} വലിപ്പമുണ്ട്. പരമാവധി അനുവദനീയമായ വലിപ്പം {{PLURAL:$2|ഒരു കിലോബൈറ്റ്|$2 കിലോബൈറ്റ്സ്}} ആണ്‌. അതിനാലിതു സേവ് ചെയ്യാൻ സാദ്ധ്യമല്ല.'''",
-       "readonlywarning": "'''മുന്നറിയിപ്പ്: ഡേറ്റാബേസ് പരിപാലനത്തിനു വേണ്ടി ബന്ധിച്ചിരിക്കുന്നു, അതുകൊണ്ട് താങ്കളിപ്പോൾ വരുത്തിയ മാറ്റങ്ങൾ സേവ് ചെയ്യാൻ സാദ്ധ്യമല്ല.''' താങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ ഒരു ടെക്സ്റ്റ് ഫയലിലേക്ക് പകർത്തി (കോപ്പി & പേസ്റ്റ്) പിന്നീടുപയോഗിക്കുന്നതിനായി കരുതിവക്കാൻ താല്പര്യപ്പെടുന്നു. ഡേറ്റാബേസ് ബന്ധിച്ച അഡ്മിനിസ്ട്രേറ്റർ നൽകിയ വിശദീകരണം: $1",
+       "readonlywarning": "<strong>മുന്നറിയിപ്പ്: ഡേറ്റാബേസ് പരിപാലനത്തിനു വേണ്ടി ബന്ധിച്ചിരിക്കുന്നു, അതുകൊണ്ട് താങ്കളിപ്പോൾ വരുത്തിയ മാറ്റങ്ങൾ സേവ് ചെയ്യാൻ സാദ്ധ്യമല്ല.</strong>\nതാങ്കൾ വരുത്തിയ മാറ്റങ്ങൾ ഒരു ടെക്സ്റ്റ് ഫയലിലേക്ക് പകർത്തി (കോപ്പി & പേസ്റ്റ്)  പിന്നീടുപയോഗിക്കുന്നതിനായി കരുതിവക്കാൻ താല്പര്യപ്പെടുന്നു. \n\nഡേറ്റാബേസ് ബന്ധിച്ച സിസ്റ്റം അഡ്മിനിസ്ട്രേറ്റർ നൽകിയ വിശദീകരണം: $1",
        "protectedpagewarning": "'''മുന്നറിയിപ്പ്:  ഈ താൾ കാര്യനിർവാഹക പദവിയുള്ളവർക്കു മാത്രം തിരുത്താൻ സാധിക്കാവുന്ന തരത്തിൽ സം‌രക്ഷിക്കപ്പെട്ടിരിക്കുന്നു.''' അവലംബമായി രേഖകളിൽ ലഭ്യമായ ഏറ്റവും പുതിയ വിവരം താഴെ നൽകിയിരിക്കുന്നു:",
        "semiprotectedpagewarning": "'''ശ്രദ്ധിക്കുക:'''അംഗത്വമെടുത്തിട്ടുള്ളവർക്കുമാത്രം തിരുത്താൻ സാധിക്കുന്ന വിധത്തിൽ ഈ താൾ സംരക്ഷിക്കപ്പെട്ടിരിക്കുന്നു. അവലംബമായി രേഖകളിലെ ഏറ്റവും പുതിയ വിവരം താഴെ കൊടുത്തിരിക്കുന്നു:",
        "cascadeprotectedwarning": "<strong>മുന്നറിയിപ്പ്:</strong> ഈ താൾ കാര്യനിർവാഹക അവകാശമുള്ളവർക്കു മാത്രം തിരുത്തുവാൻ സാധിക്കുന്ന വിധത്തിൽ സം‌രക്ഷിക്കപ്പെട്ടിട്ടുള്ളതാണ്‌. ഇനിക്കൊടുക്കുന്ന {{PLURAL:$1|താൾ|താളുകൾ}} നിർഝരിത(cascade) സം‌രക്ഷണം ചെയ്തപ്പോൾ അതിന്റെ ഭാഗമായി സംരക്ഷിക്കപ്പെട്ടിട്ടുള്ളതാണ്‌ ഈ താൾ:",
        "userrights": "ഉപയോക്തൃ അവകാശ പരിപാലനം",
        "userrights-lookup-user": "ഉപയോക്തൃസംഘങ്ങളെ പരിപാലിക്കുക",
        "userrights-user-editname": "ഒരു ഉപയോക്തൃനാമം ടൈപ്പു ചെയ്യുക:",
-       "editusergroup": "ഉപയോക്തൃസംഘങ്ങൾ തിരുത്തുക",
+       "editusergroup": "{{GENDER:$1|ഉപയോക്തൃസംഘങ്ങൾ}} തിരുത്തുക",
        "editinguser": "{{GENDER:$1|user}} <strong>[[User:$1|$1]]</strong> $2 എന്ന ഉപയോക്താവിന്റെ ഉപയോക്തൃ അവകാശങ്ങൾ തിരുത്തുന്നു",
        "userrights-editusergroup": "ഉപയോക്തൃസമൂഹത്തിലെ അംഗത്വം തിരുത്തുക",
-       "saveusergroups": "ഉപയോക്തൃസംഘങ്ങൾ സേവ് ചെയ്യുക",
+       "saveusergroups": "{{GENDER:$1|ഉപയോക്തൃസംഘങ്ങൾ}} സേവ് ചെയ്യുക",
        "userrights-groupsmember": "അംഗത്വമുള്ളത്:",
        "userrights-groupsmember-auto": "അന്തർലീനമായ അംഗത്വം:",
        "userrights-groups-help": "ഈ ഉപയോക്താവ് ഉൾപ്പെട്ടിട്ടുള്ള സംഘങ്ങൾ താങ്കൾക്ക് മാറ്റാവുന്നതാണ്:\n*ഉപയോക്താവ് ആ സംഘത്തിലുണ്ടെന്ന് ശരിയിട്ട ചതുരം അർത്ഥമാക്കുന്നു.\n*ഉപയോക്താവ് ആ സംഘത്തിലില്ലെന്ന് ശരിയിടാത്ത ചതുരം അർത്ഥമാക്കുന്നു.\n*സംഘങ്ങളോടൊപ്പമുള്ള *,  ഒരിക്കൽ മാറ്റം വരുത്തിയാൽ പിന്നീട് അതിൽ മാറ്റം വരുത്താൻ താങ്കൾക്ക് കഴിയില്ലെന്ന് അർത്ഥമാക്കുന്നു.",
        "right-createpage": "താളുകൾ സൃഷ്ടിക്കുക (സംവാദം താളുകൾ അല്ലാത്തവ)",
        "right-createtalk": "സംവാദ താളുകൾ സൃഷ്ടിക്കുക",
        "right-createaccount": "പുതിയ ഉപയോക്തൃ അംഗത്വങ്ങൾ സൃഷ്ടിക്കുക",
+       "right-autocreateaccount": "ബാഹ്യ ഉപയോക്തൃ അംഗത്വമുപയോഗിച്ച് സ്വയം പ്രവേശിക്കുക",
        "right-minoredit": "ചെറിയ തിരുത്തലായി രേഖപ്പെടുത്തുക",
        "right-move": "താളുകൾ നീക്കുക",
        "right-move-subpages": "താളുകൾ അവയുടെ ഉപതാളുകളോടുകൂടീ നീക്കുക",
        "right-blockemail": "ഇമെയിൽ അയക്കുന്നതിൽ നിന്നും ഉപയോക്താവിനെ തടയുക",
        "right-hideuser": "ഒരു ഉപയോക്തൃനാമത്തെ തടയുക, പരസ്യമായി കാണപ്പെടുന്നതിൽ നിന്നും മറയ്ക്കുന്നു",
        "right-ipblock-exempt": "ഐ.പി. തടയലുകൾ, സ്വതേയുള്ള തടയലുകൾ, റേഞ്ച് തടയലുകൾ ഒക്കെ ബാധകമല്ലാതിരിക്കുക",
-       "right-proxyunbannable": "പ്രോക്സികളെ സ്വതേ തടയുന്നത് ബാധകമല്ലാതിരിക്കുക",
        "right-unblockself": "സ്വയം തടയൽ നീക്കുക",
        "right-protect": "സംരക്ഷണ മാനത്തിൽ മാറ്റം വരുത്തുക, നിർഝരിത മാർഗ്ഗത്തിൽ സംരക്ഷിക്കപ്പെട്ടിരിക്കുന്ന താളുകൾ തിരുത്തുക",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" എന്ന് അടയാളപ്പെടുത്തി സംരക്ഷിച്ചിട്ടുള്ള താളുകൾ തിരുത്തുക",
        "right-managechangetags": "ഡേറ്റാബേസിൽ നിന്നുള്ള [[Special:Tags|ടാഗുകൾ]] സൃഷ്ടിക്കുക അല്ലെങ്കിൽ മായ്ക്കുക",
        "right-applychangetags": "മാറ്റങ്ങളോടൊപ്പം [[Special:Tags|ടാഗുകളും]] ബാധകമാക്കുക",
        "right-changetags": "ഒറ്റയൊറ്റ നാൾപ്പതിപ്പുകൾക്കും രേഖയിലെ ഉൾപ്പെടുത്തലുകൾക്കും ഐച്ഛിക [[Special:Tags|ടാഗുകൾ]] ചേർക്കുക അല്ലെങ്കിൽ നീക്കംചെയ്യുക",
+       "grant-generic": "\"$1\" അവകാശ സഞ്ചയം",
+       "grant-group-page-interaction": "താളുകളുമായി സമ്പർക്കം പുലർത്തുക",
+       "grant-group-file-interaction": "മീഡിയയുമായി സമ്പർക്കം പുലർത്തുക",
+       "grant-group-watchlist-interaction": "ശ്രദ്ധിക്കേണ്ട പട്ടികയുമായി സമ്പർക്കം പുലർത്തുക",
+       "grant-group-email": "ഇമെയിൽ അയയ്ക്കുക",
+       "grant-group-high-volume": "ഉയർന്ന തോതിലുള്ള പ്രവൃത്തികൾ നടത്തുക",
+       "grant-group-customization": "ഇച്ഛാനുസരണമാക്കലുകളും ക്രമീകരണങ്ങളും",
+       "grant-group-administration": "കാര്യനിർവ്വാഹക ജോലികൾ നടത്തുക",
+       "grant-group-other": "വിവിധ പ്രവൃത്തികൾ",
+       "grant-blockusers": "ഉപയോക്താക്കളെ തടയുക, തടയൽ നീക്കുക",
+       "grant-createaccount": "അംഗത്വങ്ങൾ സൃഷ്ടിക്കുക",
+       "grant-createeditmovepage": "താളുകൾ സൃഷ്ടിക്കുക, തിരുത്തുക, മാറ്റുക",
+       "grant-delete": "താളുകൾ, നാൾപ്പതിപ്പുകൾ, രേഖകളിലെ ഉൾപ്പെടുത്തലുകൾ മായ്ക്കുക",
+       "grant-editinterface": "മീഡിയവിക്കി നാമമേഖലയും ഉപയോക്തൃ സി.എസ്.എസ്./ജാവാസ്ക്രിപ്റ്റും തിരുത്തുക",
+       "grant-editmycssjs": "താങ്കളുടെ ഉപയോക്തൃ സി.എസ്.എസ്./ജാവാസ്ക്രിപ്റ്റ് തിരുത്തുക",
+       "grant-editmyoptions": "താങ്കളുടെ ഉപയോക്തൃ ക്രമീകരണങ്ങൾ തിരുത്തുക",
+       "grant-editmywatchlist": "താങ്കൾ ശ്രദ്ധിക്കുന്നവയുടെ പട്ടിക തിരുത്തുക",
+       "grant-editpage": "നിലവിലുള്ള താളുകൾ തിരുത്തുക",
+       "grant-editprotected": "സംരക്ഷിച്ചിട്ടുള്ള താളുകൾ തിരുത്തുക",
+       "grant-highvolume": "ഉയർന്ന അളവിലുള്ള തിരുത്തുകൾ",
+       "grant-oversight": "ഉപയോക്താക്കളെ മറയ്ക്കുക ഒപ്പം നാൾപ്പതിപ്പുകൾ ഒതുക്കുക",
+       "grant-patrol": "താളുകളിലെ മാറ്റങ്ങളിൽ റോന്തുചുറ്റുക",
+       "grant-protect": "താളുകൾ സംരക്ഷിക്കുക, സംരക്ഷണം നീക്കുക",
+       "grant-rollback": "താളുകളിലെ മാറ്റങ്ങൾ മുൻപ്രാപനം ചെയ്യുക",
+       "grant-sendemail": "മറ്റുപയോക്താക്കൾക്ക് ഇമെയിൽ അയയ്ക്കുക",
+       "grant-uploadeditmovefile": "പ്രമാണങ്ങൾ അപ്‌ലോഡ് ചെയ്യുക, മാറ്റിച്ചേർക്കുക, മാറ്റുക",
+       "grant-uploadfile": "പുതിയ പ്രമാണങ്ങൾ അപ്‌ലോഡ് ചെയ്യുക",
+       "grant-basic": "അടിസ്ഥാന അവകാശങ്ങൾ",
+       "grant-viewdeleted": "മായ്ക്കപ്പെട്ട പ്രമാണങ്ങളും താളുകളും കാണുക",
+       "grant-viewmywatchlist": "താങ്കൾ ശ്രദ്ധിക്കുന്നവയുടെ പട്ടിക കാണുക",
        "newuserlogpage": "ഉപയോക്തൃ സൃഷ്ടിയുടെ രേഖ",
        "newuserlogpagetext": "പുതിയതായി അംഗത്വമെടുത്ത ഉപയോക്താക്കളുടെ പട്ടിക താഴെ കാണാം.",
        "rightslog": "ഉപയോക്തൃ അവകാശ രേഖ",
        "action-createpage": "താളുകൾ നിർമ്മിക്കുക",
        "action-createtalk": "സംവാദ താളുകൾ നിർമ്മിക്കുക",
        "action-createaccount": "ഈ ഉപയോക്തൃനാമം സൃഷ്ടിക്കുക",
+       "action-autocreateaccount": "ബാഹ്യ ഉപയോക്തൃ അംഗത്വം സ്വതേ സൃഷ്ടിക്കുക",
        "action-history": "ഈ താളിന്റെ നാൾവഴി കാണുക",
        "action-minoredit": "ഈ തിരുത്തൽ ഒരു ചെറിയ തിരുത്തലായി അടയാളപ്പെടുത്തുക",
        "action-move": "ഈ താൾ മാറ്റുക",
        "upload-form-label-select-file": "പ്രമാണം തിരഞ്ഞെടുക്കുക",
        "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": "പ്രമാണത്തിന്റെ പേര്",
        "foreign-structured-upload-form-label-own-work": "ഇതെന്റെ സ്വന്തം സൃഷ്ടി ആണ്",
        "foreign-structured-upload-form-label-own-work-message-shared": "ഈ പ്രമാണത്തിന്റെ പകർപ്പവകാശം എനിക്ക് സ്വന്തമാണെന്നും, ഈ പ്രമാണം വിക്കിമീഡിയ കോമൺസിൽ പിന്നീട് മാറ്റാനാവത്തവിധം [https://creativecommons.org/licenses/by-sa/4.0/ ക്രിയേറ്റീവ് കോമൺസ് ആട്രിബ്യൂഷൻ-ഷെയർഎലൈക് 4.0] ഉപയോഗാനുമതിയിൽ പ്രസിദ്ധീകരിക്കാമെന്നും [https://wikimediafoundation.org/wiki/Terms_of_Use/ml ഉപയോഗനിബന്ധനകൾ] അംഗീകരിക്കുന്നുവെന്നും സാക്ഷ്യപ്പെടുത്തുന്നു.",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "ഈ പ്രമാണത്തിന്റെ പകർപ്പവകാശം താങ്കളുടെ സ്വന്തമല്ലെങ്കിൽ അഥവാ മറ്റൊരു ഉപയോഗാനുമതിയിലാണ് പ്രമാണം പ്രസിദ്ധീകരിക്കാൻ ഉദ്ദേശിക്കുന്നതെങ്കിൽ [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=ml കോമൺസിലെ അപ്‌ലോഡ് സഹായി] ഉപയോഗിക്കുന്നത് പരിഗണിക്കുക.",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "ഈ പ്രമാണം അവരുടെ നയങ്ങൾക്കനുസൃതമായി അപ്‌ലോഡ് ചെയ്യാൻ സൈറ്റ് അനുവദിക്കുമെങ്കിൽ [[Special:Upload|{{SITENAME}} സംരംഭത്തിലെ അപ്‌ലോഡ് താൾ]] പരീക്ഷിച്ചു നോക്കാവുന്നതാണ്.",
+       "foreign-structured-upload-form-2-label-intro": "{{SITENAME}} സംരംഭത്തിലേക്ക് ഒരു ചിത്രം സംഭാവന ചെയ്യുന്നതിന് നന്ദി. നിബന്ധനകൾ പാലിക്കുന്നുണ്ടോയെന്ന് പരിശോധിക്കുക:",
+       "foreign-structured-upload-form-2-label-ownwork": "ഇത് <strong>താങ്കളുടെ സ്വന്തം സൃഷ്ടി</strong> ആയിരിക്കണം, ഇന്റർനെറ്റിൽ നിന്ന് സംഘടിപ്പിച്ചത് ആയിരിക്കരുത്",
+       "foreign-structured-upload-form-2-label-noderiv": "ഇതിൽ <strong>മറ്റുള്ളവരുടെ സൃഷ്ടികൾ ഉൾപ്പെടരുത്</strong>, അവയിൽ നിന്ന് പ്രചോദിതമായി സൃഷ്ടിച്ചതും പാടില്ല",
+       "foreign-structured-upload-form-2-label-useful": "ഇത് <strong>വൈജ്ഞാനിക  മൂല്യമുള്ളതും</strong> മറ്റുള്ളവർക്ക് വിദ്യാഭ്യാസാവശ്യങ്ങൾക്ക് ഉപയോഗിക്കാനാകുന്നതുമാവണം",
+       "foreign-structured-upload-form-2-label-ccbysa": "ഇത് ഇന്റർനെറ്റിൽ  [https://creativecommons.org/licenses/by-sa/4.0/ ക്രിയേറ്റീവ് കോമൺസ് ആട്രിബ്യൂഷൻ-ഷെയർഎലൈക് 4.0] ഉപയോഗാനുമതിയിൽ <strong>എന്നെന്നേയ്ക്കുമായി പ്രസിദ്ധീകരിക്കുന്നതുമാവണം</strong>",
+       "foreign-structured-upload-form-2-label-alternative": "മുകളിൽ കൊടുത്തിരിക്കുന്നതത്രയും പാലിക്കുന്നില്ലെങ്കിലും, അതൊരു സ്വതന്ത്ര ഉപയോഗാനുമതിയിൽ ഉള്ളതാണെങ്കിൽ [https://commons.wikimedia.org/wiki/Special:UploadWizard കോമൺസിലെ അപ്‌ലോഡ് സഹായി] ഉപയോഗിച്ച് താങ്കൾക്ക് ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യാൻ സാധിച്ചേക്കാം.",
+       "foreign-structured-upload-form-2-label-termsofuse": "ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യുന്നത് വഴി, ഈ പ്രമാണത്തിന്റെ പകർപ്പവകാശം താങ്കൾക്ക് സ്വന്തമാണെന്ന് താങ്കൾ സാക്ഷ്യപ്പെടുത്തുന്നുണ്ട്, അതോടൊപ്പം ഈ പ്രമാണം ഇനി മാറ്റാനാവാത്ത വിധം വിക്കിമീഡിയ കോമൺസിൽ ക്രിയേറ്റീവ് കോമൺസ് ആട്രിബ്യൂഷൻ-ഷെയർഎലൈക് 4.0 ഉപയോഗാനുമതി പ്രകാരമാണ് താങ്കൾ പ്രസിദ്ധീകരിക്കുന്നതെന്നും, [https://wikimediafoundation.org/wiki/Terms_of_Use ഉപയോഗനിബന്ധനകൾക്കും] സമ്മതിക്കുകയും ചെയ്യുന്നുണ്ട്.",
+       "foreign-structured-upload-form-3-label-question-website": "ഈ ചിത്രം ഏതെങ്കിലും വെബ്‌സൈറ്റിൽ നിന്ന് ഡൗൺലോഡ് ചെയ്തതോ, ചിത്രങ്ങൾ തിരഞ്ഞ് ലഭ്യമാക്കിയതോ ആണോ?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "താങ്കൾ ഈ ചിത്രം സ്വന്തമായി (ഫോട്ടോ എടുത്ത്, ചിത്രം വരച്ച് തുടങ്ങിയ രീതികളിൽ) സൃഷ്ടിച്ചതാണോ?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "ഇത് ലോഗോ പോലുള്ള മറ്റാരുടെയെങ്കിലും സൃഷ്ടി ഉൾപ്പെടുന്ന ചിത്രം ആണോ, അല്ലെങ്കിൽ അതിൽ നിന്ന് പ്രചോദനം ഉൾക്കൊണ്ട് സൃഷ്ടിച്ചതാണോ?",
+       "foreign-structured-upload-form-3-label-yes": "അതെ",
+       "foreign-structured-upload-form-3-label-no": "അല്ല",
+       "foreign-structured-upload-form-3-label-alternative": "അങ്ങനെയെങ്കിൽ നിർഭാഗ്യവശാൽ, ഈ ഉപകരണം ഉപയോഗിച്ച് ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യുന്നത് പിന്തുണയ്ക്കാനാവില്ല. അതൊരു സ്വതന്ത്ര ഉപയോഗാനുമതിയിൽ ഉള്ളതാണെങ്കിൽ [https://commons.wikimedia.org/wiki/Special:UploadWizard കോമൺസിലെ അപ്‌ലോഡ് സഹായി] ഉപയോഗിച്ച് താങ്കൾക്ക് ഈ പ്രമാണം അപ്‌ലോഡ് ചെയ്യാൻ സാധിച്ചേക്കാം.",
+       "foreign-structured-upload-form-4-label-good": "ഈ ഉപകരണം ഉപയോഗിച്ച്, മറ്റുള്ളവരുടെ സൃഷ്ടികൾ ഉൾപ്പെടാത്ത, വിദ്യാഭ്യാസ ആവശ്യങ്ങൾക്ക് താങ്കൾ സൃഷ്ടിച്ച പടങ്ങളോ, താങ്കൾ എടുത്ത ഫോട്ടോഗ്രാഫുകളോ താങ്കൾക്ക് അപ്‌ലോഡ് ചെയ്യാവുന്നതാണ്.",
+       "foreign-structured-upload-form-4-label-bad": "സേർച്ച് എഞ്ചിനിൽ നിന്ന ലഭിച്ച അല്ലെങ്കിൽ മറ്റ് വെബ്‌സൈറ്റുകളിൽ നിന്ന് അപ്‌ലോഡ് ചെയ്ത ചിത്രങ്ങൾ അപ്‌ലോഡ് ചെയ്യാൻ കഴിയില്ല.",
        "backend-fail-stream": "$1 എന്ന പ്രമാണം സ്ട്രീം ചെയ്യാൻ കഴിഞ്ഞില്ല.",
        "backend-fail-backup": "$1 എന്ന പ്രമാണത്തിന്റെ ബാക്ക്അപ് എടുക്കാൻ കഴിഞ്ഞില്ല.",
        "backend-fail-notexists": "$1 എന്ന പ്രമാണം നിലവിലില്ല.",
        "log-title-wildcard": "ഈ വാക്കിൽ തുടങ്ങുന്ന തിരച്ചിൽ ഫലങ്ങൾ",
        "showhideselectedlogentries": "തിരഞ്ഞെടുത്ത രേഖാ വിവരങ്ങൾ പ്രദർശിപ്പിക്കുക/മറയ്ക്കുക",
        "log-edit-tags": "രേഖയിലെ തിരഞ്ഞെടുക്കപ്പെട്ട ഉൾപ്പെടുത്തലുകളുടെ ടാഗുകൾ തിരുത്തുക",
+       "checkbox-select": "തിരഞ്ഞെടുക്കുക: $1",
+       "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": "ഈ താളിൽ മീഡിയവിക്കി സോഫ്റ്റ്‌വേർ സ്വതേ നിർമ്മിക്കുന്ന പിന്തുടരൽ വർഗ്ഗങ്ങളുടെ പട്ടിക കാണാം. അവയുടെ പേരുകൾ {{ns:8}} നാമമേഖലയിലെ ബന്ധപ്പെട്ട വ്യവസ്ഥാസന്ദേശങ്ങൾ തിരുത്തി മാറ്റാവുന്നതാണ്.",
        "trackingcategories-msg": "പിന്തുടരൽ വർഗ്ഗം",
        "wlshowhideanons": "അജ്ഞാത ഉപയോക്താക്കൾ",
        "wlshowhidepatr": "റോന്തു ചുറ്റിയ മാറ്റങ്ങൾ",
        "wlshowhidemine": "എന്റെ തിരുത്തുകൾ",
+       "wlshowhidecategorization": "താൾ വർഗ്ഗീകരണം",
        "watchlist-options": "ശ്രദ്ധിക്കുന്ന താളുകളുടെ സജ്ജീകരണങ്ങൾ",
        "watching": "ശ്രദ്ധിക്കുന്നു...",
        "unwatching": "അവഗണിക്കുന്നു...",
        "rollback-success": "$1 ചെയ്ത തിരുത്ത് തിരസ്ക്കരിച്ചിരിക്കുന്നു; $2 ചെയ്ത തൊട്ടു മുൻപത്തെ പതിപ്പിലേക്ക് സേവ് ചെയ്യുന്നു.",
        "sessionfailure-title": "സെഷൻ പരാജയപ്പെട്ടിരിക്കുന്നു",
        "sessionfailure": "താങ്കളുടെ ലോഗിൻ സെഷനിൽ പ്രശ്നങ്ങളുള്ളതായി കാണുന്നു;\nസെഷൻ തട്ടിയെടുക്കൽ ഒഴിവാക്കാനുള്ള മുൻകരുതലായി ഈ പ്രവൃത്തി റദ്ദാക്കിയിരിക്കുന്നു.\nദയവായി പിന്നോട്ട് പോയി താങ്കൾ വന്ന താളിൽ ചെന്ന്, വീണ്ടും ശ്രമിക്കുക.",
+       "changecontentmodel": "താളിന്റെ ഉള്ളടക്ക രീതി തിരുത്തുക",
        "changecontentmodel-title-label": "താളിന്റെ തലക്കെട്ട്",
        "changecontentmodel-model-label": "പുതിയ ഉള്ളടക്ക രീതി",
        "changecontentmodel-reason-label": "കാരണം:",
        "unblock": "ഉപയോക്താവിനുള്ള തടയൽ നീക്കുക",
        "blockip": "{{GENDER:$1|ഉപയോക്താവിനെ}} തടയുക",
        "blockip-legend": "ഉപയോക്താവിനെ തടയുക",
-       "blockiptext": "ഏതെങ്കിലും ഐ.പി. വിലാസത്തേയോ ഉപയോക്താവിനേയോ തടയുവാൻ താഴെയുള്ള ഫോം ഉപയോഗിക്കുക.\n[[{{MediaWiki:Policy-url}}|വിക്കിയുടെ നയം]] അനുസരിച്ച് നശീകരണപ്രവർത്തനം തടയാൻ മാത്രമേ ഇതു ചെയ്യാവൂ.\nതടയാനുള്ള വ്യക്തമായ കാരണം (ഏതു താളിലാണു നശീകരണപ്രവർത്തനം നടന്നത് എന്നതടക്കം) താഴെ രേഖപ്പെടുത്തിയിരിക്കണം.",
+       "blockiptext": "ഏതെങ്കിലും ഐ.പി. വിലാസത്തേയോ ഉപയോക്താവിനേയോ തടയുവാൻ താഴെയുള്ള ഫോം ഉപയോഗിക്കുക.\n[[{{MediaWiki:Policy-url}}|വിക്കിയുടെ നയം]] അനുസരിച്ച് നശീകരണപ്രവർത്തനം തടയാൻ മാത്രമേ ഇതു ചെയ്യാവൂ.\nതടയാനുള്ള വ്യക്തമായ കാരണം (ഏതു താളിലാണു നശീകരണപ്രവർത്തനം നടന്നത് എന്നതടക്കം) താഴെ രേഖപ്പെടുത്തിയിരിക്കണം.\n[https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing സി.ഐ.ഡി.ആർ.] എഴുത്തുരീതി ഉപയോഗിച്ച് താങ്കൾക്ക് ഐ.പി. റേഞ്ചുകൾ തടയാൻ കഴിയുന്നതാണ്;  /$1  ആണ് ഐ.പി.v4-നു അനുവദിച്ചിരിക്കുന്ന വലിയ റേഞ്ച്,  /$2  ആണ് ഐ.പി.v6-നു അനുവദിച്ചിരിക്കുന്ന വലിയ റേഞ്ച്.",
        "ipaddressorusername": "ഐ.പി. വിലാസം അല്ലെങ്കിൽ ഉപയോക്തൃനാമം:",
        "ipbexpiry": "കാലാവധി:",
        "ipbreason": "കാരണം:",
        "export-download": "ഒരു പ്രമാണമാക്കി സൂക്ഷിക്കുക",
        "export-templates": "ഫലകങ്ങളും ഉൾപ്പെടുത്തുക",
        "export-pagelinks": "ഉൾപ്പെടുത്തേണ്ട കണ്ണികളുള്ള താളുകളുടെ ആഴം:",
+       "export-manual": "താളുകൾ ചേർക്കുക:",
        "allmessages": "സന്ദേശസഞ്ചയം",
        "allmessagesname": "പേര്‌",
        "allmessagesdefault": "സ്വതേയുള്ള ഉള്ളടക്കം",
        "javascripttest-pagetext-frameworks": "താഴെക്കൊടുത്തിരിക്കുന്നവയിൽ ഒരു പരീക്ഷണ ചട്ടക്കൂട് തിരഞ്ഞെടുക്കുക: $1",
        "javascripttest-pagetext-skins": "പരീക്ഷണങ്ങൾ നടത്താനുള്ള ദൃശ്യരൂപം തിരഞ്ഞെടുക്കുക:",
        "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-anoncontribs": "ഈ ഐ.പി. വിലാസത്തിൽ നിന്നും ചെയ്തിട്ടുള്ള തിരുത്തുകളുടെ പട്ടിക",
        "tooltip-pt-login": "ലോഗിൻ ചെയ്യുവാൻ താല്പര്യപ്പെടുന്നു; പക്ഷേ നിർബന്ധമല്ല",
        "tooltip-pt-logout": "ലോഗൗട്ട് ചെയ്യാനുള്ള കണ്ണി",
        "tooltip-t-recentchangeslinked": "താളുകളിലെ പുതിയ മാറ്റങ്ങൾ",
        "tooltip-feed-rss": "ഈ താളിന്റെ ആർ.എസ്.എസ്. ഫീഡ്",
        "tooltip-feed-atom": "ഈ താളിന്റെ ആറ്റം ഫീഡ്",
-       "tooltip-t-contributions": "à´\89പയàµ\8bà´\95àµ\8dതാവിനàµ\8dà´±àµ\86 à´¸à´\82ഭാവനà´\95à´³àµ\81à´\9fàµ\86 à´ªà´\9fàµ\8dà´\9fà´¿à´\95 à´\95ാണàµ\81ക",
-       "tooltip-t-emailuser": "ഈ ഉപയോക്താവിനു ഇമെയിൽ അയക്കുക",
+       "tooltip-t-contributions": "à´\88 {{GENDER:$1|à´\89പയàµ\8bà´\95àµ\8dതാവിനàµ\8dà´±àµ\86}} à´¸à´\82ഭാവനà´\95à´³àµ\81à´\9fàµ\86 à´ªà´\9fàµ\8dà´\9fà´¿ക",
+       "tooltip-t-emailuser": "{{GENDER:$1|ഈ ഉപയോക്താവിനു}} ഇമെയിൽ അയക്കുക",
        "tooltip-t-info": "ഈ താളിനെക്കുറിച്ചുള്ള കൂടുതൽ വിവരങ്ങൾ",
        "tooltip-t-upload": "പ്രമാണങ്ങൾ അപ്‌ലോഡ് ചെയ്യുവാൻ",
        "tooltip-t-specialpages": "പ്രത്യേകതാളുകളുടെ പട്ടിക",
        "pageinfo-category-files": "പ്രമാണങ്ങളുടെ എണ്ണം",
        "markaspatrolleddiff": "റോന്തുചുറ്റിയതായി അടയാളപ്പെടുത്തുക",
        "markaspatrolledtext": "ഈ താളിൽ റോന്തുചുറ്റിയതായി രേഖപ്പെടുത്തുക",
+       "markaspatrolledtext-file": "പ്രമാണത്തിന്റെ ഈ പതിപ്പിൽ റോന്തുചുറ്റിയതായി അടയാളപ്പെടുത്തുക",
        "markedaspatrolled": "റോന്തുചുറ്റിയതായി രേഖപ്പെടുത്തിയിരിക്കുന്നു",
        "markedaspatrolledtext": "[[:$1]] എന്ന താളിന്റെ തിരഞ്ഞെടുത്ത നാൾപ്പതിപ്പിൽ റോന്തുചുറ്റിയതായി രേഖപ്പെടുത്തിയിരിക്കുന്നു",
        "rcpatroldisabled": "പുതിയ മാറ്റങ്ങളുടെ റോന്തുചുറ്റൽ ദുർബലപ്പെടുത്തിയിരിക്കുന്നു",
        "newimages-legend": "അരിപ്പ",
        "newimages-label": "പ്രമാണത്തിന്റെ പേര്‌ (അഥവാ പേരിന്റെ ഭാഗം)",
        "newimages-showbots": "യന്ത്രങ്ങൾ ചെയ്ത അപ്‌ലോഡുകൾ പ്രദർശിപ്പിക്കുക",
+       "newimages-hidepatrolled": "റോന്തുചുറ്റപ്പെട്ട അപ്‌ലോഡുകൾ മറയ്ക്കുക",
        "noimages": "ഒന്നും കാണാനില്ല.",
        "ilsubmit": "തിരയൂ",
        "bydate": "ദിനക്രമത്തിൽ",
        "scarytranscludefailed-httpstatus": "[$1-നു ഫലകം എടുക്കാൻ കഴിഞ്ഞില്ല: എച്ച്.റ്റി.റ്റി.പി. $2]",
        "scarytranscludetoolong": "[വളരെ നീളക്കൂടുതലുള്ള യൂ.ആർ.എൽ.]",
        "deletedwhileediting": "'''മുന്നറിയിപ്പ്''': താങ്കൾ തിരുത്തുവാൻ തുടങ്ങിയ ശേഷം താൾ മായ്ക്കപ്പെട്ടിരിക്കുന്നു!",
-       "confirmrecreate": "താà´\99àµ\8dà´\95ൾ à´\88 à´¤à´¾àµ¾ à´¤à´¿à´°àµ\81à´¤àµ\8dതാൻ à´¤àµ\81à´\9fà´\99àµ\8dà´\99ിയതിനàµ\81à´¶àµ\87à´·à´\82 [[User:$1|$1]] ([[User talk:$1|talk]]) à´\8eà´¨àµ\8dà´¨ à´\89പയàµ\8bà´\95àµ\8dതാവàµ\8d à´\87à´\99àµ\8dà´\99à´¨àµ\86 à´\92à´°àµ\81 à´\95ാരണà´\82 à´¨àµ½à´\95à´¿ à´\88 à´¤à´¾àµ¾ à´¨àµ\80à´\95àµ\8dà´\95à´\82 à´\9aàµ\86à´¯àµ\8dà´¤àµ\81:\n: ''$2''\nദയവായി താൾ പുനഃസൃഷ്ടിക്കേണ്ടതുണ്ടോ എന്ന് സ്ഥിരീകരിക്കുക.",
-       "confirmrecreate-noreason": "താങ്കൾ തിരുത്താനാരംഭിച്ചതിനു ശേഷം, ഉപയോക്താവ് [[User:$1|$1]] ([[User talk:$1|സംവാദം]]) ഈ താൾ മായ്ച്ചിരിക്കുന്നു. ഈ താൾ പുനഃസൃഷ്ടിക്കണം എന്നത് സ്ഥിരീകരിക്കുക.",
+       "confirmrecreate": "താà´\99àµ\8dà´\95ൾ à´¤à´¿à´°àµ\81à´¤àµ\8dതാൻ à´¤àµ\81à´\9fà´\99àµ\8dà´\99ിയതിനàµ\81à´¶àµ\87à´·à´\82 [[User:$1|$1]] ([[User talk:$1|à´¸à´\82വാദà´\82]]) à´\8eà´¨àµ\8dà´¨ à´\89പയàµ\8bà´\95àµ\8dതാവàµ\8d à´\87à´\99àµ\8dà´\99à´¨àµ\86 à´\92à´°àµ\81 à´\95ാരണà´\82 à´¨àµ½à´\95à´¿ à´\88 à´¤à´¾àµ¾ {{GENDER:$1|à´¨àµ\80à´\95àµ\8dà´\95à´\82 à´\9aàµ\86à´¯àµ\8dà´¤àµ\81}}:\n: ''$2''\nദയവായി താൾ പുനഃസൃഷ്ടിക്കേണ്ടതുണ്ടോ എന്ന് സ്ഥിരീകരിക്കുക.",
+       "confirmrecreate-noreason": "താങ്കൾ തിരുത്താനാരംഭിച്ചതിനു ശേഷം, ഉപയോക്താവ് [[User:$1|$1]] ([[User talk:$1|സംവാദം]]) ഈ താൾ {{GENDER:$1|മായ്ച്ചിരിക്കുന്നു}}. ഈ താൾ പുനഃസൃഷ്ടിക്കണം എന്നത് സ്ഥിരീകരിക്കുക.",
        "recreate": "പുനഃസൃഷ്ടിക്കുക",
        "unit-pixel": "ബിന്ദു",
        "confirm_purge_button": "ശരി",
        "watchlisttools-edit": "ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടിക കാണുക, തിരുത്തുക",
        "watchlisttools-raw": "താങ്കൾ ശ്രദ്ധിക്കുന്ന താളുകളുടെ പട്ടികയുടെ മൂലരൂപം തിരുത്തുക",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|സംവാദം]])",
+       "timezone-local": "പ്രാദേശികം",
        "duplicate-defaultsort": "'''മുന്നറിയിപ്പ്:''' ക്രമപ്പെടുത്താനുള്ള ചാവിയായ \"$2\" മുമ്പ് ക്രമപ്പെടുത്താനുള്ള ചാവിയായിരുന്ന \"$1\" എന്നതിനെ അതിലംഘിക്കുന്നു.",
        "duplicate-displaytitle": "<strong>മുന്നറിയിപ്പ്:</strong> പ്രദർശിപ്പിക്കുന്ന തലക്കെട്ട് \"$2\" മുമ്പ് പ്രദർശിപ്പിച്ചിരുന്ന തലക്കെട്ട് \"$1\" എന്നതിനെ അതിലംഘിക്കുന്നു.",
        "version": "പതിപ്പ്",
        "version-libraries-license": "ഉപയോഗാനുമതി",
        "version-libraries-description": "വിവരണം",
        "version-libraries-authors": "രചയിതാക്കൾ",
-       "redirect": "പ്രമാണത്താൽ, ഉപയോക്താവിനാൽ, താളിനാൽ അഥവാ നാൾപ്പതിപ്പ് ഐ.ഡി.യാൽ ചെയ്യുന്ന തിരിച്ചുവിടൽ",
+       "redirect": "പ്രമാണത്താൽ, ഉപയോക്താവിനാൽ, താളിനാൽ, നാൾപ്പതിപ്പിനാൽ അല്ലെങ്കിൽ രേഖയുടെ ഐ.ഡി.യാൽ ചെയ്യുന്ന തിരിച്ചുവിടൽ",
        "redirect-legend": "ഒരു പ്രമാണത്തിലോട്ടോ താളിലോട്ടോ ഉള്ള തിരിച്ചുവിടൽ",
-       "redirect-summary": "à´\88 à´ªàµ\8dà´°à´¤àµ\8dà´¯àµ\87à´\95 à´¤à´¾àµ¾ à´\92à´°àµ\81 à´ªàµ\8dരമാണതàµ\8dതിലàµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\8b (à´ªàµ\8dരമാണതàµ\8dതിനàµ\8dà´±àµ\86 à´ªàµ\87à´°àµ\8d à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d), à´\92à´°àµ\81 à´¤à´¾à´³à´¿à´²àµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\8b (നാൾപàµ\8dപതിപàµ\8dപിനàµ\8dà´±àµ\86 à´\90.à´¡à´¿. à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ à´¤à´¾àµ¾ à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d), à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ à´\92à´°àµ\81 à´\89പയàµ\8bà´\95àµ\8dà´¤àµ\83താളിലàµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\8b (à´\89പയàµ\8bà´\95àµ\8dതാവിനàµ\8dà´±àµ\86 à´¸à´\82à´\96àµ\8dയാ à´\90.à´¡à´¿. à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d) à´¤à´¿à´°à´¿à´\9aàµ\8dà´\9aàµ\81വിà´\9fàµ\81à´¨àµ\8dà´¨àµ\81. à´\89പയàµ\8bà´\97à´\82: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],  [[{{#Special:Redirect}}/revision/328429]], à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "à´\88 à´ªàµ\8dà´°à´¤àµ\8dà´¯àµ\87à´\95 à´¤à´¾àµ¾ à´\92à´°àµ\81 à´ªàµ\8dരമാണതàµ\8dതിലàµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\8b (à´ªàµ\8dരമാണതàµ\8dതിനàµ\8dà´±àµ\86 à´ªàµ\87à´°àµ\8d à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d), à´\92à´°àµ\81 à´¤à´¾à´³à´¿à´²àµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\8b (നാൾപàµ\8dപതിപàµ\8dപിനàµ\8dà´±àµ\86 à´\90.à´¡à´¿. à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ à´¤à´¾àµ¾ à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d), à´\92à´°àµ\81 à´\89പയàµ\8bà´\95àµ\8dà´¤àµ\83താളിലàµ\87à´¯àµ\8dà´\95àµ\8dà´\95àµ\8b (à´\89പയàµ\8bà´\95àµ\8dതാവിനàµ\8dà´±àµ\86 à´¸à´\82à´\96àµ\8dയാ à´\90.à´¡à´¿. à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d), à´\92à´°àµ\81 à´°àµ\87à´\96യിലàµ\86 à´\89ൾപàµ\8dà´ªàµ\86à´\9fàµ\81à´¤àµ\8dതലിലàµ\87à´\95àµ\8dà´\95àµ\8b (à´°àµ\87à´\96à´¯àµ\81à´\9fàµ\86 à´\90.à´¡à´¿. à´¤à´¨àµ\8dനിà´\9fàµ\8dà´\9fàµ\81à´£àµ\8dà´\9fàµ\8d) à´¤à´¿à´°à´¿à´\9aàµ\8dà´\9aàµ\81വിà´\9fàµ\81à´¨àµ\8dà´¨àµ\81. à´\89പയàµ\8bà´\97à´\82: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]],  [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] à´\85à´²àµ\8dà´²àµ\86à´\99àµ\8dà´\95ിൽ [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "പോകൂ",
        "redirect-lookup": "തേടുക:",
        "redirect-value": "വില:",
        "redirect-page": "താളിന്റെ ഐ.ഡി.",
        "redirect-revision": "താളിന്റെ നാൾപ്പതിപ്പ്",
        "redirect-file": "പ്രമാണത്തിന്റെ പേര്",
+       "redirect-logid": "രേഖയുടെ ഐ.ഡി.",
        "redirect-not-exists": "വില കണ്ടെത്താൻ കഴിഞ്ഞില്ല",
        "fileduplicatesearch": "ഒരേ പ്രമാണത്തിന്റെ പലപകർപ്പുകളുണ്ടോയെന്നു തിരയുക",
        "fileduplicatesearch-summary": "ഒരേ പ്രമാണം തന്നെ വിവിധ പേരിലുണ്ടോയെന്നു ഹാഷ് വാല്യൂവധിഷ്ഠിതമായി തിരയുക.",
        "logentry-suppress-block": "$5 $6 കാലത്തേക്ക് {{GENDER:$4|$3}} എന്ന അംഗത്വത്തെ $1 {{GENDER:$2|തടഞ്ഞിരിക്കുന്നു}}",
        "logentry-suppress-reblock": "$5 $6 കാലത്തേക്ക് {{GENDER:$4|$3}} എന്ന അംഗത്വത്തിന്റെ തടയൽ സജ്ജീകരണങ്ങൾ $1 {{GENDER:$2|മാറ്റിയിരിക്കുന്നു}}",
        "logentry-import-upload": "പ്രമാണ അപ്‌ലോഡ് വഴി $3 എന്ന താൾ $1 {{GENDER:$2|ഇറക്കുമതി ചെയ്തിരിക്കുന്നു}}",
+       "logentry-import-upload-details": "പ്രമാണം അപ്‌ലോഡ് ചെയ്യുക വഴി $3 ($4 {{PLURAL:$4|നാൾപ്പതിപ്പ്|നാൾപ്പതിപ്പുകൾ}}) $1 {{GENDER:$2|ഇറക്കുമതി ചെയ്തിരിക്കുന്നു}}",
        "logentry-import-interwiki": "മറ്റൊരു വിക്കിയിൽ നിന്നും $3 എന്ന താൾ $1 {{GENDER:$2|ഇറക്കുമതി ചെയ്തിരിക്കുന്നു}}",
+       "logentry-import-interwiki-details": "$5 വിക്കിയിൽ നിന്നും $3 ($4 {{PLURAL:$4|നാൾപ്പതിപ്പ്|നാൾപ്പതിപ്പുകൾ}}) $1 {{GENDER:$2|ഇറക്കുമതി ചെയ്തിരിക്കുന്നു}}",
        "logentry-merge-merge": "$3 എന്ന താൾ $4 എന്നതിലേക്ക് ($5 നാൾപ്പതിപ്പ് വരെ), $1 {{GENDER:$2|ലയിപ്പിച്ചു}}",
        "logentry-move-move": "$1 എന്ന ഉപയോക്താവ് $3 എന്ന താൾ $4 എന്നാക്കി {{GENDER:$2|മാറ്റിയിരിക്കുന്നു}}",
        "logentry-move-move-noredirect": "$3 എന്ന താൾ $4 എന്ന തലക്കെട്ടിലേയ്ക്ക് തിരിച്ചുവിടലില്ലാതെ $1 {{GENDER:$2|മാറ്റി}}",
        "expand_templates_preview": "എങ്ങനെയുണ്ടെന്നു കാണുക",
        "expand_templates_preview_fail_html": "<em>{{SITENAME}} സംരംഭത്തിൽ അസംസ്കൃത എച്ച്.റ്റി.എം.എൽ സജ്ജമാക്കിയിരിക്കുന്നതിനാലും, സെഷൻ വിവരങ്ങൾ നഷ്ടപ്പെട്ടിരിക്കുന്നതിനാലും, ജാവാസ്ക്രിപ്റ്റ് ആക്രമണങ്ങൾക്കെതിരെയുള്ള മുൻകരുതൽ എന്ന നിലയിൽ എങ്ങനെയുണ്ടെന്ന് കാണൽ മറച്ചിരിക്കുകയാണ്.</em>\n\n<strong>ഇത് എങ്ങനെയുണ്ടെന്ന് കാണാനുള്ള യഥാർത്ഥശ്രമമാണെങ്കിൽ വീണ്ടും ശ്രമിക്കുക.</strong>\nഇപ്പോഴും പ്രവർത്തിക്കുന്നില്ലെങ്കിൽ, [[Special:UserLogout|പുറത്ത് കടന്ന്]] വീണ്ടും പ്രവേശിച്ച ശേഷം പരീക്ഷിക്കുക.",
        "expand_templates_preview_fail_html_anon": "<em>{{SITENAME}} സംരംഭത്തിൽ അസംസ്കൃത എച്ച്.റ്റി.എം.എൽ സജ്ജമാക്കിയിരിക്കുന്നതിനാലും, സെഷൻ വിവരങ്ങൾ നഷ്ടപ്പെട്ടിരിക്കുന്നതിനാലും, ജാവാസ്ക്രിപ്റ്റ് ആക്രമണങ്ങൾക്കെതിരെയുള്ള മുൻകരുതൽ എന്ന നിലയിൽ എങ്ങനെയുണ്ടെന്ന് കാണൽ മറച്ചിരിക്കുകയാണ്.</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 ബൈറ്റ്}} ($2; $3%)",
+       "mediastatistics-bytespertype": "ഈ ഭാഗത്തിന്റെ ആകെ പ്രമാണ വലിപ്പം: {{PLURAL:$1|$1 ബൈറ്റ്|$1 ബൈറ്റുകൾ}} ($2; $3%).",
+       "mediastatistics-allbytes": "എല്ലാ പ്രമാണങ്ങളുടേയും ആകെ പ്രമാണവലിപ്പം: {{PLURAL:$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-error-unknown": "ജെസണിൽ ഒരു പ്രശ്നമുണ്ടായി. പിഴവ്: $1",
        "json-error-depth": "സ്റ്റാക്കിന്റെ പരമാവധി ആഴം അധികരിച്ചിരിക്കുന്നു",
        "mw-widgets-dateinput-no-date": "തീയതി ഒന്നും തിരഞ്ഞെടുത്തിട്ടില്ല",
        "mw-widgets-titleinput-description-new-page": "താൾ ഇപ്പോൾ നിലവിലില്ല",
        "mw-widgets-titleinput-description-redirect": "$1 എന്ന താളിലേക്കുള്ള തിരിച്ചുവിടൽ",
-       "api-error-blacklisted": "ദയവായി മറ്റൊരു വിവരണാത്മകമായ തലക്കെട്ട് തിരഞ്ഞെടുക്കുക."
+       "api-error-blacklisted": "ദയവായി മറ്റൊരു വിവരണാത്മകമായ തലക്കെട്ട് തിരഞ്ഞെടുക്കുക.",
+       "sessionprovider-generic": "$1 സെഷനുകൾ",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "കൂക്കി-അധിഷ്ഠിത സെഷനുകൾ",
+       "sessionprovider-nocookies": "കൂക്കികൾ സജ്ജമല്ലായിരിക്കാം. കൂക്കികൾ സജ്ജമാണോയെന്ന് ഉറപ്പാക്കിയ ശേഷം വീണ്ടും തുടങ്ങുക.",
+       "randomrootpage": "മൂലതാൾ ക്രമരഹിതമായി നൽകുക"
 }
index 958abe2..338ed14 100644 (file)
        "october-date": "ऑक्टोबर $1",
        "november-date": "नोव्हेंबर $1",
        "december-date": "डिसेंबर $1",
+       "period-am": "मा.पू.",
+       "period-pm": "मा.नं.",
        "pagecategories": "{{PLURAL:$1|वर्ग}}",
        "category_header": "\"$1\" वर्गातील लेख",
        "subcategories": "उपवर्ग",
        "laggedslavemode": "'''सुचना:''' पानावर अद्ययावत बदल नसतील.",
        "readonly": "विदागारास (डाटाबेस) ताळे आहे.",
        "enterlockreason": "विदागारास ताळे ठोकण्याचे कारण, ताळे उघडले जाण्याच्या अदमासे कालावधीसहीत द्या.",
-       "readonlytext": "बहुधा विदागार परिरक्षणामुळे (मेंटेनन्स) नवीन भर घालण्यापासून आणि इतर बदल करण्यापासून बंद ठेवण्यात आला आहे, परिरक्षणानंतर तो सामान्य होईल.\n\nताळे ठोकणाऱ्या प्रबंधकांनी खालील स्पष्टीकरण नमूद केले आहे: $1",
+       "readonlytext": "बहà¥\81धा à¤µà¤¿à¤¦à¤¾à¤\97ार à¤ªà¤°à¤¿à¤°à¤\95à¥\8dषणामà¥\81ळà¥\87 (मà¥\87à¤\82à¤\9fà¥\87ननà¥\8dस) à¤¨à¤µà¥\80न à¤­à¤° à¤\98ालणà¥\8dयापासà¥\82न à¤\86णि à¤\87तर à¤¬à¤¦à¤² à¤\95रणà¥\8dयापासà¥\82न à¤¬à¤\82द à¤ à¥\87वणà¥\8dयात à¤\86ला à¤\86हà¥\87, à¤ªà¤°à¤¿à¤°à¤\95à¥\8dषणानà¤\82तर à¤¤à¥\8b à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤¹à¥\8bà¤\88ल.\n\nताळà¥\87 à¤ à¥\8bà¤\95णाऱà¥\8dया à¤ªà¥\8dरणालà¥\80 à¤ªà¥\8dरबà¤\82धà¤\95ाà¤\82नà¥\80 à¤\96ालà¥\80ल à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¨à¤®à¥\82द à¤\95à¥\87लà¥\87 à¤\86हà¥\87: $1",
        "missing-article": "डाटाबेसला \"$1\" $2 नावाचे पान मिळालेले नाही, जे मिळायला हवे होते.\n\nअसे बहुदा संपुष्टात आलेल्या फरकामुळे किंवा वगळलेल्या पानाच्या इतिहास दुव्यामुळे घडते.\n\nजर असे घडलेले नसेल, तर तुम्हाला प्रणाली मधील त्रुटी आढळलेली असू शकते.\nकृपया याबद्दल एखाद्या [[Special:ListUsers/sysop|प्रचालकाशी]] चर्चा करा व या URLची नोंद करा.",
        "missingarticle-rev": "(आवृत्ती#: $1)",
        "missingarticle-diff": "(फरक: $1, $2)",
        "mypreferencesprotected": "आपणास आपला पसंतीक्रम बदलण्याची परवानगी नाही.",
        "ns-specialprotected": "विशेष पाने संपादित करता येत नाहीत.",
        "titleprotected": "या शीर्षकाचे पान सदस्य [[User:$1|$1]]ने निर्मितीपासून सुरक्षित केलेले आहे.त्याने याचे \"\"$2\"\" हे कारण नमूद केलेले आहे.",
-       "filereadonlyerror": "\"$1\" à¤²à¤¾ à¤¸à¥\81धार à¤\85शà¤\95à¥\8dय à¤\86हà¥\87 à¤\95ारण à¤¸à¤\82à¤\9aिà¤\95ाभाà¤\82डार  \"$2\" à¤¹à¥\87 'फà¤\95à¥\8dत à¤µà¤¾à¤\9aा'(रà¥\80ड à¤\93नà¥\8dलà¥\80) à¤¯à¤¾ à¤¶à¥\8dरà¥\87णà¥\80तà¤\9a à¤\86हà¥\87.\n\nà¤\9cà¥\8dया à¤ªà¥\8dरशासà¤\95ानà¥\87 à¤¹à¥\87 à¤\95à¥\81लà¥\81पबà¤\82द à¤\95à¥\87लà¥\87 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¦à¤¿à¤²à¥\87लà¥\87 à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤\86हà¥\87: \"$3\"",
+       "filereadonlyerror": "\"$1\" à¤¸à¤\82à¤\9aिà¤\95à¥\87à¤\9aा à¤¸à¥\81धार à¤\85शà¤\95à¥\8dय à¤\86हà¥\87 à¤\95ारण à¤¸à¤\82à¤\9aिà¤\95ाभाà¤\82डार  \"$2\" à¤¹à¥\87 'फà¤\95à¥\8dत à¤µà¤¾à¤\9aा'(रà¥\80ड à¤\93नà¥\8dलà¥\80) à¤¯à¤¾ à¤¸à¥\8dथितà¥\80तà¤\9a à¤\86हà¥\87.\n\nà¤\9cà¥\8dया à¤ªà¥\8dरशासà¤\95ानà¥\87 à¤¹à¥\87 à¤\95à¥\81लà¥\81पबà¤\82द à¤\95à¥\87लà¥\87 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¤à¥\8dयाà¤\82नà¥\80 à¤¦à¤¿à¤²à¥\87लà¥\87 à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤\86हà¥\87: \"$3\".",
        "invalidtitle-knownnamespace": "\"$2\" नामविश्वात \"$3\" मजकूराचे अयोग्य शीर्षक",
        "invalidtitle-unknownnamespace": "अनोळखी नामविश्वाच्या आकड्यासह अवैध मथळा $1 व मजकूर \"$2\"",
        "exception-nologin": "सनोंद-प्रवेशित नाही",
        "virus-scanfailed": "क्रमवीक्षण (स्कॅन) अयशस्वी (कोड $1)",
        "virus-unknownscanner": "अनोळखी ऍन्टीव्हायरस:",
        "logouttext": "'''तुम्ही आता सनोंद-निर्गमित(लॉग-आउट) झाला आहात.'''\n\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": "विदागार ’खातरजमा’ (प्रमाणितीकरण) त्रुटी होती अथवा तुम्हाला तुमचे बाह्य खाते अद्ययावत  करण्याची परवानगी नाही.",
        "resetpass_submit": "परवलीचा शब्द टाका आणि सनोंद-प्रवेश करा",
        "changepassword-success": "तुमचा परवलीचा शब्द यशस्वीरित्या बदललेला आहे!",
        "changepassword-throttled": "तुम्ही नुकतेच सनोंद- प्रवेशासाठी अनेकानेक प्रयत्न केले आहेत.\nकृपया, पुन्हा प्रयत्न करण्याआधी $1 थोडी उसंत घ्या.",
+       "botpasswords": "सांगकाम्याचे परवलीचे शब्द",
+       "botpasswords-summary": "<em>सांगकाम्याचे परवलीचे शब्द</em>हे त्या खात्याची मुख्य सनोंद-प्रवेश अधिकारपत्रे न वापरता, एपीआय मार्फत, सदस्य खात्याच्या प्रवेशास पोहोच देतात.सांगकाम्याचा परवलीचा शब्द वापरुन सनोंद प्रवेश केलेल्यांचे उपलब्ध सदस्य अधिकार प्रतिबंधित असू शकतात.\n\nजर आपणास कळत नसेल आपण हे कां करीत आहोत,तर आपण ते बहुतेक करावयास नको.कोणीही आपणास असे कधीही सांगु नये कि यापैकी एखादे उत्पादित करा व त्यांना द्या.",
+       "botpasswords-disabled": "सांगकाम्यांचे परवलीचे शब्द अक्षम केले आहेत.",
+       "botpasswords-no-central-id": "सांगकाम्याचा परवलीचा शब्द वापरण्यास, आपण केंद्रीय खात्यात सनोंद प्रवेशित हवेत.",
+       "botpasswords-existing": "अस्तित्वात असलेला सांगकाम्याचा परवलीचा शब्द",
+       "botpasswords-createnew": "सांगकाम्याचा परवलीचा शब्द नविन तयार करा",
+       "botpasswords-editexisting": "संपादन व अस्तित्वात असलेला सांगकाम्याचा परवलीचा शब्द",
+       "botpasswords-label-appid": "सांगकाम्याचे नाव:",
+       "botpasswords-label-create": "तयार करा",
+       "botpasswords-label-update": "अद्यतन करा",
+       "botpasswords-label-cancel": "रद्द करा",
+       "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": "सांगकाम्याचा परवलीचा शब्द तयार केल्या गेला.",
+       "botpasswords-created-body": "\"$1\" हा सांगकाम्याचा परवलीचा शब्द यशस्वीरित्या तयार केल्या गेला.",
+       "botpasswords-updated-title": "सांगकाम्याचा परवलीचा शब्द अद्यतन केला",
+       "botpasswords-updated-body": "\"$1\" हा सांगकाम्याचा परवलीचा शब्द यशस्वीरित्या अद्यतन केल्या गेला.",
+       "botpasswords-deleted-title": "सांगकाम्याचा परवलीचा शब्द वगळला",
+       "botpasswords-deleted-body": "सांगकाम्याचा परवलीचा शब्द \"$1\" वगळला.",
+       "botpasswords-newpassword": "<strong>$1</strong>द्वारे सनोंद प्रवेशास नविन परवलीचा शब्द <strong>$2</strong>आहे. <em>कृपया याचा भविष्यातील संदर्भासाठी वापर करा.</em>",
+       "botpasswords-no-provider": "सांगकाम्यापरवलीशब्दसत्रपुरवठादार उपलब्ध नाही.",
+       "botpasswords-restriction-failed": "सांगकाम्या परवलीच्या शब्दावर असलेले प्रतिबंध या सनोंद प्रवेशास अटकाव करीत आहेत.",
+       "botpasswords-invalid-name": "नमूद केलेल्या सदस्यनावात सांगकाम्याचा परवलीचा शब्द-विभाजक (\"$1\") नाही.",
+       "botpasswords-not-exist": "सदस्य \"$1\" चा \"$2\" नावाचा सांगकाम्या परवलीचा शब्द नाही.",
        "resetpass_forbidden": "परवलीचे शब्द बदलता येत नाहीत.",
        "resetpass-no-info": "या पानामध्ये थेट जाण्यासाठी तुम्हास  सनोंद-प्रवेशित असावयास हवे.",
        "resetpass-submit-loggedin": "परवलीचा शब्द बदला",
        "passwordreset-emailtext-ip": "कुणीतरी (कदाचित तुम्ही, अंकपत्ता $1 वरुन) {{SITENAME}}($4) करिता नविन 'परवलीचा शब्द' पुनर्स्थापनेबद्दल विनंती केली आहे.\nखालील{{PLURAL:$3|सदस्यखाते}}या विपत्रपत्त्याशी निगडीत आहे: \n\"$2\"\n{{PLURAL:$3|हा तात्पुरता परवलीचा शब्द|हे तात्पुरते परवलीचे शब्द}}{{PLURAL:$5|एक दिवस|$5 दिवसात}} मुदतबाह्य होतील.आता आपण लॉग-ईन करून  नविन परवलीचा शब्द निवडा.जर ईतर कोणी ही विनंती केली असेल,किंवा जर आपणास परवलीच शब्द आठवला असेल तर,व जर आपण तो बदलु इच्छित नसाल तर आपण हा संदेश टाळा व आपला जुना परवलीचा शब्द वापरणे सुरू ठेवा.",
        "passwordreset-emailtext-user": " {{SITENAME}}वरील सदस्य $1ने {{SITENAME}}($4) करिता नविन 'परवलीचा शब्द' पुनर्स्थापनेबद्दल विनंती केली आहे.\nखालील{{PLURAL:$3|सदस्यखाते}}या विपत्रपत्त्याशी निगडीत आहे: \n\n\"$2\"\n\n{{PLURAL:$3|हा तात्पुरता परवलीचा शब्द|हे तात्पुरते परवलीचे शब्द}}{{PLURAL:$5|एक दिवस|$5 दिवसात}} मुदतबाह्य होतील.आता आपण लॉग-ईन करून  नविन परवलीचा शब्द निवडा.जर ईतर कोणी ही विनंती केली असेल,किंवा जर आपणास परवलीच शब्द आठवला असेल तर,व, जर आपण तो बदलु इच्छित नसाल तर आपण हा संदेश टाळा व आपला जुना परवलीचा शब्द वापरणे सुरू ठेवा.",
        "passwordreset-emailelement": "सदस्यनाव: \n$1\n\nअस्थायी परवलीचा शब्द: \n$2",
-       "passwordreset-emailsentemail": "à¤\9cर à¤¹à¤¾ à¤\86पलà¥\8dया à¤\96ातà¥\8dयाà¤\9aा à¤¨à¥\8bà¤\82दणिà¤\95à¥\83त à¤µà¤¿à¤ªà¤¤à¥\8dरपतà¥\8dता असेल तर, परवलीच्या शब्दाच्या पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात येईल.",
-       "passwordreset-emailsentusername": "à¤\9cर à¤¤à¤¦à¤¨à¥\81रà¥\82प à¤¨à¥\8bà¤\82दà¥\80à¤\95à¥\83त à¤µà¤¿à¤ªà¤¤à¥\8dरपतà¥\8dता à¤\85सà¥\87ल à¤¤à¤°, à¤ªà¤°à¤µà¤²à¥\80à¤\9aा à¤¶à¤¬à¥\8dद à¤ªà¥\81नरà¥\8dसà¥\8dथापन विपत्र पाठविल्या जाईल.",
+       "passwordreset-emailsentemail": "à¤\9cर à¤¹à¤¾ à¤µà¤¿à¤ªà¤¤à¥\8dरपतà¥\8dता à¤\86पलà¥\8dया à¤\96ातà¥\8dयाशà¥\80 à¤¸à¤\82लà¤\97à¥\8dन असेल तर, परवलीच्या शब्दाच्या पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात येईल.",
+       "passwordreset-emailsentusername": "à¤\9cर à¤¯à¤¾ à¤¸à¤¦à¤¸à¥\8dयनावाशà¥\80 à¤¸à¤\82लà¤\97à¥\8dन à¤µà¤¿à¤ªà¤¤à¥\8dरपतà¥\8dता à¤\85सà¥\87ल à¤¤à¤°, à¤ªà¤°à¤µà¤²à¥\80à¤\9aा à¤¶à¤¬à¥\8dद à¤ªà¥\81नरà¥\8dसà¥\8dथापनाबाबत विपत्र पाठविल्या जाईल.",
        "passwordreset-emailsent-capture": "'परवलीचा शब्द' पुनर्स्थापनेबाबत एक विपत्र पाठवण्यात आले आहे जे खाली दर्शविण्यात आले आहे.",
        "passwordreset-emailerror-capture": "'परवलीचा शब्द' पुनर्स्थापनेबाबत एक विपत्र निर्माण करण्यात आले, जे खाली दर्शविण्यात आले आहे.परंतु,{{GENDER:$2|सदस्य}}ला पाठविणे असफल झाले: $1",
        "changeemail": "विपत्रपत्ता बदला किंवा हटवा",
        "copyrightwarning2": "{{SITENAME}} येथे केलेले कोणतेही लेखन हे इतर संपादकांकरवी बदलले अथवा काढले जाऊ शकते. जर आपणास आपल्या लेखनाचे मुक्त संपादन होणे पसंत नसेल तर येथे संपादन करू नये.<br />\nतुम्ही येथे लेखन करताना हे सुद्धा गृहीत धरलेले असते की येथे केलेले लेखन तुमचे स्वतःचे आणि केवळ स्वतःच्या प्रताधिकार (कॉपीराईट) मालकीचे आहे किंवा प्रताधिकाराने गठित न होणाऱ्या सार्वजनिक ज्ञानक्षेत्रातून घेतले आहे किंवा तत्सम मुक्त स्रोतातून घेतले आहे. तुम्ही संपादन करताना तसे वचन देत आहात (अधिक माहितीसाठी $1 पहा). '''प्रताधिकारयुक्त लेखन सुयोग्य परवानगीशिवाय मुळीच चढवू/भरू नये!'''",
        "editpage-cannot-use-custom-model": "या पानाचा आशय-आराखडा(कंटेन्ट मॉडेल) बदलता येणार नाही.",
        "longpageerror": "<strong>त्रुटी:आपण दिलेला मजकूर जास्तीत जास्त शक्य {{PLURAL:$2|१ किलोबाईट पेक्षा|$2 किलोबाईट पेक्षा}}  अधिक लांबीचा {{PLURAL:$1|१ किलोबाईट|$1 किलोबाईट}} आहे.</strong>\nतो जतन केला जाऊ शकत नाही.",
-       "readonlywarning": "'''सावधान:विदागारास अनुरक्षणासाठी(मेंटेनन्स) ताळे ठोकले आहे,त्यामुळे सध्याच तुम्ही तुमचे संपादन जतन करू शकत नाही.'''\nजर तुम्हाला हवे असेल तर नंतर उपयोग करण्याच्या दृष्टीने, तुम्ही मजकूर नक्कल करुन, पुढील संपादनासाठी ’मजकुर संचिकेत’(टेक्स्ट फाईल)चिटकवू शकता.\nविदागारास ताळे ठोकलेल्या प्रचालकांनी खालील स्पष्टीकरण दिले आहे:$1",
+       "readonlywarning": "<strong>सावधान:विदागारास अनुरक्षणासाठी(मेंटेनन्स) ताळे ठोकले आहे,त्यामुळे सध्याच तुम्ही तुमचे संपादन जतन करू शकत नाही.</strong>\nजर तुम्हाला हवे असेल तर नंतर उपयोग करण्याच्या दृष्टीने, तुम्ही मजकूर नक्कल करुन, पुढील संपादनासाठी ’मजकुर संचिकेत’(टेक्स्ट फाईल)चिटकवू शकता.\nविदागारास ताळे ठोकलेल्या प्रणाली प्रशासकांनी खालील स्पष्टीकरण दिले आहे:$1",
        "protectedpagewarning": "'''सूचना: हे सुरक्षित पान आहे. फक्त प्रचालक याच्यात बदल करू शकतात.'''",
        "semiprotectedpagewarning": "'''सूचना:''' हे पान सुरक्षित आहे. फक्त नोंदणीकृत सदस्य याच्यात बदल करू शकतात.",
        "cascadeprotectedwarning": "<strong>ताकिद:</strong>हे पान निम्न-लिखीत निपतन-प्रतिबंधीत {{PLURAL:$1|पानात|पानांत}} आंतरभूत असल्यामुळे,केवळ प्रचालक-सुविधाप्राप्त सदस्यांनाच संपादन करता यावे असे ताळे त्यास ठोकलेले आहे :",
        "permissionserrors": "परवानगीस नकार",
        "permissionserrorstext": "खालील{{PLURAL:$1|कारणामुळे|कारणांमुळे}} तुम्हाला तसे करण्याची परवानगी नाही:",
        "permissionserrorstext-withaction": "तुम्हाला $2 क्रियेची परवानगी नाही, खालील {{PLURAL:$1|कारणासाठी|कारणांसाठी}}:",
-       "contentmodelediterror": "ही आवृत्ती आपण संपादू शकत नाही कारण त्याचा आशय-आराखडा (कंटेन्ट मॉडेल)<code>$1</code> आहे व सध्याच्या पानाचा आशय आराखडा <code>$2</code> आहे.",
+       "contentmodelediterror": "ही आवृत्ती आपण संपादू शकत नाही कारण त्याचा आशय-आराखडा (कंटेन्ट मॉडेल)<code>$1</code> आहे व सध्याच्या <code>$2</code> पानाचा आशय आराखडा वेगळा आहे.",
        "recreate-moveddeleted-warn": "'''सूचना: पूर्वी वगळलेला लेख तुम्ही पुन्हा बनवित आहात.'''\n\nआपण याचा विचार करा कि या पानाचे संपादन यापुढे करणे योग्य आहे काय.या पानाच्या वगळण्याच्या व स्थानांतराच्या नोंदी आपल्या (कामाच्या) सुलभतेसाठी दिलेल्या आहेत:",
        "moveddeleted-notice": "हे पान वगळण्यात आलेले आहे.\nसंदर्भासाठी, वगळण्याची व स्थानांतराची नोंद खाली दिलेली आहे.",
        "moveddeleted-notice-recent": "माफ करा,हे पान अलीकडेच (मागील २४ तासात) वगळल्या गेले आहे.हा पानाच्या वगळण्याचा व हलविण्याचा लॉग संदर्भासाठी खाली दिला आहे.",
        "right-createpage": "पृष्ठे तयार करा (जी चर्चापानांव्यतिरिक्त आहेत)",
        "right-createtalk": "चर्चा पृष्ठे तयार करा",
        "right-createaccount": "नवीन सदस्य खाती तयार करा",
+       "right-autocreateaccount": "या बाह्य सदस्य खात्याद्वारे आपोआप सनोंद प्रवेश करा",
        "right-minoredit": "बदल किरकोळ म्हणून जतन करा",
        "right-move": "पानांचे स्थानांतरण करा",
        "right-move-subpages": "पाने उपपानांसकट स्थानांतरीत करा",
        "right-blockemail": "एखाद्या सदस्याला इ-मेल पाठविण्यास प्रतिबंधित करा",
        "right-hideuser": "एखादे सदस्यनाव, त्याची सार्वजनिक दृष्यता लपवुन, प्रतिबंधित करा",
        "right-ipblock-exempt": "आइपी ब्लॉक्स,ऑटो ब्लॉक्स व रेंज ब्लॉक्स टाळा",
-       "right-proxyunbannable": "प्रॉक्सी असताना ब्लॉक्स कडे दुर्लक्ष करा",
        "right-unblockself": "कोण्या-एकास अप्रतिबंधित करा",
        "right-protect": "सुरक्षा पातळी बदलवा व निपात-प्रतिबंधित पानांचे संपादन करा",
        "right-editprotected": "\"{{int:protect-level-sysop}}\"म्हणून नमुद केलेली सुरक्षित पाने संपादा",
        "right-managechangetags": "डाटाबेस मधून [[Special:Tags|खूणपताका]] तयार करा किंवा  वगळा",
        "right-applychangetags": "कोणाच्याही बदलास [[Special:Tags|खूणपताका]] जोडा",
        "right-changetags": "वैयक्तिक आवृत्त्यांना व नोंद प्रवेष्ट्यांना, आहेतुक(arbitrary) [[Special:Tags|खूणपताका]] जोडा अथवा हटवा",
+       "grant-generic": "\"$1\" अधिकार गठ्ठा",
+       "grant-group-email": "विपत्र पाठवा",
+       "grant-blockusers": "सदस्यांना प्रतिबंधन/अप्रतिबंधित करा",
+       "grant-createaccount": "खाते तयार करा",
+       "grant-createeditmovepage": "पाने बनवा,संपादा व स्थानांतरण करा",
+       "grant-delete": "पाने, आवृत्त्या व नोंदी वगळा",
+       "grant-editinterface": "मिडियाविकि नामविश्व व सदस्यांची CSS/JS संपादा",
+       "grant-editmycssjs": "आपले सदस्य CSS/JavaScript संपादित करा",
+       "grant-editmyoptions": "आपला सदस्य पसंतीक्रम संपादा",
+       "grant-editmywatchlist": "आपली निरीक्षणयादी संपादित करा",
+       "grant-editpage": "अस्तित्वात असलेली पाने संपादा",
+       "grant-editprotected": "संपादनांपासून सुरक्षित असलेली पाने",
+       "grant-highvolume": "अत्त्युच्च-जागा घेणारे संपादन",
+       "grant-oversight": "सदस्य लपवा व आवृत्त्या दाबा",
+       "grant-patrol": "पानांच्या बदलांवर गस्त घाला",
+       "grant-protect": "पाने सुरक्षित किंवा असुरक्षित करा",
+       "grant-rollback": "पानाचे बदल परतवा",
+       "grant-sendemail": "इतर सदस्यांना विपत्र पाठवा",
+       "grant-basic": "मूळ अधिकार",
+       "grant-viewdeleted": "वगळलेल्या संचिका व पाने बघा",
+       "grant-viewmywatchlist": "आपली निरीक्षणसूची बघा",
        "newuserlogpage": "नवीन सदस्यांची नोंद",
        "newuserlogpagetext": "ही नवीन सदस्यांची नोंद यादी आहे.",
        "rightslog": "सदस्य आधिकार नोंद",
        "action-createpage": "लेख बनवा",
        "action-createtalk": "चर्चा पृष्ठे तयार करा",
        "action-createaccount": "हे सदस्यखाते तयार करा",
+       "action-autocreateaccount": "हे बाह्य सदस्य खाते आपोआप तयार करा",
        "action-history": "या पानाचा इतिहास बघा.",
        "action-minoredit": "हे संपादन 'किरकोळ' म्हणून खूण करा",
        "action-move": "हे पान स्थानांतरित करा",
        "foreign-structured-upload-form-label-own-work": "हे माझे स्वत:चे काम आहे",
        "foreign-structured-upload-form-label-infoform-categories": "वर्ग",
        "foreign-structured-upload-form-label-infoform-date": "दिनांक",
+       "foreign-structured-upload-form-3-label-yes": "होय",
+       "foreign-structured-upload-form-3-label-no": "नाही",
        "backend-fail-stream": "$1 या संचिकेचा स्त्रोत शोधता आला नाही.",
        "backend-fail-backup": "$1 या संचिकेची आधारप्रत बनविता आली नाही.",
        "backend-fail-notexists": "$1 ही संचिका अस्तित्वात नाही.",
        "usereditcount": "$1 {{PLURAL:$1|संपादन|संपादने}}",
        "usercreated": "दि. $1 ला, $2 वाजता, सदस्य खाते{{GENDER:$3|द्वारे बनविल्या गेले}}",
        "newpages": "नवीन पाने",
+       "newpages-submit": "दाखवा",
        "newpages-username": "सदस्य नाव:",
        "ancientpages": "जुनी पाने",
        "move": "स्थानांतरण",
        "specialloguserlabel": "कार्यकर्ता:",
        "speciallogtitlelabel": "लक्ष (शिर्षक किंवा {{ns:user}}:सदस्याचे सदस्यनाव):",
        "log": "नोंदी",
+       "logeventslist-submit": "दाखवा",
        "all-logs-page": "सर्व सार्वजनिक नोंदी",
        "alllogstext": "{{SITENAME}}च्या सर्व नोंदीचे एकत्र दर्शन.नोंद प्रकार, सदस्यनाव किंवा बाधित पान निवडून तुम्ही तुमचे दृश्यपान मर्यादित करू शकता.",
        "logempty": "नोंदीत अशी बाब नाही.",
        "cachedspecial-viewing-cached-ts": "तुम्ही या पानाची कॅशेतील आवृत्ती पहात आहात जी, पुर्णतः मूळ आवृत्ती नसू शकते.",
        "cachedspecial-refresh-now": "नुकतेच केलेले दाखवा.",
        "categories": "वर्ग",
+       "categories-submit": "दाखवा",
        "categoriespagetext": "विकिवर खालील वर्ग {{PLURAL:$1|आहे|आहेत}}.\n[[Special:UnusedCategories|न वापरलेले वर्ग]] येथे दाखवलेले नाहीत.\nहेही पहा: [[Special:WantedCategories|पाहिजे असलेले वर्ग]].",
        "categoriesfrom": "या शब्दापासून सुरू होणारे वर्ग दाखवा:",
        "special-categories-sort-count": "मोजणीनुसार निवडा",
        "listgrouprights-removegroup-self-all": "सर्व गट स्वतःच्या खात्यातून काढून टाका",
        "listgrouprights-namespaceprotection-header": "नामविश्व प्रतिबंध",
        "listgrouprights-namespaceprotection-namespace": "नामविश्व",
+       "listgrants": "अनुदाने",
+       "listgrants-grant": "अनुदान",
+       "listgrants-rights": "अधिकार",
        "trackingcategories": "वर्ग शोधत आहोत",
        "trackingcategories-summary": "या पानात ते रेखापथनातील वर्ग(tracking categories) आहेत, जे, मिडियाविकि संचेतनाद्वारे स्वयंचलितरित्या वसविण्यात (तयार करण्यात) आले आहेत. त्यांची नावे, {{ns:8}} नामविश्वातील संबंधित प्रणाली संदेशात फेरफार करुन, बदलविता येतात.",
        "trackingcategories-name": "संदेश नाम",
        "wlshowhideanons": "अनामिक सदस्य",
        "wlshowhidepatr": "पहारा केलेली संपादने",
        "wlshowhidemine": "माझी संपादने",
+       "wlshowhidecategorization": "पानांचे वर्गीकरण",
        "watchlist-options": "पहाऱ्याच्या सूचीचे पर्याय",
        "watching": "पहारा देत आहे...",
        "unwatching": "पहारा काढत आहे...",
        "delete-confirm": "\"$1\" वगळा",
        "delete-legend": "वगळा",
        "historywarning": "<strong>इशारा:</strong> आपण वगळत असलेल्या पानाला $1 {{PLURAL:$1|आवर्तनाचा|आवर्तनांचा}} इतिहास आहे:",
+       "historyaction-submit": "दाखवा",
        "confirmdeletetext": "आपण एक लेखपान त्याच्या सर्व इतिहासासोबत वगळण्याच्या तयारीत आहात.\nकृपया, याची खात्री कि, करीत असलेल्या कृतीचे परिणाम, आपण कृती करण्यापूर्वी जाणून घेतले आहेत व आपण हे   [[{{MediaWiki:Policy-url}}|मीडियाविकीच्या नीतीनुसारच]] करीत आहात.",
        "actioncomplete": "काम पूर्ण",
        "actionfailed": "कृती अयशस्वी झाली",
        "unblock": "सदस्यप्रतिबंध काढा",
        "blockip": "{{GENDER:$1|सदस्यास}} प्रतिबंधित करा",
        "blockip-legend": "सदस्यास प्रतिबंध करा",
-       "blockiptext": "एखाद्या विशिष्ट अंकपत्त्याची किंवा सदस्याची लिहिण्याची क्षमता प्रतिबंधित  करण्याकरिता खालील सारणी वापरा.\nहे केवळ उच्छेद टाळण्याच्याच दृष्टीने आणि [[{{MediaWiki:Policy-url}}|निती]]स अनुसरून केले पाहिजे.\nखाली विशिष्ट कारण भरा(उदाहरणार्थ,ज्या पानांवर उच्छेद माजवला गेला त्यांची उद्धरणे देऊन).",
+       "blockiptext": "एखाद्या विशिष्ट अंकपत्त्याची किंवा सदस्याची लिहिण्याची क्षमता प्रतिबंधित  करण्याकरिता खालील सारणी वापरा.\nहे केवळ उच्छेद टाळण्याच्याच दृष्टीने आणि [[{{MediaWiki:Policy-url}}|निती]]स अनुसरून केले पाहिजे.\nखाली विशिष्ट कारण भरा(उदाहरणार्थ,ज्या पानांवर उच्छेद माजवला गेला त्यांची उद्धरणे देऊन).\nआपण [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] ही वाक्यरचना वापरुन अंकपत्त्याचा आवाका प्रतिबंधित करु शकता. जास्तीत जास्त अनुमानित आवाका आहे  /$1  IPv4 साठी व /$2 IPv6 साठी.",
        "ipaddressorusername": "अंकपत्ता किंवा सदस्यनाम:",
        "ipbexpiry": "समाप्ति:",
        "ipbreason": "कारण:",
        "javascripttest-pagetext-frameworks": "कृपया टेस्टिंग साठी पुढील पैकी व्यवस्था / पद्धत निवडावी: $1",
        "javascripttest-pagetext-skins": "टेस्ट करण्यासाठी योग्य ती स्कीन निवडावी",
        "javascripttest-qunit-intro": "mediawiki.org वर [$1 testing documentation] पहा",
-       "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-anoncontribs": "या अंकपत्त्यावरुन झालेले संपादन",
        "tooltip-pt-login": "आपणांस सनोंद प्रवेशासाठी प्रोत्साहीत करण्यात येत आहे;अर्थातच, ते अनिवार्य नाही.",
        "tooltip-pt-logout": "सनोंद निर्गम",
        "tooltip-t-recentchangeslinked": "या पानास जोडलेल्या सर्व पानांवरील अलीकडील बदल",
        "tooltip-feed-rss": "या पानाकरिता आर.एस.एस. रसद",
        "tooltip-feed-atom": "या पानाकरिता अॅटम रसद",
-       "tooltip-t-contributions": "या सदस्याच्या योगदानांची यादी पहा",
-       "tooltip-t-emailuser": "या सदस्याला ई-मेल पाठवा",
+       "tooltip-t-contributions": "{{GENDER:$1|या सदस्याच्या}} योगदानांची यादी",
+       "tooltip-t-emailuser": "{{GENDER:$1|या सदस्याला}} विपत्र पाठवा",
        "tooltip-t-info": "या पानाबाबत अधिक माहिती",
        "tooltip-t-upload": "संचिकेचे अपभारण करा",
        "tooltip-t-specialpages": "सर्व विशेष पृष्ठांची यादी",
        "pageinfo-category-files": "संचिकांची संख्या",
        "markaspatrolleddiff": "टेहळणी केल्याची खूण करा",
        "markaspatrolledtext": "या पानावर गस्त झाल्याची खूण करा",
+       "markaspatrolledtext-file": "या संचिकेच्या आवृत्तीस गस्त घातली म्हणून् खूण करा",
        "markedaspatrolled": "गस्त केल्याची खूण केली",
        "markedaspatrolledtext": "निवडलेल्या [[:$1]]च्या आवर्तनास गस्त घातल्याची खूण केली.",
        "rcpatroldisabled": "अलीकडील बदलची गस्ती अनुपलब्ध",
        "newimages-legend": "गाळक",
        "newimages-label": "संचिकानाम (किंवा त्याचा भाग):",
        "newimages-showbots": "सांगकाम्याद्वारे केलेली अपभारणे दाखवा",
+       "newimages-hidepatrolled": "गस्त घातलेली अपभारणे लपवा",
        "noimages": "बघण्यासारखे येथे काही नाही.",
        "ilsubmit": "शोधा",
        "bydate": "तारखेनुसार",
        "watchlisttools-edit": "पहाऱ्याची  सूची पहा आणि संपादित करा",
        "watchlisttools-raw": "नित्य पहाण्याची कच्ची-सूची संपादित करा",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|चर्चा]])",
+       "timezone-local": "स्थानिक",
        "duplicate-defaultsort": "'''ताकिद:''' डिफॉल्ट सॉर्ट की \"$2\" ओवर्राइड्स अर्लीयर डिफॉल्ट सॉर्ट की \"$1\".",
        "version": "आवृत्ती",
        "version-extensions": "स्थापित विस्तार",
        "tags-activate": "सक्रीय करा",
        "tags-deactivate": "निष्क्रिय करा",
        "tags-hitcount": "$1 {{PLURAL:$1|बदल|बदल}}",
+       "tags-manage-blocked": "आपण प्रतिबंधित असतांना बदल खूणपताकांचे व्यवस्थापन करु शकत नाही.",
        "tags-create-heading": "नवीन बिल्ला तयार करा",
        "tags-create-tag-name": "खूणपताकेचे नाव:",
        "tags-create-reason": "कारण:",
        "tags-deactivate-reason": "कारण:",
        "tags-deactivate-not-allowed": "\"$1\" खूणपताकेस अक्रिय करणे शक्य नाही.",
        "tags-deactivate-submit": "निष्क्रिय करा",
+       "tags-apply-blocked": "आपण प्रतिबंधित असतांना आपल्या बदलांसह, बदल खूणपताकांना  लागू करु शकत नाही.",
+       "tags-update-blocked": "आपण प्रतिबंधित असतांना बदल खूणपताकांना जोडू अथवा हटवू शकत नाही.",
        "tags-edit-reason": "कारण:",
        "tags-edit-none-selected": "जोडण्यास किंवा हटविण्यास किमान एक खूणपताका निवडा.",
        "comparepages": "पानांची तुलना करा",
        "expand_templates_remove_nowiki": "निकालात <nowiki>खूणपतका दाखवू नका",
        "expand_templates_generate_xml": "XML चा पार्स (parse) वृक्ष दाखवा",
        "expand_templates_preview": "झलक",
+       "expand_templates_input_missing": "आपण काहीतरी आंतरदेय मजकूर पुरवावयास हवा.",
        "pagelang-name": "पान",
        "pagelang-language": "भाषा",
        "pagelang-use-default": "अविचल भाषा वापरा",
        "pagelang-select-lang": "भाषा निवडा",
+       "pagelang-submit": "सादर करा",
        "right-pagelang": "पानाची भाषा बदला",
        "action-pagelang": "पानाची असलेली भाषा बदला",
        "log-name-pagelang": "भाषा बदल नोंदवही",
        "mediastatistics-table-totalbytes": "एकत्रित आकार",
        "mediastatistics-header-unknown": "अनोळखी",
        "mediastatistics-header-office": "कार्यालय",
+       "mediastatistics-header-total": "सर्व संचिका",
        "json-error-syntax": "वाक्यरचना त्रुटी",
        "headline-anchor-title": "या विभागाचा दुवा",
        "special-characters-group-latin": "लॅटीन",
        "mw-widgets-dateinput-no-date": "कोणताही दिनांक निवडला नाही",
        "mw-widgets-titleinput-description-new-page": "अद्याप पान अस्तित्वात नाही",
        "mw-widgets-titleinput-description-redirect": "$1ला पुनर्निर्देशित करा",
-       "api-error-blacklisted": "कुपया वेगळे वर्णनात्मक शीर्षक निवडा"
+       "api-error-blacklisted": "कुपया वेगळे वर्णनात्मक शीर्षक निवडा",
+       "sessionmanager-tie": "हे एकत्रित करु शकत नाही,बहुविध विनंती अधिप्रमाणन प्रकार:$1",
+       "sessionprovider-generic": "$1 सत्रे",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "कुकी-आधारीत सत्रे",
+       "sessionprovider-nocookies": "कुकिज अक्षम असू शकतात. याची खात्री करा कि कुकिज सक्षम केल्या आहेत व पुन्हा सुरुवात करा.",
+       "randomrootpage": "अविशिष्ट मूळ पान"
 }
index 4e6d608..b03830e 100644 (file)
                        "Zawthet",
                        "ကိုရာဝီ",
                        "아라",
-                       "9.sinistra"
+                       "9.sinistra",
+                       "Ninjastrikers"
                ]
        },
        "tog-underline": "လင့်ကို မျဉ်းသားသည့် ပုံစံ -",
        "tog-hideminor": "လတ်တလော အပြောင်းအလဲများတွင် အရေးမကြီးသည်များကို ဝှက်ရန်",
-       "tog-hidepatrolled": "လတ်တလော အပြောင်းအလဲများတွင် အရေးမကြီးသည်များကို ဝှက်ရန်",
-       "tog-newpageshidepatrolled": "လက်တလော အပြောင်းလဲများတွင် စာမျက်နှာသစ်များကို ဝှက်ရန်",
+       "tog-hidepatrolled": "လတ်တလော အပြောင်းအလဲများတွင် ကင်းလှည့်တည်းဖြတ်မှုများကို ဝှက်ရန်",
+       "tog-newpageshidepatrolled": "စာမျက်နှာသစ်စာရင်းတွင် ကင်းလှည့်စာမျက်နှာများကို ဝှက်ရန်",
+       "tog-hidecategorization": "စာမျက်နှာများ၏ ကဏ္ဍကို ဝှက်ရန်",
        "tog-extendwatchlist": "စောင့်ကြည့်စာရင်းတွင် ပြောင်းလဲမှုအားလုံးအား  ပြရန်။",
-       "tog-usenewrc": "á\80\9cá\80\90á\80ºá\80\90á\80\9cá\80±á\80¬á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸á\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80¬á\80¸á\80\96á\80¼á\80\84á\80·á\80º á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\9cá\80­á\80¯á\80\80á\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸ (JavaScript á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºá\80\9eá\80\8aá\80º)",
-       "tog-numberheadings": "ခေါင်းစဉ်များ အား စေ့ဆော်ချက်အတိုင်း လုပ်ဆောင်ရန်",
-       "tog-showtoolbar": "á\80\95á\80¼á\80¯á\80\95á\80¼á\80\84á\80ºá\80\9bá\80\94á\80º á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\99á\80»á\80¬á\80¸ (JavaScript á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºá\80\9eá\80\8aá\80º)",
-       "tog-editondblclick": "á\80\80á\80\9cá\80\85á\80ºá\80\94á\80¾á\80\85á\80ºá\80\81á\80«á\80\94á\80¾á\80­á\80\95á\80ºá\80\9cá\80»á\80¾á\80\84á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80¬á\80¸á\80\95á\80¼á\80¯á\80\95á\80¼á\80\84á\80ºá\80\95á\80« (JavaScript á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºá\80\9eá\80\8aá\80º)",
+       "tog-usenewrc": "á\80\9cá\80\90á\80ºá\80\90á\80\9cá\80±á\80¬á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\99á\80¾á\80¯á\80\99á\80»á\80¬á\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\9bá\80¾á\80­ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\9cá\80­á\80¯á\80\80á\80º á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\81á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸",
+       "tog-numberheadings": "ခေါင်းစဉ်များအား အလိုအလျောက် နံပါတ်စဉ်ရန်",
+       "tog-showtoolbar": "á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80º á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\98á\80¬á\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80\9bá\80\94á\80º",
+       "tog-editondblclick": "á\80\80á\80\9cá\80\85á\80ºá\80\94á\80¾á\80\85á\80ºá\80\81á\80«á\80\94á\80¾á\80­á\80\95á\80ºá\80\95á\80¼á\80®á\80¸ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80¡á\80¬á\80¸ á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80ºá\80\9bá\80\94á\80º",
        "tog-editsectiononrightclick": "အပိုင်းလိုက်ခေါင်းစဉ်များကို ညာကလစ်နှိပ်ခြင်းဖြင့် အပိုင်းလိုက် တည်းဖြတ်ခြင်းကို အသုံးပြုရန်",
        "tog-watchcreations": "ကျွန်ုပ်စတင်ရေးသားခဲ့သည့်စာမျက်နှာများနှင့် အပ်လုပ်တင်ခဲ့သည့် ဖိုင်များကို စောင့်​ကြည့်​စာ​ရင်း​ထဲ ပေါင်းထည့်ရန်",
        "tog-watchdefault": "ကျွန်ုပ် တည်းဖြတ်ခဲ့သည့် စာမျက်နှာများနှင့် ဖိုင်များကို စောင့်ကြည့်စာရင်းသို့  ပေါင်းထည့်ပါ။",
        "tog-minordefault": "တည်းဖြတ်မှုအားလုံးသည် အရေးမကြီးသော တည်းဖြတ်မှုဟု ပုံသေသတ်မှတ်ရန်",
        "tog-previewontop": "တည်းဖြတ်သည့်အကွက်မတိုင်မီ နမူနာကို ပြရန်",
        "tog-previewonfirst": "ပထမတည်းဖြတ်မှုတွင် နမူနာကို ပြရန်",
-       "tog-enotifwatchlistpages": "ကျွန်ုပ်၏စောင့်ကြည့်စာရင်းမှ စာမျက်နှာတစ်ခု သို့မဟုတ် ဖိုင်တစ်ခုကို ပြောင်းလဲလိုက်ပါက ကျွနုပ်ဆီ အီးမေးပို့ရန်",
+       "tog-enotifwatchlistpages": "á\80\80á\80»á\80½á\80\94á\80ºá\80¯á\80\95á\80ºá\81\8fá\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\99á\80¾ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\90á\80\85á\80ºá\80\81á\80¯ á\80\9eá\80­á\80¯á\80·á\80\99á\80\9fá\80¯á\80\90á\80º á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\9cá\80­á\80¯á\80\80á\80ºá\80\95á\80«á\80\80 á\80\80á\80»á\80½á\80\94á\80ºá\80¯á\80\95á\80ºá\80\86á\80® á\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\95á\80­á\80¯á\80·á\80\9bá\80\94á\80º",
        "tog-enotifusertalkpages": "ကျွန်ုပ်၏ဆွေးနွေးချက်စာမျက်နှာ ပြောင်းလဲမှုရှိပါက ကျွန်ုပ်ထံ အီးမေးပို့ရန်",
        "tog-enotifminoredits": "စာမျက်နှာများနှင့် ဖိုင်များ၏ အရေးမကြီးသော တည်းဖြတ်မှုများကိုလည်း အီးမေးပို့ရန်",
        "tog-enotifrevealaddr": " အသိပေးချက်အီးမေးများတွင် ကျွန်ုပ်၏ အီးမေးလိပ်စာကို ဖော်ပြရန်",
        "tog-shownumberswatching": "စောင့်ကြည့်နေသော အသုံးပြုသူအရေအတွက်ကို ပြရန်",
-       "tog-oldsig": "ရှိနှင့်ပြီးသား လက်မှတ်၏ နမူနာ -",
+       "tog-oldsig": "ရှိနှင့်ပြီးသား လက်မှတ် -",
        "tog-fancysig": "လက်မှတ်ကို ဝီကီလင့်အဖြစ် သတ်မှတ်ရန် (အလိုအလျောက်လင့်မပါဘဲနှင့်)",
        "tog-forceeditsummary": "တည်းဖြတ်အတိုချုပ် ဗလာဖြစ်နေလျှင် သတိပေးရန်",
        "tog-watchlisthideown": "ကျွန်ုပ်၏ တည်းဖြတ်မှုများကို စောင့်ကြည့်စာရင်းမှ ဝှက်ထားရန်",
        "tog-watchlisthideliu": "စောင့်ကြည့်စာရင်းမှ loggin ဝင်ထားသော အသုံးပြုသူတို့၏ တည်းဖြတ်မှုများကို ဝှက်ရန်",
        "tog-watchlisthideanons": "စောင့်ကြည့်စာရင်းမှ အမည်မသိ အသုံးပြုသူများ၏ တည်းဖြတ်မှုများကို ဝှက်ရန်",
        "tog-watchlisthidepatrolled": "patrolled တည်းဖြတ်မှုများကို စောင့်ကြည့်စာရင်းမှ ဝှက်ထားရန်",
+       "tog-watchlisthidecategorization": "စာမျက်နှာများ၏ ကဏ္ဍကို ဝှက်ရန်",
        "tog-ccmeonemails": "ကျွန်ုပ် အခြားအသုံးပြုသူများထံပို့သော အီးမေးမိတ္တူကို ကျွန်ုပ်ထံ ပြန်ပို့ရန်",
        "tog-diffonly": "ကွဲပြားမှုများအောက်ရှိ စာမျက်နှာတွင်ပါဝင်သည်များကို မပြပါနှင့်",
        "tog-showhiddencats": "ဝှက်ထားသော ကဏ္ဍများကို ပြရန်",
        "tog-useeditwarning": "မသိမ်းရသေးသော ပြောင်းလဲမှုများ နှင့် တည်းဖြတ်ဆဲစာမျက်နှာမှ ထွက်သွားလျှင် သတိပေးပါ",
+       "tog-prefershttps": "log in ဝင်တိုင်း လုံခြုံသော ဆက်သွယ်မှုကို အသုံးပြုရန်",
        "underline-always": "အမြဲ",
        "underline-never": "ဘယ်သောအခါမျှ",
        "underline-default": "ဘရောက်ဆာ သို့ Skin default အတိုင်း",
@@ -70,7 +74,7 @@
        "monday": "တနင်္လာ",
        "tuesday": "အင်္ဂါ",
        "wednesday": "ဗုဒ္ဓဟူး",
-       "thursday": "ကြာ​သ​ပ​တေး​",
+       "thursday": "ကြာသပတေး",
        "friday": "သောကြာ",
        "saturday": "စနေ",
        "sun": "နွေ",
        "thu": "တေး",
        "fri": "ကြာ",
        "sat": "နေ",
-       "january": "ဇန်​န​ဝါ​ရီ​",
+       "january": "ဇန်နဝါရီ",
        "february": "ဖေဖော်ဝါရီ",
-       "march": "မတ်",
-       "april": "ဧ​ပြီ​",
-       "may_long": "မေ",
-       "june": "ဇွန်",
-       "july": "ဇူ​လိုင်​",
-       "august": "ဩ​ဂုတ်​",
-       "september": "စက်​တင်​ဘာ​",
-       "october": "အောက်​တို​ဘာ​",
-       "november": "နို​ဝင်​ဘာ​",
-       "december": "ဒီ​ဇင်​ဘာ​",
-       "january-gen": "ဇန်​န​ဝါ​ရီ​",
-       "february-gen": "ဖေ​ဖော်​ဝါ​ရီ​",
-       "march-gen": "မတ်",
-       "april-gen": "ဧ​ပြီ​",
-       "may-gen": "မေ",
-       "june-gen": "ဇွန်",
-       "july-gen": "ဇူ​လိုင်​",
-       "august-gen": "ဩ​ဂုတ်​",
-       "september-gen": "စက်​တင်​ဘာ​",
-       "october-gen": "အောက်​တို​ဘာ​",
-       "november-gen": "နို​ဝင်​ဘာ​",
-       "december-gen": "ဒီ​ဇင်​ဘာ​",
+       "march": "မတ်",
+       "april": "ဧပြီ",
+       "may_long": "မေ",
+       "june": "ဇွန်",
+       "july": "ဇူလိုင်",
+       "august": "ဩဂုတ်",
+       "september": "စက်တင်ဘာ",
+       "october": "အောက်တိုဘာ",
+       "november": "နိုဝင်ဘာ",
+       "december": "ဒီဇင်ဘာ",
+       "january-gen": "ဇန်နဝါရီ",
+       "february-gen": "ဖေဖော်ဝါရီ",
+       "march-gen": "မတ်",
+       "april-gen": "ဧပြီ",
+       "may-gen": "မေ",
+       "june-gen": "ဇွန်",
+       "july-gen": "ဇူလိုင်",
+       "august-gen": "ဩဂုတ်",
+       "september-gen": "စက်တင်ဘာ",
+       "october-gen": "အောက်တိုဘာ",
+       "november-gen": "နိုဝင်ဘာ",
+       "december-gen": "ဒီဇင်ဘာ",
        "jan": "ဇန်",
        "feb": "ဖေ",
        "mar": "မတ်",
        "apr": "ဧ",
-       "may": "မေ",
+       "may": "မေ",
        "jun": "ဇွန်",
        "jul": "ဇူ",
        "aug": "ဩ",
        "march-date": "မတ် $1",
        "april-date": "ဧပြီ $1",
        "may-date": "မေ $1",
-       "june-date": "á\80\82á\80»ွန် $1",
-       "july-date": "á\80\82á\80»ူလိုင် $1",
+       "june-date": "á\80\87ွန် $1",
+       "july-date": "á\80\87ူလိုင် $1",
        "august-date": "ဩဂုတ် $1",
        "september-date": "စက်တင်ဘာ $1",
        "october-date": "အောက်တိုဘာ $1",
        "november-date": "နိုဝင်ဘာ $1",
        "december-date": "ဒီဇင်ဘာ $1",
-       "pagecategories": "{{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍ}}",
+       "period-am": "နံနက်",
+       "period-pm": "ညနေ",
+       "pagecategories": "{{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍများ}}",
        "category_header": "ကဏ္ဍ \"$1\" မှ စာမျက်နှာများ",
-       "subcategories": "á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯ခွဲ",
+       "subcategories": "á\80\80á\80\8fá\80¹á\80\8dခွဲ",
        "category-media-header": "ကဏ္ဍ \"$1\" မှ မီဒီယာ",
-       "category-empty": "ဗလာအုပ်စု",
+       "category-empty": "<em>ဤကဏ္ဍသည် လက်ရှိတွင် စာမျက်နှာများ သို့မဟုတ် မီဒီယာများ မရှိပါ။</em>",
        "hidden-categories": "{{PLURAL:$1|ဝှက်ထားသော ကဏ္ဍ|ဝှက်ထားသော ကဏ္ဍများ}}",
        "hidden-category-category": "ဝှက်ထားသော ကဏ္ဍများ",
        "category-subcat-count": "{{PLURAL:$2|ဤကဏ္ဍတွင် အောက်ပါ ကဏ္ဍခွဲသာ ရှိသည်။ |ဤကဏ္ဍတွင် စုစုပေါင်း $2 ခု အနက်မှ အောက်ပါ {{PLURAL:$1|ကဏ္ဍခွဲ|ကဏ္ဍခွဲ $1 ခု}} ရှိသည်။}}",
        "newwindow": "(ဝင်းဒိုးအသစ်တခုကိုဖွင့်ရန်)",
        "cancel": "မ​လုပ်​တော့​",
        "moredotdotdot": "နောက်ထပ်...",
-       "morenotlisted": "á\80\94á\80±á\80¬á\80\80á\80ºá\80\91á\80\95á\80º á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸ á\80\99á\80\9bá\80¾á\80­á\80\95á\80«...",
+       "morenotlisted": "á\80¤á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\99á\80¾á\80¬ á\80\99á\80\95á\80¼á\80\8aá\80·á\80ºá\80\85á\80¯á\80¶á\80\95á\80«á\81\8b",
        "mypage": "စာမျက်နှာ",
        "mytalk": "ဆွေးနွေးချက်",
-       "anontalk": "á\80¤ IP address á\80¡á\80\90á\80½á\80\80á\80º á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\9bá\80\94á\80º",
+       "anontalk": "ဆွေးနွေးရန်",
        "navigation": "အ​ညွှန်း​",
        "and": "&#32;နှင့်",
        "qbfind": "ရှာပါ",
        "actions": "ဆောင်ရွက်ချက်များ",
        "namespaces": "အမည်ညွှန်းများ",
        "variants": "အမျိုးမျိုးအပြားပြား",
+       "navigation-heading": "လမ်းညွှန်မီနူး",
        "errorpagetitle": "အမှား",
        "returnto": "$1 သို့ ပြန်သွားရန်။",
        "tagline": "{{SITENAME}} မှ",
        "searcharticle": "သွား​ပါ​",
        "history": "စာမျက်နှာ ရာဇဝင်",
        "history_short": "ရာဇဝင်",
-       "updatedmarker": "á\80\94á\80±á\80¬á\80\80á\80ºá\80\86á\80¯á\80¶á\80¸á\80\91á\80¬á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80¼á\80®á\80¸á\80\9eá\80\8aá\80·á\80ºá\80\94á\80±á\80¬á\80\80á\80ºá\80\95á\80­á\80¯á\80\84á\80ºá\80¸ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80ºá\81\8b",
+       "updatedmarker": "နောက်ဆုံးကြည့်ပြီးသည့်နောက်ပိုင်း တည်းဖြတ်ထားသည်။",
        "printableversion": "ပရင့်ထုတ်ရန်",
        "permalink": "ပုံ​သေ​လိပ်​စာ​",
        "print": "ပရင့်",
        "view": "ကြည့်ရန်",
+       "view-foreign": "$1 တွင် ကြည့်ရန်",
        "edit": "ပြင်​ဆင်​ရန်​",
        "create": "စတင်ရေးသားရန်",
        "editthispage": "ဤစာမျက်နှာကို ပြင်ရန်",
        "create-this-page": "ဤစာမျက်နှာကို စတင်ရေးသားရန်",
        "delete": "ဖျက်​ပါ​",
        "deletethispage": "ဤစာမျက်နှာဖျက်ပါ",
+       "undeletethispage": "ဤစာမျက်နှာကို မဖျက်တော့ရန်",
        "undelete_short": "{{PLURAL:$1|တည်းဖြတ်မှုတစ်ခု|တည်းဖြတ်မှု $1 ခုတို့}}ကို မဖျက်တော့ရန်",
        "viewdeleted_short": "{{PLURAL:$1|ဖျက်လိုက်သည့်တည်းဖြတ်မှုတစ်ခု|ဖျက်လိုက်သည့် တည်းဖြတ်မှု $1 ခု}}ကို ကြည့်ရန်",
-       "protect": "á\80\91á\80­á\80\99á\80ºá\80¸â\80\8bá\80\9eá\80­á\80\99á\80ºá\80¸â\80\8bá\80\95á\80«â\80\8b",
+       "protect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\95á\80«",
        "protect_change": "ပြောင်းလဲရန်",
        "protectthispage": "ဤစာမျက်နှာကို ကာကွယ်ရန်",
-       "unprotect": "á\80\99á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\90á\80±á\80¬á\80·ရန်",
-       "unprotectthispage": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\99á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\90á\80±á\80¬á\80·ရန်",
+       "unprotect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
+       "unprotectthispage": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
        "newpage": "စာမျက်နှာအသစ်",
        "talkpage": "ဆွေးနွေးရန်",
-       "talkpagelinktext": "ဆွေးနွေးရန်",
+       "talkpagelinktext": "ဆွေးနွေး",
        "specialpage": "အထူး စာမျက်နှာ",
        "personaltools": "ကိုယ်ပိုင် ကိရိယာများ",
        "articlepage": "မာတိကာ ကြည့်ရန်",
        "talk": "ဆွေးနွေးချက်များ",
        "views": "ပုံပန်းသွင်ပြင်",
-       "toolbox": "á\80\9cá\80\80á\80ºá\80\85á\80½á\80² á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\99á\80»á\80¬á\80¸",
+       "toolbox": "ကိရိယာများ",
        "userpage": "အသုံးပြုသူ၏ စာမျက်နှာကို ကြည့်ရန်",
        "projectpage": "ပရောဂျက်စာမျက်နှာကို ကြည့်ရန်",
        "imagepage": "ဖိုင်စာမျက်နှာကိုကြည့်ရန်",
-       "mediawikipage": "á\80\99á\80®á\80\92á\80®á\80\9aá\80¬á\80\9dá\80®á\80\80á\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬",
+       "mediawikipage": "á\80\85á\80¬á\80\90á\80­á\80¯á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\80á\80¼á\80\8aá\80·á\80ºá\80\9bá\80\94á\80º",
        "templatepage": "တမ်းပလိတ်စာမျက်နှာကို ကြည့်ရန်",
-       "viewhelppage": "á\80\80á\80°á\80\8aá\80®á\80\99á\80\8aá\80·á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ကြည့်ရန်",
-       "categorypage": "á\80\80á\80\8fá\80¹á\80\8dá\80\99á\80»á\80¬á\80¸ကို ကြည့်ရန်",
+       "viewhelppage": "á\80¡á\80\80á\80°á\80¡á\80\8aá\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ ကြည့်ရန်",
+       "categorypage": "á\80\80á\80\8fá\80¹á\80\8dá\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ကို ကြည့်ရန်",
        "viewtalkpage": "ဆွေးနွေးမှုကို ကြည့်ရန်",
        "otherlanguages": "တခြား ဘာသာဖြင့်",
        "redirectedfrom": "($1 မှ ပြန်ညွှန်းထားသည်)",
        "redirectpagesub": "ပြန်ညွှန်းသော စာမျက်နှာ",
+       "redirectto": "ပြန်ညွှန်းရန် -",
        "lastmodifiedat": "ဤစာမျက်နှာကို $1 ရက် $2 အချိန်တွင် နောက်ဆုံး ပြင်ဆင်ခဲ့သည်။",
        "viewcount": "ဤစာမျက်နှာကို {{PLURAL:$1|တစ်ကြိမ်|$1 ကြိမ်}} ဝင်ထားသည်။",
        "protectedpage": "ကာကွယ်ထားသည့် စာမျက်နှာ",
        "jumptonavigation": "အ​ညွှန်း​",
        "jumptosearch": "ရှာ​ဖွေ​ရန်​",
        "view-pool-error": "ဆာဗာသည် ယခုအချိန်တွင် မမျှသောဝန်ကို ထမ်းနေရသည်။\nအသုံးပြုသူ အမြောက်အများက ဤစာမျက်နှာကို ကြည့်ရှုရန် ကြိုးပမ်းနေကြသည်။\nဤစာမျက်နှာကို နောက်တစ်ကြိမ် ပြန်မကြည့်မီ ခဏတာမျှ စောင့်ပါ။\n\n$1",
+       "generic-pool-error": "ဝမ်းနည်းပါသည်၊ ဆာဗာများသည် ယခုအချိန်တွင် မမျှသောဝန်ကို ထမ်းနေရသည်။\nအသုံးပြုသူ အမြောက်အများက ဤစာမျက်နှာကို ကြည့်ရှုရန် ကြိုးပမ်းနေကြသည်။\nဤစာမျက်နှာကို နောက်တစ်ကြိမ် ပြန်မကြည့်မီ ခဏတာမျှ စောင့်ပါ။",
        "pool-errorunknown": "အမည်မသိအမှား",
+       "poolcounter-usage-error": "အသုံးပြုမှု အမှား: $1",
        "aboutsite": "{{SITENAME}} အကြောင်း",
        "aboutpage": "Project: အကြောင်းအရာ",
-       "copyright": "$1 အောက်တွင် ဤအကြောင်းအရာကို ရရှိနိုင်သည်။",
+       "copyright": "$1 á\80¡á\80±á\80¬á\80\80á\80ºá\80\90á\80½á\80\84á\80º á\80¤á\80¡á\80\80á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80¡á\80\9bá\80¬á\80\80á\80­á\80¯ á\80\99á\80¾á\80\90á\80ºá\80\9eá\80¬á\80¸á\80\99á\80\91á\80¬á\80¸á\80\95á\80«á\80\80 á\80\9bá\80\9bá\80¾á\80­á\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b",
        "copyrightpage": "{{ns:project}}: မူပိုင်ခွင့်",
-       "currentevents": "လက်​ရှိ​လုပ်​ငန်း​များ​",
-       "currentevents-url": "Project:လက်​ရှိ​လုပ်​ငန်း​များ​",
+       "currentevents": "လက်ရှိဖြစ်ရပ်များ",
+       "currentevents-url": "Project:လက်ရှိဖြစ်ရပ်များ",
        "disclaimers": "သတိပြုစရာများ",
        "disclaimerpage": "Project: အထွေထွေ သတိပြုဖွယ်",
        "edithelp": "ပြင်​ဆင်​ရန် အ​ကူ​အ​ညီ​",
+       "helppage-top-gethelp": "အကူအညီ",
        "mainpage": "ဗဟိုစာမျက်နှာ",
        "mainpage-description": "ဗ​ဟို​စာ​မျက်​နှာ​",
        "policy-url": "Project:မူ​ဝါ​ဒ",
        "privacy": "ကိုယ်ပိုင်ရေးရာ မူဝါဒ",
        "privacypage": "Project: ကိုယ်ပိုင်ရေးရာ မူဝါဒ",
        "badaccess": "ခွင့်ပြုချက်မှ အမှား",
-       "versionrequired": "မီဒီယာဝီကီဗာရှင်း $1 လိုအပ်သည်",
+       "badaccess-groups": "သင်တောင်းဆိုလိုက်သော လုပ်ဆောင်ချက်ကို {{PLURAL:$2|အုပ်စု|အုပ်စုဝင်}}: $1 ၏ အသုံးပြုသူများကိုသာ ကန့်သတ်ထားသည်။",
+       "versionrequired": "မီဒီယာဝီကီဗားရှင်း $1 လိုအပ်သည်",
        "versionrequiredtext": "ဤစာမျက်နှာကို ကြည့်ရန် မီဒီယာဝီကီဗာရှင်း $1 လိုအပ်သည်။\n[[Special:Version|ဗားရှင်းစာမျက်နှာ]]ကို ကြည့်ပါ။",
        "ok": "အိုကေ",
        "retrievedfrom": "\"$1\" မှ ရယူရန်",
        "youhavenewmessages": "သင့်တွင် $1 ($2) ရှိသည်။",
-       "youhavenewmessagesmulti": "$1 မှာ မက်ဆေ့အသစ်များ ရှိသည်",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|သင့်ထံတွင်}} {{PLURAL:$3|အခြားအသုံးပြုသူ|အသုံးပြုသူများ $3 ဦး}} ထံမှ $1 ရှိသည် ($2)။",
+       "youhavenewmessagesmanyusers": "သင့်ထံတွင် အသုံးပြုသူများထံမှ $1 ရှိသည် ($2)။",
+       "newmessageslinkplural": "{{PLURAL:$1|စာလွှာအသစ် တစ်စောင်|999=စာလွှာ အသစ်များ}}",
+       "newmessagesdifflinkplural": "နောက်ဆုံး {{PLURAL:$1|ပြောင်းလဲမှု|999=ပြောင်းလဲမှုများ}}",
+       "youhavenewmessagesmulti": "$1 မှာ စာတိုအသစ်များ ရှိသည်",
        "editsection": "ပြင်​ဆင်​ရန်​",
        "editold": "ပြင်​ဆင်​ရန်​",
        "viewsourceold": "ရင်းမြစ်ကို ကြည့်ရန်",
        "hidetoc": "ဝှက်",
        "collapsible-collapse": "ချုံ့ရန်",
        "collapsible-expand": "ချဲ့ရန်",
+       "confirmable-confirm": "{{GENDER:$1|သင်}} သေချာပြီလား?",
+       "confirmable-yes": "လုပ်မည်",
+       "confirmable-no": "မလုပ်ပါ",
        "thisisdeleted": "$1 ကို ကြည့်မည်လော (သို့) restore ပြန်သိမ်းမည်လော။",
        "viewdeleted": "$1 ကို ကြည့်မည်လော။",
        "restorelink": "{{PLURAL:$1|ဖျက်လိုက်သည့်တည်းဖြတ်မှုတစ်ခု|ဖျက်လိုက်သည့် တည်းဖြတ်မှု $1 ခု}}",
        "nstab-template": "တမ်းပလိတ်",
        "nstab-help": "အကူအညီ စာမျက်နှာ",
        "nstab-category": "ကဏ္ဍ",
+       "mainpage-nstab": "ဗဟိုစာမျက်နှာ",
        "nosuchaction": "ဤကဲ့သို့ ဆောင်ရွက်ချက်မျိုး မရှိပါ။",
        "nosuchspecialpage": "ဤကဲ့သို့သော အထူးစာမျက်နှာ မရှိပါ",
        "error": "အမှား",
        "databaseerror": "ဒေတာဘေ့စ် အမှား",
+       "databaseerror-function": "လုပ်ဆောင်ချက် - $1",
+       "databaseerror-error": "အမှား - $1",
        "readonly": "ဒေတာဘေ့စ် ပိတ်ထားသည်။",
        "missing-article": "စာမျက်နှာ \"$1\" မှ $2 ကို ရှာတွေ့သင့်သည်ဖြစ်သော်လည်း ဒေတာဘေ့(စ်) သည် ရှာမတွေ့ပါ။\n\nယင်းသည် ဖျက်ထားပြီးသား diff သို့မဟုတ် မှတ်တမ်းလင့် တစ်ခုကြောင့် ဖြစ်လေ့ရှိသည်။\n\nယင်းသို့မဟုတ်ပါက သင်သည် ဤဆော့ဝဲအတွင်းမှ အမှားတစ်ခုကို တွေ့နေခြင်းဖြစ်ကောင်းဖြစ်မည်။ ဤသည်ကို [[Special:ListUsers/sysop|administrator]] သို့ ကျေးဇူးပြု၍ သတင်းပို့ပေးပါ။ URL လင့်ကိုပါ ထည့်သွင်းဖော်ပြပေးပါရန်။",
        "missingarticle-rev": "(တည်းဖြတ်မူ#: $1)",
        "directorycreateerror": "လမ်းညွှန် \"$1\" ကို ဖန်တီးမရနိုင်ပါ။",
        "filenotfound": "ဖိုင် \"$1\" ကို ရှာမတွေ့ပါ။",
        "formerror": "အမှား - ဖောင်သွင်းနိုင်ခြင်းမရှိပါ",
+       "cannotdelete": "\"$1\" စာမျက်နှာ သို့မဟုတ် ဖိုင်ကို ဖျက်၍ မရပါ။\nတစ်စုံတစ်ဦးမှ ဖျက်နှင့်ပြီး ဖြစ်နိုင်ပါသည်။",
+       "cannotdelete-title": "\"$1\" စာမျက်နှာကို ဖျက်၍ မရပါ",
        "badtitle": "ညံ့ဖျင်းသော ခေါင်းစဉ်",
        "badtitletext": "တောင်းဆိုထားသော စာမျက်နှာ ခေါင်းစဉ်သည် တရားမဝင်ပါ (သို့) ဗလာဖြစ်နေသည် (သို့) အခြားဘာသာများ(inter-language or inter-wiki title)သို့ မှားယွင်းစွာ လင့်ချိတ်ထားသည်။",
        "viewsource": "ရင်းမြစ်ကို ကြည့်ရန်",
-       "protectedpagetext": "ဤစာမျက်နှာအား တည်းဖြတ်မရနိုင်ရန် ထိန်းသိမ်းထားသည်။",
+       "viewsource-title": "$1၏ ရင်းမြစ်ကို ကြည့်ရန်",
+       "protectedpagetext": "ဤစာမျက်နှာအား တည်းဖြတ်ခြင်းနှင့် အခြားလုပ်ဆောင်မှုများ မလုပ်ဆောင်နိုင်အောင် ကာကွယ်ထားသည်။",
        "namespaceprotected": "'''$1''' စာညွှန်းဖြင့် စာမျက်နှာကို တည်းဖြတ်ရန် ခွင့်ပြုချက် မရှိပါ။",
+       "mycustomcssprotected": "ဤ CSS စာမျက်နှာကို သင်တည်းဖြတ်ပြင်ဆင်ခွင့် မရှိပါ။",
+       "mycustomjsprotected": "ဤ JavaScript စာမျက်နှာကို သင်တည်းဖြတ်ပြင်ဆင်ခွင့် မရှိပါ။",
+       "myprivateinfoprotected": "သင်သည် သင်၏ ပုဂ္ဂိုလ်ရေးရာ အချက်အလက်များကို ပြင်ဆင်ခွင့် မရှိပါ။",
+       "mypreferencesprotected": "သင်သည် သင်၏ ရွေးချယ်စရာများကို ပြင်ဆင်ခွင့်မရှိပါ။",
        "ns-specialprotected": "အထူးစာမျက်နှာများကို တည်းဖြတ်မရနိုင်ပါ။",
+       "exception-nologin": "အကောင့် မဝင်ထားပါ",
+       "exception-nologin-text": "ဤစာမျက်နှာကို ကြည့်ရှုနိုင်ရန် သို့မဟုတ် အခြားလုပ်ဆောင်ချက်များ ခွင့်ပြုချက်ရရှိရန် ကျေးဇူးပြု၍ အကောင့်ဝင်ပါ။",
+       "exception-nologin-text-manual": "ဤစာမျက်နှာကို ဝင်ရောက်နိုင်ရန် သို့မဟုတ် အခြားလုပ်ဆောင်ချက်များ ရရှိနိုင်ရန် ကျေးဇူးပြု၍ $1 ပါ။",
        "virus-unknownscanner": "အမည်မသိအန်တီဗိုင်းရပ်စ် -",
-       "logouttext": "သင်သည် လော့ဂ်အောက် လုပ်လိုက်ပြီဖြစ်သည်။\nသင့်အနေနှင့် ဤ {{SITENAME}} ဝက်ဘ်ဆိုက်ဒ်ကို အမည်မသိ အသုံးပြုသူ အနေနှင့် ဆက်လက် အသုံးပြုနိုင်သည်။ သို့မဟုတ် ယခင် အသုံးပြုသူ အမည် သို့ အသုံးပြုသူ အခြားအမည်တစ်ခုဖြင့် <span class='plainlinks'>[$1 နောက်တစ်ကြိမ် လော့ဂ်အင်ပြန်ဝင်]</span> နိုင်သည်။\nသင်၏ ဘရောက်ဆာမှ cache ကို ရှင်းလင်းသည့် အချိန် အထိ အချို့သော စာမျက်နှာ များသည် သင် လော့ဂ်အင် ဝင်ထားစဉ်က အတိုင်းပင် ဆက်လက် ပြသနေမည်ဖြစ်သည်။",
+       "logouttext": "<strong>သင်သည် လော့ဂ်အောက် လုပ်လိုက်ပြီဖြစ်သည်။</strong>",
+       "welcomeuser": "ကြိုဆိုပါတယ် $1!",
+       "welcomecreation-msg": "သင့်အကောင့်အား ဖန်တီးပြီးပါပြီ။\nသင် ဆန္ဒရှိပါက {{SITENAME}} [[Special:Preferences|ပုဂ္ဂိုလ်ရေးအချက်အလက်များ]]ကို ပြောင်းလဲနိုင်ပါသည်။",
        "yourname": "အသုံးပြုသူအမည် -",
+       "userlogin-yourname": "အသုံးပြုသူအမည်",
+       "userlogin-yourname-ph": "သင်၏ အသုံးပြုသူအမည် ရိုက်ထည့်ပါ",
+       "createacct-another-username-ph": "အသုံးပြုသူအမည် ရိုက်ထည့်ပါ",
        "yourpassword": "စကားဝှက် -",
+       "userlogin-yourpassword": "စကားဝှက်",
+       "userlogin-yourpassword-ph": "သင်၏ စကားဝှက်ကို ရိုက်ထည့်ပါ",
+       "createacct-yourpassword-ph": "စကားဝှက် ရိုက်ထည့်ပါ",
        "yourpasswordagain": "စကားဝှက် ပြန်​ရိုက်​ပါ -",
-       "remembermypassword": "ဤ​ကွန်​ပျူ​တာ​တွင်​ ကျွနု်ပ်ကို​မှတ်​ထား​ရန် (အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}ကြာ)",
+       "createacct-yourpasswordagain": "စကားဝှက်ကို အတည်ပြုပါ",
+       "createacct-yourpasswordagain-ph": "စကားဝှက်ကို ထပ်မံ ရိုက်ထည့်ပါ",
+       "remembermypassword": "ဤ​ကွန်​ပျူ​တာ​တွင်​ ကျွန်ုပ်ကို ​မှတ်​ထား​ရန် (အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}ကြာ)",
+       "userlogin-remembermypassword": "Log in ဝင်ထားမည်",
+       "userlogin-signwithsecure": "လုံခြုံသော ဆက်သွယ်မှုကို သုံးမည်",
        "yourdomainname": "သင့်ဒိုမိန်း -",
+       "password-change-forbidden": "ဤဝီကီတွင် စကားဝှက်များကို ပြောင်းလဲ၍ မရပါ။",
        "login": "Log in ဝင်ရန်",
-       "nav-login-createaccount": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\9cá\80¯á\80\95á\80ºရန်",
-       "userlogin": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\9cá\80¯á\80\95á\80ºရန်",
+       "nav-login-createaccount": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\96á\80\94á\80ºá\80\90á\80®á\80¸ရန်",
+       "userlogin": "Log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º/ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80º á\80\96á\80\94á\80ºá\80\90á\80®á\80¸ရန်",
        "userloginnocreate": "Log in ဝင်ရန်",
        "logout": "ထွက်ရန်",
        "userlogout": "ထွက်ရန်",
-       "notloggedin": "logged in ဝင်မထားပါ",
+       "notloggedin": "log in ဝင်မထားပါ",
+       "userlogin-noaccount": "အကောင့် မရှိဘူးလား?",
+       "userlogin-joinproject": "{{SITENAME}} ကို ချိတ်ဆက်ရန်",
        "nologin": "အကောင့်မရှိဘဲ ဖြစ်နေပါသလား။ $1။",
-       "nologinlink": "á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\9cá\80¯á\80\95á\80ºရန်",
-       "createaccount": "အကောင့်လုပ်ရန်",
+       "nologinlink": "á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯ á\80\96á\80\94á\80ºá\80\90á\80®á\80¸ရန်",
+       "createaccount": "အကောင့် ဖန်တီးရန်",
        "gotaccount": "အကောင့်ရှိပြီးသားလား။ $1။",
        "gotaccountlink": "Log in ဝင်ရန်",
        "userlogin-resetlink": "Login ဝင်သည့် အသေးစိတ်တို့ကို မေ့သွားပါသလား?",
-       "createaccountmail": "အီးမေးဖြင့်",
+       "userlogin-resetpassword-link": "စကားဝှက် မေ့နေသလား?",
+       "userlogin-helplink2": "log in အကူအညီ",
+       "userlogin-loggedin": "သင်သည် {{GENDER:$1|$1}} အနေဖြင့် လော့အင်ဝင်ထားပြီး ဖြစ်သည်။ အခြားအသုံးပြုသူ အနေဖြင့် ဝင်ရောက်ရန် အောက်ပါပုံစံကို အသုံးပြုပါ။",
+       "userlogin-createanother": "အခြားအကောင့် ဖန်တီးရန်",
+       "createacct-emailrequired": "အီးမေး လိပ်စာ",
+       "createacct-emailoptional": "အီးမေး လိပ်စာ (ဖြည့်လိုက)",
+       "createacct-email-ph": "သင့်အီးမေး လိပ်စာ ရိုက်ထည့်ပါ",
+       "createacct-another-email-ph": "အီးမေး လိပ်စာ ရိုက်ထည့်ပါ",
+       "createaccountmail": "ယာယီ ကျပန်းစကားဝှက်ကို သီးသန့် အီးမေးလ်လိပ်စာသို့ ပေးပို့အသုံးပြုရန်",
+       "createacct-realname": "နာမည်ရင်း (ဖြည့်လိုက)",
        "createaccountreason": "အ​ကြောင်း​ပြ​ချက် -",
+       "createacct-reason": "အကြောင်းပြချက်",
+       "createacct-reason-ph": "သင်ဘာကြောင့် အခြားအကောင့် ဖန်တီးချင်တာလဲ",
+       "createacct-submit": "သင့်အကောင့်ကို ဖန်တီးရန်",
+       "createacct-another-submit": "အကောင့် ဖန်တီးရန်",
+       "createacct-benefit-heading": "{{SITENAME}} အား သင်ကဲ့သို့သော လူများဖြင့် ပြုလုပ်ထားသည်။",
+       "createacct-benefit-body1": "{{PLURAL:$1|တည်းဖြတ်မှု|တည်းဖြတ်မှုများ}}",
+       "createacct-benefit-body2": "{{PLURAL:$1|စာမျက်နှာ|စာမျက်နှာများ}}",
+       "createacct-benefit-body3": "မကြာမီက {{PLURAL:$1|ဆောင်ရွက်သူ|ဆောင်ရွက်သူများ}}",
        "badretype": "သင်ထည့်သွင်းလိုက်သော စကားဝှက်များ ကိုက်ညီမှု မရှိပါ။",
+       "usernameinprogress": "ဤအသုံးပြုသူအမည်အတွက် အကောင့်ကို ဖန်တီးနေဆဲဖြစ်သည်။\nကျေးဇူးပြု၍ ခတ္တစောင့်ဆိုင်းပါ။",
        "userexists": "သင်ရွေးသော အသုံးပြုသူအမည်မှာ ရှိပြီးဖြစ်သည်။\nအခြား အမည် ရွေးပါ။",
        "loginerror": "Login ဝင်ခြင်း အမှား",
+       "createacct-error": "အကောင့်ဖန်တီးမှု အမှား",
        "createaccounterror": "ဤအကောင့်ကို မဖန်တီးနိုင်ပါ - $1",
        "noname": "တရားဝင် အသုံးပြုသူအမည်ကို မသတ်မှတ်ရသေးပါ။",
        "loginsuccesstitle": "Login ဝင်​ခြင်း အောင်မြင်သည်။",
        "passwordtooshort": "စကားဝှက်တွင် စကားလုံး အနည်းဆုံး {{PLURAL:$1|တစ်လုံး|$1 လုံး}} ရှိရမည်။",
        "password-name-match": "သင့်စကားဝှက်သည် အသုံးပြုသူအမည်နှင့် အတူတူမဖြစ်စေရဘဲ ကွဲပြားရမည်။",
        "password-login-forbidden": "ဤအသုံးပြုသူအမည်နှင့် စကားဝှက်အား အသုံးပြုခြင်းကို တားမြစ်ထားသည်။",
-       "mailmypassword": "á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80¡á\80\9eá\80\85á\80ºá\80\80á\80­á\80¯ á\80¡á\80®á\80¸á\80\99á\80±á\80¸ á\80\95á\80­á\80¯á\80·ရန်",
+       "mailmypassword": "á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80\80á\80­á\80¯ á\80\95á\80¼á\80\94á\80ºá\80\81á\80»á\80­á\80\94á\80ºရန်",
        "passwordremindertitle": "{{SITENAME}} အတွက် ယာယီစကားဝှက်အသစ်",
        "noemail": "အသုံးပြုသူ \"$1\" အတွက် မည်သည့်အီးမေးလိပ်စာမှ မှတ်သားထားခြင်း မရှိပါ။",
        "noemailcreate": "တရာဝင်အီးမေးလိပ်စာ ပေးရန် လိုအပ်သည်",
        "mailerror": "မေးပို့ခြင်း အမှား - $1",
-       "emailauthenticated": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\9cá\80­á\80\95á\80ºá\80\85á\80¬á\80\90á\80\8aá\80ºá\80\9bá\80¾á\80­á\80\80á\80¼á\80±á\80¬á\80\84á\80ºá\80¸ $2 á\80\94á\80±á\80· $3 á\80¡á\80\81á\80»á\80­á\80\94á\80ºá\80\80 á\80¡á\80\90á\80\8aá\80ºá\80\95á\80¼á\80¯á\80\9cá\80­á\80¯á\80\80်သည်။",
+       "emailauthenticated": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\9cá\80­á\80\95á\80ºá\80\85á\80¬á\80\80á\80­á\80¯ $2 á\80\94á\80±á\80· $3 á\80¡á\80\81á\80»á\80­á\80\94á\80ºá\80\90á\80½á\80\84á\80º á\80¡á\80\90á\80\8aá\80ºá\80\95á\80¼á\80¯á\80\95á\80¼á\80®á\80¸ á\80\96á\80¼á\80\85်သည်။",
        "emailconfirmlink": "အီးမေးကိုအတည်ပြုပါ",
        "accountcreated": "အကောင့်ဖန်တီးပြီးပါပြီ",
-       "accountcreatedtext": "$1 အတွက် အသုံးပြုသူအကောင့်တစ်ခု ဖန်တီးပြီးဖြစ်သည်။",
+       "accountcreatedtext": "[[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|ဆွေးနွေး]]) အတွက် အကောင့်ကို ဖန်တီးပြီး ဖြစ်သည်။",
        "createaccount-title": "{{SITENAME}} အတွက် အကောင့်ပြုလုပ်ခြင်း",
-       "login-throttled": "သင်သည် login ဝင်ရန် အကြိမ်မြောက်မြားစွာ အားထုတ်ခဲ့ပြီးဖြစ်သည်။\nကျေးဇူးပြု၍ ထပ်မဝင်ခင် စောင့်ပေးပါ။",
+       "login-throttled": "သင်သည် login ဝင်ရန် အကြိမ်မြောက်မြားစွာ အားထုတ်ခဲ့ပြီးဖြစ်သည်။\nကျေးဇူးပြု၍ ထပ်မဝင်ခင် $1 စောင့်ပေးပါ။",
        "login-abort-generic": "Login ဝင်ခြင်း မအောင်မြင်ပါ - ထွက်သွားပြီ",
        "loginlanguagelabel": "ဘာသာ: $1",
        "pt-login": "အကောင့်ဝင်ရန်",
+       "pt-login-button": "အကောင့်ဝင်ရန်",
        "pt-createaccount": "အကောင့် ဖန်တီးရန်",
+       "pt-userlogout": "အကောင့်ထွက်ရန်",
        "changepassword": "စကားဝှက် ပြောင်းရန်",
-       "resetpass_announce": "á\80\9eá\80\84á\80ºá\80\9eá\80\8aá\80º á\80\9aá\80¬á\80\9aá\80® á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80\96á\80¼á\80\84á\80·á\80º á\80\9dá\80\84á\80ºá\80\9bá\80±á\80¬á\80\80á\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b\ná\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80º á\80¡á\80\9eá\80\85á\80ºá\80¡á\80¬á\80¸ á\80¤á\80\94á\80±á\80\9bá\80¬á\80\90á\80½á\80\84á\80ºá\80\9bá\80­á\80¯á\80\80á\80ºá\80\95á\80« :",
+       "resetpass_announce": "á\80\9cá\80±á\80¬á\80·á\80\82á\80ºá\80¡á\80\84á\80ºá\80\9dá\80\84á\80ºá\80\9bá\80±á\80¬á\80\80á\80ºá\80\81á\80¼á\80\84á\80ºá\80¸ á\80\95á\80¼á\80®á\80¸á\80\99á\80¼á\80±á\80¬á\80\80á\80ºá\80\9bá\80\94á\80º á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80¡á\80\9eá\80\85á\80º á\80\95á\80±á\80¸á\80\9bá\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b",
        "resetpass_header": "အကောင့်စကားဝှက်ပြောင်းရန်",
        "oldpassword": "စကားဝှက် အဟောင်း -",
        "newpassword": "စကားဝှက် အသစ် -",
        "retypenew": "စကားဝှက် အသစ်ကို ထပ်ရိုက်ပါ -",
        "resetpass_submit": "စကားဝှက်ကို သတ်မှတ်ပြီးနောက် Log in ဝင်ရန်",
-       "changepassword-success": "သင့်စကားဝှက်ကို အောင်မြင်စွာ ပြောင်းလဲပြီးပါပြီ။ အခု Log in ဝင်နေပါပြီ...",
+       "changepassword-success": "သင့်စကားဝှက်ကို အောင်မြင်စွာ ပြောင်းလဲပြီးပါပြီ။",
+       "botpasswords-label-appid": "ဘော့အမည်-",
+       "botpasswords-label-create": "ဖန်တီး",
+       "botpasswords-label-cancel": "မလုပ်တော့ပါ",
+       "botpasswords-label-delete": "ဖျက်",
+       "botpasswords-label-resetpassword": "စကားဝှက်ကို ပြန်ချိန်ရန်",
        "resetpass_forbidden": "စကားဝှက် ပြောင်းမရနိုင်ပါ",
        "resetpass-no-info": "ဤစာမျက်နှာကို တိုက်ရိုက်အသုံးပြုနိုင်ရန်အတွက် Log in ဝင်ထားရပါမည်။",
        "resetpass-submit-loggedin": "စကားဝှက်ပြောင်းရန်",
        "resetpass-submit-cancel": "မလုပ်တော့ပါ",
        "resetpass-temp-password": "ယာယီစကားဝှက် -",
+       "passwordreset": "စကားဝှက်အသစ် ပြုလုပ်ရန်",
        "passwordreset-username": "အသုံးပြုသူအမည် :",
        "passwordreset-email": "အီးမေး လိပ်စာ :",
+       "changeemail": "အီးမေးလိပ်စာ ပြင်ဆင် သို့ ဖယ်ရှားရန်",
        "bold_sample": "စာလုံးမည်း",
        "bold_tip": "စာလုံးမည်း",
        "italic_sample": "စာလုံး အစောင်း",
        "sig_tip": "အချိန်ပါပြသော သင့်လက်မှတ်",
        "hr_tip": "မျဉ်းလဲ (စိစစ်သုံးရန်)",
        "summary": "အ​ကျဉ်း​ချုပ်​ -",
-       "subject": "အကြောင်းအရာ/ခေါင်းကြီးပိုင်း -",
+       "subject": "အကြောင်းအရာ -",
        "minoredit": "အရေးမကြီးသော ​ပြင်​ဆင်​မှု ​ဖြစ်​သည်​",
        "watchthis": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
        "savearticle": "ဤစာမျက်နှာကို သိမ်းရန်",
        "preview": "နမူနာ",
        "showpreview": "န​မူ​နာ​ပြ​ရန်",
        "showdiff": "ပြင်​ဆင်​ထား​သည်​များ​ကို​ ပြရန်",
-       "anoneditwarning": "'''သတိပေးချက် - ''' သင်သည် logged in ဝင်မထားပါ။\nဤစာမျက်နှာ၏ တည်းဖြတ်မှတ်တမ်းတွင် သင့် IP address ကို မှတ်သားထားမည် ဖြစ်သည်။",
+       "anoneditwarning": "<strong>သတိပေးချက် - </strong> သင်သည် လော့ဂ်အင် ဝင်မထားပါ။ သင်တည်းဖြတ်မှု ပြုလုပ်ပါက သင့်အိုင်ပီလိပ်စာကို မည်သူမဆို တွေ့မြင်နိုင်မည်။ အကယ်၍ သင် <strong>[$1 လော့ဂ်အင်ဝင်]</strong> သို့မဟုတ် <strong>[$2 အကောင့်တစ်ခု ဖန်တီး]</strong>ပါက၊ သင့်တည်းဖြတ်မှုများသည် သင့်အမည်နှင့် တွဲဖက်မှတ်သားမည် ဖြစ်သည်။",
        "anonpreviewwarning": "သင်သည် logged in ဝင်မထားပါ။ သိမ်းဆည်းမည် ဆိုပါက သင်၏IP အား ဤစာမျက်နှာ မှတ်တမ်းတွင် မှတ်သားထားမည်ဖြစ်ပါသည်။",
        "missingcommenttext": "ကျေးဇူးပြု၍ အောက်တွင် မှတ်ချက်တစ်ခုရေးပါ။",
        "summary-preview": "အ​ကျဉ်း​ချုပ်​န​မူ​နာ:",
-       "subject-preview": "အကြောင်းအရာ/ခေါင်းကြီးပိုင်း နမူနာ -",
+       "subject-preview": "အကြောင်းအရာ နမူနာ -",
        "blockedtitle": "အသုံးပြုသူကို ပိတ်ပင်ထားသည်",
        "blockednoreason": "အကြောင်းပြချက် မပေးထားပါ",
        "whitelistedittext": "စာမျက်နှာများကို တည်းဖြတ်ရန် $1ရမည်။",
        "newarticle": "(အသစ်)",
        "newarticletext": "သင်သည် မရှိသေးသော စာမျက်နှာလင့် ကို ရောက်လာခြင်းဖြစ်သည်။\nစာမျက်နှာအသစ်စတင်ရန် အောက်မှ သေတ္တာထဲတွင် စတင်ရိုက်ထည့်ပါ (နောက်ထပ် သတင်းအချက်အလက်များအတွက်[$1 အကူအညီ စာမျက်နှာ]ကို ကြည့်ပါ)။\nမတော်တဆရောက်လာခြင်း ဖြစ်ပါက ဘရောက်ဆာ၏ နောက်ပြန်ပြန်သွားသော'''back''' ခလုတ်ကို နှိပ်ပါ။",
        "noarticletext": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]၊ သို့မဟုတ် [{{fullurl:{{FULLPAGENAME}}|action=edit}} ဤစာမျက်နှာကို တည်းဖြတ်နိုင်သည်]</span>။",
-       "noarticletext-nopermission": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ သို့မဟုတ် <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]</span>။",
+       "noarticletext-nopermission": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ သို့မဟုတ် <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]</span>။ သို့သော် ဤစာမျက်နှာကို ဖန်တီးရန် သင့်တွင် အခွင့်အရေး မရှိပါ။",
        "note": "'''မှတ်ချက် -'''",
-       "previewnote": "'''ဤသည်မှာ နမူနာ ကြည့်နေခြင်းသာဖြစ်ကြောင်း မမေ့ပါနှင့်။'''\nသင်ပြောင်းလဲထားသည်များကို မသိမ်းရသေးပါ။",
+       "previewnote": "<strong>ဤသည်မှာ နမူနာ ကြည့်နေခြင်းသာဖြစ်ကြောင်း မမေ့ပါနှင့်။</strong>\nသင်ပြောင်းလဲထားသည်များကို မသိမ်းရသေးပါ။",
+       "continue-editing": "တည်းဖြတ်ဧရိယာသို့ သွားရန်",
        "editing": "$1 ကို တည်းဖြတ်နေသည်",
        "editingsection": "$1 (အပိုင်း) ကို ပြင်ဆင်နေသည်။",
        "editingcomment": "$1 (အပိုင်းသစ်) ကို ပြင်ဆင်နေသည်။",
        "yourtext": "သင့်စာသား",
        "storedversion": "သိမ်းဆည်းထားသောမူ",
        "yourdiff": "ကွဲပြားချက်များ",
-       "copyrightwarning": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးကို $2 အောက်တွင် ဖြန့်ဝေရန် ဆုံးဖြတ်ပြီး ဖြစ်သည်ကို ကျေးဇူးပြု၍ သတိပြုပါ။။ (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ။)\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည်။\n'''မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။'''",
-       "copyrightwarning2": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးသည် အခြားပုံပိုးသူများ၏ တည်းဖြတ်၊ ပြောင်းလဲ၊ ဖယ်ရှားခံရနိုင်သည်ကို သတိပြုပါ။\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည် (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ)။\n'''မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။'''",
+       "copyrightwarning": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးကို $2 အောက်တွင် ဖြန့်ဝေရန် ဆုံးဖြတ်ပြီး ဖြစ်သည်ကို ကျေးဇူးပြု၍ သတိပြုပါ။။ (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ။)\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည်။\n<strong>မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။</strong>",
+       "copyrightwarning2": "{{SITENAME}} တွင် ရေးသားမှုအားလုံးသည် အခြားပုံပိုးသူများ၏ တည်းဖြတ်၊ ပြောင်းလဲ၊ ဖယ်ရှားခံရနိုင်သည်ကို သတိပြုပါ။\nအကယ်၍ သင့်ရေးသားချက်များကို အညှာအတာမရှိ တည်းဖြတ်ခံရခြင်း၊ စိတ်တိုင်းကျ ဖြန့်ဝေခံရခြင်းတို့ကို အလိုမရှိပါက ဤနေရာတွင် မတင်ပါနှင့်။<br />\nသင်သည် ဤဆောင်းပါးကို သင်ကိုယ်တိုင်ရေးသားခြင်း၊ သို့မဟုတ် အများပြည်သူဆိုင်ရာဒိုမိန်းများ၊ ယင်းကဲ့သို့ လွတ်လပ်သည့် ရင်းမြစ်မှ ကူးယူထားခြင်း ဖြစ်ကြောင်းလည်း ဝန်ခံ ကတိပြုပါသည် (အသေးစိတ်ကို $1 တွင်ကြည့်ပါ)။\n<strong>မူပိုင်ခွင့်ရှိသော စာ၊ပုံများကို ခွင့်ပြုချက်မရှိဘဲ မတင်ပါနှင့်။</strong>",
        "templatesused": "{{PLURAL:$1|တမ်းပလိတ်|တမ်းပလိတ်}} ခုကို ဤစာမျက်နှာကို သုံးထားသည် -",
        "templatesusedpreview": "{{PLURAL:$1|တမ်းပလိတ်|တမ်းပလိတ်}} ခုကို ဤနမူနာတွင် သုံးထားသည် -",
        "template-protected": "(ကာကွယ်ထားသည်)",
        "hiddencategories": "ဤစာမျက်နှာသည် {{PLURAL:$1|ဝှက်ထားသော ကဏ္ဍတစ်ခု|ဝှက်ထားသော ကဏ္ဍ $1 ခု}} ၏ အဖွဲ့ဝင် ဖြစ်သည်။",
        "nocreate-loggedin": "သင်သည် စာမျက်နှာအသစ် ဖန်တီးခွင့်မရှိပါ။",
        "sectioneditnotsupported-title": "ခေါင်းစဉ်ခွဲအလိုက် တည်းဖြတ်ခြင်းကို မထောက်ပံ့ထားပါ",
-       "permissionserrors": "ခွင့်ပြုချက်အမှားများ",
+       "permissionserrors": "ခွင့်ပြုချက်အမှား",
        "permissionserrorstext": "အောက်ပါ {{PLURAL:$1|အကြောင်းပြချက်|အကြောင်းပြချက်များ}}ကြောင့် ထိုအရာအတွက် ခွင့်ပြုချက်မရှိပါ -",
        "permissionserrorstext-withaction": "အောက်ပါ အကြောင်းပြချက် {{PLURAL:$1|ခု|ခု}} ကြောင့် $2 အတွက် ခွင့်ပြုချက်မရှိပါ -",
        "recreate-moveddeleted-warn": "'''သတိပေးချက်။ သင်သည် ယခင်က ဖျက်ထားသော စာမျက်နှာတစ်ခုကို ပြန်လည်ဖန်တီးနေသည်။'''\n\nသင့်အနေနှင့် ဤစာမျက်နှာကို ဆက်လက်တည်းဖြတ်ရန် သင့်တော်မည် မသင့်တော်မည်ကို စဉ်းစားသင့်သည်။\nဖျက်ထားခြင်း နှင့် ရွှေ့ထားခြင်းတို့၏ မှတ်တမ်းကို သင့်အတွက် အလွယ်တကူ ကိုးကားနိုင်ရန် ဖော်ပြထားသည်။",
        "post-expand-template-argument-warning": "'''သတိပေးချက် -''' ဤစာမျက်နှာတွင် ပမာဏအားဖြင့် ကြီးမားကျယ်ပြန့်သော template argument တစ်ခုပါဝင်သည်။\nယင်း arguments များကို ဖယ်ထုတ်လိုက်သည်။",
        "post-expand-template-argument-category": "ဖယ်ထုတ်ထားသော template arguments များပါဝင်သည့် စာမျက်နှာများ",
        "parser-template-loop-warning": "တမ်းပလိတ်များ လှည့်ပတ်ဆက်စပ် နေသည်ကို တွေ့ရသည်။ [[$1]]",
+       "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|ဆွေးနွေး]]) ၏ $1 ပြင်ဆင်ချက် $1 ကို ပြန်လည်ပယ်ဖျက်လိုက်သည်",
        "viewpagelogs": "ဤစာမျက်နှာအတွက် မှတ်တမ်းများကို ကြည့်ရန်",
        "nohistory": "ဤစာမျက်နှာတွင် တည်းဖြတ်မှု ရာဇဝင်မရှိပါ",
        "currentrev": "နောက်ဆုံးမူ",
        "currentrev-asof": "$1 က နောက်ဆုံး တည်းဖြတ်မူ",
        "revisionasof": "$1 ရက်နေ့က မူ",
-       "revision-info": "$1 နေ့က $2 တည်းဖြတ်သည့်မူ",
+       "revision-info": "$1 နေ့က {{GENDER:$6|$2}}$7 တည်းဖြတ်သည့်မူ",
        "previousrevision": "မူဟောင်း",
        "nextrevision": "ပိုသစ်သော တည်းဖြတ်မူ →",
        "currentrevisionlink": "နောက်ဆုံး မူ",
        "last": "ယခုမတိုင်မီ",
        "page_first": "ပထမဆုံး",
        "page_last": "အနောက်ဆုံး",
-       "histlegend": "တည်းဖြတ်မူများကို နှိုင်းယှဉ်ရန် radio boxes လေးများကို မှတ်သားပြီးနောက် Enter ရိုက်ချပါ သို့ အောက်ခြေမှ ခလုတ်ကို နှိပ်ပါ။<br />\nLegend: '''({{int:cur}})''' = နောက်ဆုံးမူနှင့် ကွဲပြားချက် '''({{int:last}})''' = ယင်းရှေ့မူနှင့် ကွဲပြားချက်, '''{{int:minoreditletter}}''' = အရေးမကြီးသော ပြုပြင်မှု.",
+       "histlegend": "တည်းဖြတ်မူများကို နှိုင်းယှဉ်ရန် radio boxes လေးများကို မှတ်သားပြီးနောက် Enter ရိုက်ချပါ သို့ အောက်ခြေမှ ခလုတ်ကို နှိပ်ပါ။<br />\nLegend: <strong>({{int:cur}})</strong> = နောက်ဆုံးမူနှင့် ကွဲပြားချက် <strong>({{int:last}})</strong> = ယင်းရှေ့မူနှင့် ကွဲပြားချက်, <strong>{{int:minoreditletter}}</strong> = အရေးမကြီးသော ပြုပြင်မှု.",
        "history-fieldset-title": "ရာဇဝင်ရှာကြည့်ရန်",
        "history-show-deleted": "ဖျက်ထားသည်များသာ",
-       "histfirst": "​အစောဆုံး",
-       "histlast": "á\80\94á\80±á\80¬á\80\80်ဆုံး",
+       "histfirst": "အဟောင်းဆုံး",
+       "histlast": "á\80¡á\80\9eá\80\85်ဆုံး",
        "historyempty": "(ဘာမှမရှိ)",
        "history-feed-title": "မူရာဇဝင်မှတ်တမ်း",
        "history-feed-item-nocomment": "$2 က $1",
        "rev-deleted-comment": "(တည်းဖြတ်မှုအတိုချုပ် ဖယ်ရှားပြီး)",
-       "rev-deleted-user": "(အသုံပြုသူအမည် ဖယ်ရှားပြီး)",
-       "rev-delundel": "á\80\95á\80¼á\80\9bá\80\94á\80º/á\80\9dá\80¾á\80\80á\80ºရန်",
+       "rev-deleted-user": "(á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80\99á\80\8aá\80º á\80\96á\80\9aá\80ºá\80\9bá\80¾á\80¬á\80¸á\80\95á\80¼á\80®á\80¸)",
+       "rev-delundel": "á\80¡á\80\99á\80¼á\80\84á\80ºá\80\95á\80¯á\80¶á\80\85á\80¶ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
        "rev-showdeleted": "ပြ",
        "revisiondelete": "မူများကို ဖျက်ရန်/မဖျက်တော့ရန်",
        "revdelete-nooldid-title": "တရားမဝင်သော မူအမည်",
        "revdelete-no-file": "ဖော်ပြထားသောဖိုင် မရှိပါ။",
        "revdelete-show-file-submit": "မှန်",
        "revdelete-legend": "မြင်နိုင်စွမ်းရှိမှုတို့အား ကန့်သတ်ခြင်းကို သတ်မှတ်ရန်",
-       "revdelete-hide-text": "တည်းဖြတ်မူမှ စာသားများကို ဝှက်ရန်",
+       "revdelete-hide-text": "တည်းဖြတ်မူမှ စာသား",
        "revdelete-hide-image": "ဖိုင်ပါ အေကြာင်းအရာများကို ဝှက်ရန်",
-       "revdelete-hide-comment": "á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\99á\80¾á\80¯á\80¡á\80\80á\80»á\80\89á\80ºá\80¸á\80\81á\80»á\80¯á\80\95á\80ºá\80\80á\80­á\80¯ á\80\9dá\80¾á\80\80်ရန်",
-       "revdelete-hide-user": "တည်းဖြတ်သူ၏ အသုံးပြုသူအမည်/IP address တို့ကို ဝှက်ရန်",
+       "revdelete-hide-comment": "á\80¡á\80\80á\80»á\80\89á\80ºá\80¸á\80\81á\80»á\80¯á\80\95á\80ºá\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90်ရန်",
+       "revdelete-hide-user": "တည်းဖြတ်သူ၏ အသုံးပြုသူအမည်/အိုင်ပီလိပ်စာ",
        "revdelete-radio-same": "(မပြောင်းလဲ)",
-       "revdelete-radio-set": "á\80\99á\80¾á\80\94်",
-       "revdelete-radio-unset": "á\80\99á\80¾á\80¬á\80¸",
+       "revdelete-radio-set": "á\80\9dá\80¾á\80\80်",
+       "revdelete-radio-unset": "á\80\99á\80¼á\80\84á\80º",
        "revdelete-unsuppress": "ပြန်လည်ထိန်းသိမ်းထားသော မူများမှ ကန့်သတ်ချက်များကို ဖယ်ရှားရန်",
        "revdelete-log": "အ​ကြောင်း​ပြ​ချက်:",
        "revdelete-submit": "ရွေးချယ်ထားသော {{PLURAL:$1|မူ|မူများ}}ကို သက်ရောက်စေရန်",
        "mergehistory-reason": "​ကြောင်း​ပြ​ချက် -",
        "mergelog": "ပေါင်းလိုက်သော မှတ်တမ်း",
        "revertmerge": "ပြန်ခွဲထုတ်ရန်",
-       "history-title": "\"$1\" ၏ တည်းဖြတ်မူ ရာဇဝင်များ",
+       "history-title": "\"$1\"၏ တည်းဖြတ်မှု ရာဇဝင်",
+       "difference-title": "\"$1\" ၏ တည်းဖြတ်မှု မူကွဲများ",
        "difference-multipage": "(စာမျက်နှာများကြားမှ ကွဲပြားချက်များ)",
        "lineno": "စာကြောင်း $1 -",
        "compareselectedversions": "ရွေးချယ်ထားသော မူများကို နှိုင်းယှဉ်ရန်",
        "notextmatches": "ဤခေါင်းစဉ်နှင့် ကိုက်ညီသောစာမျက်နှာမရှိပါ",
        "prevn": "နောက်သို့ {{PLURAL:$1|$1}}",
        "nextn": "ရှေ့သို့ {{PLURAL:$1|$1}}",
+       "prev-page": "ပြီးခဲ့သော စာမျက်နှာ",
+       "next-page": "နောက်စာမျက်နှာ",
        "prevn-title": "ပြီးခဲ့သောရလဒ် $1 {{PLURAL:$1|ခု|ခု}}",
        "nextn-title": "နောက်ထပ်ရလဒ် $1 {{PLURAL:$1|ခု|ခု}}",
        "shown-title": "စာမျက်နှာတစ်ခုလျှင် ရလဒ် $1 {{PLURAL:$1|ခု|ခု}} ပြရန်",
        "viewprevnext": "($1 {{int:မှ}} $2) အထိကြား ရလဒ် ($3) ခုကို ကြည့်ရန်",
        "searchmenu-exists": "'''ဤဝီကီတွင် \"[[:$1]]\" အမည်နှင့် စာမျက်နှာတစ်ခုရှိသည်။'''",
-       "searchmenu-new": "'''ဤဝီကီတွင် \"[[:$1]]\" အမည်နှင့် စာမျက်နှာကို ဖန်တီးပါ။'''",
+       "searchmenu-new": "<strong>ဤဝီကီတွင် \"[[:$1]]\" စာမျက်နှာကို ဖန်တီးပါ!</strong> {{PLURAL:$2|0=|သင့်ရှာဖွေမှုနှင့် စာမျက်နှာကိုလည်း ကြည့်ပါ။|ရှာဖွေမှု ရလဒ်များကိုလည်း ကြည်ါပါ။}}",
        "searchprofile-articles": "မာတိကာစာမျက်နှာများ",
        "searchprofile-images": "မာလတီမီဒီယာ",
        "searchprofile-everything": "အားလုံး",
        "search-section": "(အပိုင်း $1)",
        "search-suggest": "$1 ဟု ဆိုလိုပါသလား။",
        "search-interwiki-caption": "ညီအစ်မ ပရောဂျက်များ",
-       "search-interwiki-default": "ရလဒ် $1 ခု -",
+       "search-interwiki-default": "$1 မှ ရလဒ်များ -",
        "search-interwiki-more": "(နောက်ထပ်)",
        "search-relatedarticle": "ဆက်နွယ်သော",
        "searchrelated": "ဆက်နွယ်သော",
        "prefs-skin": "အသွင်အပြင်",
        "skin-preview": "နမူနာ",
        "datedefault": "မရွေးချယ်",
+       "prefs-user-pages": "အသုံးပြုသူ၏ စာမျက်နှာများ",
        "prefs-personal": "အသုံးပြုသူ ပရိုဖိုင်",
        "prefs-rc": "လတ်​တ​လောအ​ပြောင်း​အ​လဲ​",
        "prefs-watchlist": "စောင့်ကြည့်စာရင်း",
+       "prefs-editwatchlist": "စောင့်ကြည့်စာရင်းကို တည်းဖြတ်ရန်",
+       "prefs-editwatchlist-edit": "သင့်စောင့်ကြည့်စာရင်းရှိ ခေါင်းစဉ်များအား ကြည့်ရှုပြီး ဖယ်ရှားရန်",
+       "prefs-editwatchlist-raw": "စောင့်ကြည့်စာရင်း အကြမ်းကို တည်းဖြတ်ရန်",
+       "prefs-editwatchlist-clear": "သင့် စောင့်ကြည့်စာရင်းကို ရှင်းလင်းရန်",
        "prefs-watchlist-days": "စောင့်ကြည့်စာရင်းတွင် ပြရန်နေ့များ",
-       "prefs-watchlist-days-max": "Maximum $1 {{PLURAL:$1|day|days}}",
+       "prefs-watchlist-days-max": "အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}",
        "prefs-watchlist-edits": "ချဲ့ထားသော စောင့်ကြည့်စာရင်းတွင် ပြရန် အပြောင်းအလဲတို့၏ အများဆုံး အရေအတွက်",
        "prefs-watchlist-edits-max": "အများဆုံးအရေအတွက် - ၁ဝဝဝ",
        "prefs-watchlist-token": "စောင့်ကြည့်စာရင်း တိုကင် -",
        "prefs-misc": "အသေးအမွှား",
        "prefs-resetpass": "စကားဝှက် ပြောင်းရန်",
+       "prefs-changeemail": "အီးမေးလိပ်စာ ပြင်ဆင် သို့ ဖယ်ရှားရန်",
        "prefs-email": "အီးမေးအတွက် ရွေးချယ်စရာ",
        "prefs-rendering": "ပုံပန်းသွင်ပြင်",
        "saveprefs": "သိမ်းရန်",
-       "restoreprefs": "á\80\99á\80°á\80\9cá\80\86á\80\80á\80ºá\80\90á\80\84á\80ºá\80\99á\80»á\80¬á\80¸á\80\9eá\80­á\80¯á\80· á\80¡á\80¬á\80¸á\80\9cá\80¯á\80¶á\80¸ á\80\95á\80¼á\80\94á\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9bá\80\94á\80º",
+       "restoreprefs": "á\80\99á\80°á\80\9cá\80¡á\80\95á\80¼á\80\84á\80ºá\80¡á\80\86á\80\84á\80ºá\80¡á\80¬á\80¸á\80\9cá\80¯á\80¶á\80¸á\80\9eá\80­á\80¯á\80· á\80\95á\80¼á\80\94á\80ºá\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9bá\80\94á\80º (á\80¡á\80\95á\80­á\80¯á\80\84á\80ºá\80¸á\80¡á\80¬á\80¸á\80\9cá\80¯á\80¶á\80¸á\80\90á\80½á\80\84á\80º)",
        "prefs-editing": "တည်းဖြတ်ခြင်း",
        "rows": "အလျားလိုက်တန်း -",
        "columns": "ဒေါင်လိုက်တန်း -",
        "recentchangescount": "ပုံသေအားဖြင့် ပြရန် တည်းဖြတ်မှုအရေအတွက် -",
        "prefs-help-recentchangescount": "ဤသည်တွင်ပါဝင်သည်မှာ လတ်တလောအပြောင်းအလဲများ၊ စာမျက်နှာမှတ်တမ်းနှင့် မှတ်တမ်းများဖြစ်သည်။",
        "savedprefs": "သင့်ရွေးချယ်မှုတို့ကို သိမ်းပြီးပါပြီ။",
+       "savedrights": "{{GENDER:$1|$1}}၏ အသုံးပြု အခွင့်အရေးများကို သိမ်းပြီးပါပြီ။",
        "timezonelegend": "အချိန်ဇုန် -",
        "localtime": "ပြည်တွင်းအချိန် -",
-       "timezoneuseserverdefault": "á\80\86á\80¬á\80\97á\80¬á\80\95á\80¯á\80¶á\80\99á\80¾á\80\94á\80ºá\80¡á\80\81á\80»á\80­á\80\94á\80ºá\80\80á\80­á\80¯ á\80\9eá\80¯á\80¶á\80¸á\80\9bá\80\94á\80º",
+       "timezoneuseserverdefault": "á\80\9dá\80®á\80\80á\80®á\80¡á\80\95á\80¼á\80\84á\80ºá\80¡á\80\86á\80\84á\80ºá\80\80á\80­á\80¯ á\80\9eá\80¯á\80¶á\80¸á\80\9bá\80\94á\80º ($1)",
        "timezoneuseoffset": "အခြား (တန်ဖိုးသတ်မှတ်ပေးရန်)",
        "servertime": "ဆာဗာအချိန် -",
        "guesstimezone": "ဘရောက်ဇာမှ ဖြည့်ရန်",
        "timezoneregion-indian": "အိန္ဒိယသမုဒ္ဒရာ",
        "timezoneregion-pacific": "ပစိဖိတ်သမုဒ္ဒရာ",
        "allowemail": "အခြားအသုံးပြုသူများထံမှ အီးမေးများကို လက်ခံရန်",
-       "prefs-searchoptions": "ရှာဖွေရန် ရွေးချယ်မှု",
+       "prefs-searchoptions": "ရှာ​ဖွေ​ရန်​",
        "prefs-namespaces": "အမည်ညွှန်း",
        "default": "ပုံမှန်အားဖြင့်",
        "prefs-files": "ဖိုင်",
        "prefs-custom-js": "စိတ်ကြိုက် Javascript",
        "prefs-emailconfirm-label": "အီးမေးအတည်ပြုရန်",
        "youremail": "အီး​မေး -",
-       "username": "အသုံးပြုသူအမည် -",
-       "prefs-memberingroups": "{{PLURAL:$1|အုပ်စု|အုပ်စု}}၏ အဖွဲ့ဝင်",
+       "username": "{{GENDER:$1|အသုံးပြုသူအမည်}} -",
+       "prefs-memberingroups": "{{PLURAL:$1|အုပ်စု|အုပ်စုများ}}၏ {{GENDER:$2|အဖွဲ့ဝင်}}",
        "prefs-registration": "မှတ်ပုံတင်သည့် အချိန် -",
        "yourrealname": "နာမည်ရင်း -",
        "yourlanguage": "ဘာသာစကား -",
        "yournick": "လက်မှတ်အသစ် -",
        "badsig": "တရားမဝင်သည့် လက်မှတ်အကြမ်း။\nHTML tags ကို စစ်ဆေးပါ။",
        "badsiglength": "သင့်လက်မှတ်သည် ရှည်လွန်းနေပါသည်။\nယင်းသည် စာလုံး {{PLURAL:$1|လုံး|လုံး}}ထက် မရှည်ရပါ။",
-       "yourgender": "á\80\80á\80»á\80¬á\80¸/á\80\99 -",
-       "gender-unknown": "á\80\96á\80±á\80¬á\80ºá\80\95á\80¼á\80\99á\80\91á\80¬á\80¸",
-       "gender-male": "á\80\80á\80»á\80¬á\80¸",
-       "gender-female": "á\80\99",
+       "yourgender": "á\80\9eá\80\84á\80ºá\80\98á\80\9aá\80ºá\80\9cá\80­á\80¯ á\80\96á\80±á\80¬á\80ºá\80\95á\80¼á\80\85á\80±á\80\81á\80»á\80\84á\80ºá\80\95á\80«á\80\9eá\80\9cá\80²?",
+       "gender-unknown": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80¬á\80¸á\80\9bá\80\8aá\80ºá\80\8aá\80½á\80¾á\80\94á\80ºá\80¸á\80\9bá\80¬á\80\90á\80½á\80\84á\80º á\80\86á\80±á\80¬á\80·á\80\96á\80ºá\80\9dá\80²á\80\9cá\80ºá\80\9eá\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80\95á\80«á\80\80 á\80\98á\80\80á\80ºá\80\99á\80\9cá\80­á\80¯á\80\80á\80ºá\80\9eá\80\8aá\80·á\80º á\80\9cá\80­á\80\84á\80ºá\80¡á\80\9eá\80¯á\80¶á\80¸á\80¡á\80\94á\80¾á\80¯á\80\94á\80ºá\80¸á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9cá\80­á\80\99á\80·á\80ºá\80\99á\80\8aá\80º",
+       "gender-male": "á\80\9eá\80°á\80\9eá\80\8aá\80º á\80\9dá\80®á\80\80á\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\9eá\80\8aá\80º",
+       "gender-female": "á\80\9eá\80°á\80\99á\80\9eá\80\8aá\80º á\80\9dá\80®á\80\80á\80®á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\9eá\80\8aá\80º",
        "email": "အီးမေး",
        "prefs-help-email": "အီးမေးလ်လိပ်စာ ပေးမည် မပေးမည်မှာ သင့်သဘောသာ ဖြစ်ပါသည်။ သို့သော်လည်း သင် စကားဝှက်ကို မေ့သွားပါက စကားဝှက်ကို reset လုပ်ရန် အီးမေးလ်လိပ်စာ လိုအပ်ပါလိမ့်မည်။",
-       "prefs-help-email-others": "You can also choose to let others contact you by e-mail through a link on your user or talk page.\nသင့်ယူဆာစာမျက်နှာ သို့မဟုတ် ဆွေးနွေးရန်စာမျက်နှာရှိ လင့်မှတဆင့် သင့်ထံ အခြားသူများ အီးမေးမှဆက်သွယ်ရန်လည်း ရွေးချယ်နိုင်သည်။\nYour e-mail address is not revealed when other users contact you.\nအခြားသူများ သင့်ထံဆက်သွယ်သည့်အခါ သင့်အီးမေးကို သူတို့ကို ဖော်ပြမည်မဟုတ်ပါ။",
+       "prefs-help-email-others": "သင့်အသုံးပြုသူစာမျက်နှာ သို့မဟုတ် ဆွေးနွေးရန်စာမျက်နှာရှိ လင့်မှတဆင့် သင့်ထံ အခြားသူများ အီးမေးမှဆက်သွယ်ရန်လည်း ရွေးချယ်နိုင်သည်။\nအခြားသူများ သင့်ထံဆက်သွယ်သည့်အခါ သင့်အီးမေးကို သူတို့အား ဖော်ပြမည်မဟုတ်ပါ။",
        "prefs-help-email-required": "အီးမေးလိပ်စာ လိုအပ်ပါသည်။",
        "prefs-info": "အခြေခံသတင်းအချက်အလက်",
        "prefs-i18n": "နိုင်ငံတကာအဆင့်မီပြုလုပ်ခြင်း",
        "prefs-signature": "လက်မှတ်",
        "prefs-dateformat": "နေ့စွဲပုံစံ",
        "prefs-timeoffset": "အချိန် တန်ဖိုး",
-       "prefs-advancedediting": "အဆင့်မြင့် ရွေးချယ်မှု",
+       "prefs-advancedediting": "အထွေထွေ ရွေးချယ်စရာများ",
+       "prefs-editor": "တည်းဖြတ်သူ",
+       "prefs-preview": "နမူနာ",
        "prefs-advancedrc": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-advancedrendering": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-advancedsearchoptions": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-advancedwatchlist": "အဆင့်မြင့် ရွေးချယ်မှု",
        "prefs-displayrc": "ပြသရန် ရွေးချယ်မှု",
        "prefs-displaywatchlist": "ပြသရန် ရွေးချယ်မှု",
+       "prefs-tokenwatchlist": "တိုကင်",
        "prefs-diffs": "ကွဲပြားချက်",
-       "email-address-validity-valid": "အီးမေးလိပ်စာ တရားဝင်ပုံပေါ်သည်",
-       "email-address-validity-invalid": "တရားဝင်အီးမေးလိပ်စာတစ်ခု ထည့်ပါ",
        "userrights": "အသုံးပြုသူ၏ အခွင့်အရေးများကို စီမံခန့်ခွဲခြင်း",
        "userrights-lookup-user": "အသုံးပြုသူအုပ်စုကို စီမံရန်",
        "userrights-user-editname": "အသုံးပြုသူအမည်တစ်ခုကို ထည့်ပါ -",
-       "editusergroup": "အသုံးပြုသူအုပ်စုကို တည်းဖတြ်ရန်",
-       "userrights-editusergroup": "အသုံးပြုသူအုပ်စုကို တည်းဖတြ်ရန်",
-       "saveusergroups": "အသုံးပြုသူအုပ်စုကို သိမ်းရန်",
+       "editusergroup": "{{GENDER:$1|အသုံးပြုသူ}}အုပ်စုများကို တည်းဖြတ်ရန်",
+       "editinguser": "{{GENDER:$1|အသုံးပြုသူ}} <strong>[[User:$1|$1]]</strong> $2 ၏ အသုံးပြုအခွင့်အရေးများကို ပြောင်းလဲခြင်း",
+       "userrights-editusergroup": "အသုံးပြုသူအုပ်စုကို တည်းဖြတ်ရန်",
+       "saveusergroups": "{{GENDER:$1|အသုံးပြုသူ}}အုပ်စုများကို သိမ်းရန်",
        "userrights-groupsmember": "အဖွဲ့ဝင်",
        "userrights-reason": "အ​ကြောင်း​ပြ​ချက်:",
-       "userrights-notallowed": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\9eá\80\8aá\80º á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸á\80¡á\80\95á\80ºá\80\94á\80¾á\80\84á\80ºá\80¸á\80\9bá\80\94á\80º á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\81á\80»á\80\80á\80º မရှိပါ။",
+       "userrights-notallowed": "á\80\9eá\80\84á\80·á\80ºá\80\90á\80½á\80\84á\80º á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸ á\80\95á\80±á\80«á\80\84á\80ºá\80¸á\80\91á\80\8aá\80·á\80ºá\80\9bá\80\94á\80º  á\80\9eá\80­á\80¯á\80· á\80\96á\80\9aá\80ºá\80\9bá\80¾á\80¬á\80¸á\80\9bá\80\94á\80º á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸ မရှိပါ။",
        "userrights-changeable-col": "သင်ပြောင်းလဲပေးနိုင်သောအုပ်စုများ",
        "userrights-unchangeable-col": "သင်ပြောင်းလဲမပေးနိုင်သောအုပ်စုများ",
        "group": "အုပ်စု -",
        "group-user": "အသုံးပြုသူများ",
-       "group-autoconfirmed": "အလိုအလျောက်အတည်ပြုထားသောအသုံးပြုသူ",
+       "group-autoconfirmed": "အလိုအလျောက် အတည်ပြုထားသော အသုံးပြုသူများ",
        "group-bot": "ဘော့များ",
        "group-sysop": "အက်ဒမင်များ",
        "group-bureaucrat": "ဗျူရိုကရက်",
        "group-all": "(အားလုံး)",
-       "group-user-member": "အသုံးပြုသူ",
-       "group-autoconfirmed-member": "အလိုအလျောက်အတည်ပြုထားသောအသုံးပြုသူ",
-       "group-bot-member": "ဘော့",
-       "group-sysop-member": "အက်ဒမင်",
-       "group-bureaucrat-member": "ဗျူရိုကရက်",
+       "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|ဗျူရိုကရက်}}",
        "grouppage-user": "{{ns:project}}:အသုံးပြုသူများ",
        "grouppage-autoconfirmed": "{{ns:project}}:အလိုအလျောက်အတည်ပြုထားသောအသုံးပြုသူများ",
        "grouppage-bot": "{{ns:project}}:ဘော့များ",
        "right-reupload": "ရှိပြီးသားဖိုင်များကို ထပ်ရေးရန်",
        "right-reupload-own": "သင်ကိုယ်တိုင် Upload တင်ထားခဲ့သည့် ရှိပြီးသားဖိုင်ကို ထပ်ရေးရန်",
        "right-upload_by_url": "URL လင့်တစ်ခုမှ ဖိုင်ကို Upload တင်ရန်",
-       "right-autoconfirmed": "á\80\90á\80\85á\80ºá\80\85á\80­á\80\90á\80ºá\80\90á\80\85á\80ºá\80\95á\80­á\80¯á\80\84á\80ºá\80¸á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\9bá\80\94á\80º",
+       "right-autoconfirmed": "á\80¡á\80­á\80¯á\80\84á\80ºá\80\95á\80®á\80¡á\80\81á\80¼á\80±á\80\95á\80¼á\80¯ á\80\80á\80\94á\80·á\80ºá\80\9eá\80\90á\80ºá\80\81á\80»á\80\80á\80ºá\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80¡á\80\80á\80»á\80­á\80¯á\80¸á\80\99á\80\9eá\80\80á\80ºá\80\9bá\80±á\80¬á\80\80á\80ºá\80\95á\80«",
        "right-bot": "အလိုအလျောက်ပြုမူသော ဖြစ်စဉ်အဖြစ်ဆောင်ရွက်ရန်",
+       "right-writeapi": "ရေးသားမှု API ကို သုံးရန်",
        "right-delete": "စာမျက်နှာများကိုဖျက်ပါ။",
        "right-bigdelete": "လွန်စွာများပြားသော ရာဇဝင်များရှိသည့် စာမျက်နှာများကို ဖျက်ရန်",
        "right-browsearchive": "ဖျက်ပစ်လိုက်သော စာမျက်နှာများကို ရှာရန်",
        "right-block": "အခြားအသုံးပြုသူများ တည်းဖြတ်ခြင်းမှ ပိတ်ပင်ရန်",
        "right-blockemail": "အီးမေးပို့ခြင်းမှ အသုံးပြုသူကို တားဆီးရန်",
        "right-hideuser": "အသုံးပြုသူအမည်ကို ပိတ်ပင်ရန်နှင့် ယင်းအမည်ကို အများမမြင်နိုင်အောင် ဝှက်ထားရန်",
-       "right-unblockself": "á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80ºá\80\80á\80­á\80¯ á\80\9eá\80°á\80\90á\80­á\80¯á\80·á\80\98á\80¬á\80\9eá\80¬ á\80\95á\80¼á\80\94á\80ºá\80\96á\80½á\80\84á\80·်ရန်",
-       "right-protect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\99á\80¾á\80¯á\80¡á\80\86á\80\84á\80·á\80º á\80\9cá\80»á\80¾á\80±á\80¬á\80·á\80\81á\80»á\80\9bá\80\94á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80·á\80ºစာမျက်နှာများကို တည်းဖြတ်ရန်",
+       "right-unblockself": "á\80\80á\80­á\80¯á\80\9aá\80·á\80ºá\80\80á\80­á\80¯á\80\80á\80­á\80¯á\80\9aá\80º á\80\95á\80¼á\80\94á\80ºá\80\99á\80\95á\80­á\80\90á\80ºá\80\95á\80\84်ရန်",
+       "right-protect": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\99á\80¾á\80¯á\80¡á\80\86á\80\84á\80·á\80º á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²á\80\9bá\80\94á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\9eá\80½á\80\9aá\80ºá\80\96á\80¼á\80¬-á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80\8aá\80·á\80º စာမျက်နှာများကို တည်းဖြတ်ရန်",
        "right-editusercss": "အခြားအသုံးပြုသူများ၏ CSS ဖိုင်ကို တည်းဖြတ်ရန်",
        "right-edituserjs": "အခြားအသုံးပြုသူများ၏ JavaScript ဖိုင်ကို တည်းဖြတ်ရန်",
        "right-import": "အခြားဝီကီများမှ စာမျက်နှာများကို ထည့်သွင်းရန်",
        "action-suppressionlog": "ဤကိုယ်ပိုင်မှတ်တမ်းကို ကြည့်ရန်",
        "action-block": "တည်းဖြတ်ခြင်းမှ ဤအသုံးပြုသူကို ပိတ်ပင်ရန်",
        "action-protect": "ဤစာမျက်နှာအတွက် ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲရန်",
-       "action-import": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80¡á\80\81á\80¼á\80¬á\80¸á\80\9dá\80®á\80\80á\80®á\80\99á\80¾ ထည့်သွင်းရန်",
-       "action-importupload": "Upload á\80\90á\80\84á\80ºá\80\9cá\80­á\80¯á\80\80á\80ºá\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯á\80\99á\80¾ á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ကို ထည့်သွင်းရန်",
+       "action-import": "á\80¡á\80\81á\80¼á\80¬á\80¸á\80\9dá\80®á\80\80á\80®á\80\99á\80»á\80¬á\80¸á\80\99á\80¾ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ ထည့်သွင်းရန်",
+       "action-importupload": "Upload á\80\90á\80\84á\80ºá\80\9cá\80­á\80¯á\80\80á\80ºá\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯á\80\99á\80¾ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸ကို ထည့်သွင်းရန်",
        "action-autopatrol": "သင့်တည်းဖြတ်မှုကို စောင့်ကြပ်စစ်ဆေးနေသည်ဟု မှတ်သားထားရန်",
        "action-unwatchedpages": "စောင့်မကြည့်တော့သော စာမျက်နှာများ၏ စာရင်းကို ကြည့်ရန်",
        "action-mergehistory": "ဤစာမျက်နှာ၏ရာဇဝင်ကို ပေါင်းရန်",
        "action-userrights": "အသုံးပြုသူ၏အခွင့်အရေးများအားလုံးကို တည်းဖြတ်ရန်",
        "action-userrights-interwiki": "အခြားဝီကီများမှ အသုံးပြုသူများ၏ အသုံးပြုသူအခွင့်အရေးများကို တည်းဖြတ်ရန်",
+       "action-sendemail": "အီးမေးများ ပို့ရန်",
+       "action-editmywatchlist": "သင့် စောင့်ကြည့်စာရင်းကို တည်းဖြတ်ရန်",
+       "action-viewmywatchlist": "သင့် စောင့်ကြည့်စာရင်းကို ကြည့်ရန်",
        "nchanges": "ပြောင်းလဲချက် $1 {{PLURAL:$1|ခု|ခု}}",
-       "recentchanges": "လတ်​တ​လောအ​ပြောင်း​အ​လဲ​",
-       "recentchanges-legend": "လတ်တလောအပြောင်းအလဲများအတွက် ရွေးချယ်စရာများ",
+       "enhancedrc-history": "ရာဇဝင်",
+       "recentchanges": "လတ်တလော အပြောင်းအလဲများ",
+       "recentchanges-legend": "လတ်တလော အပြောင်းအလဲများအတွက် ရွေးချယ်စရာများ",
        "recentchanges-summary": "ဤစာမျက်နှာတွင် ဝီကီ၏ လတ်တလောပြောင်းလဲမှုများကို နောက်ကြောင်းခံလိုက်ရန်",
        "recentchanges-feed-description": "ဤ feed ထဲတွင် ဝီကီ၏ လတ်တလောပြောင်းလဲမှုများကို နောက်ကြောင်းခံလိုက်ရန်",
        "recentchanges-label-newpage": "ဤတည်းဖြတ်မှုသည် စာမျက်နှာအသစ်ကို ဖန်တီးခဲ့သည်။",
        "recentchanges-label-minor": "အရေးမကြီးသော ​ပြင်​ဆင်​မှု ​ဖြစ်​သည်​",
        "recentchanges-label-bot": "ဤတည်းဖြတ်မှုကို ဘော့က လုပ်ဆောင်သွားသည်။",
        "recentchanges-label-unpatrolled": "ဤတည်းဖြတ်မှုကို မစောင့်ကြပ်မစစ်ဆေးရသေးပါ",
-       "rcnotefrom": "အောက်ပါတို့သည် '''$2''' ကတည်းက အ​ပြောင်းအလဲများ ြဖစ်သည် ('''$1''' ခု ြပထားသည်)။",
+       "recentchanges-label-plusminus": "စာမျက်နှာ အရွယ်အစားမှာ အောက်ပါ ဘိုက်ပမာဏ ပြောင်းလဲသွားခဲ့သည်",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|စာမျက်နှာသစ်များ စာရင်း]]ကိုလည်း ကြည့်ရန်)",
+       "recentchanges-submit": "ပြသရန်",
+       "rcnotefrom": "အောက်ပါတို့မှာ <strong>$3၊ $4</strong> မှစ၍ {{PLURAL:$5|ပြောင်းလဲမှု|ပြောင်းလဲမှုများ}} ဖြစ်သည်  (<strong>$1</strong> အထိ ပြထား)။",
        "rclistfrom": "$3 $2 မှစသော အပြောင်းအလဲအသစ်များကို ပြရန်",
        "rcshowhideminor": "အရေးမကြီးသော ပြင်ဆင်မှု $1ရန်",
-       "rcshowhidebots": "ဘော့ $1ရန်",
-       "rcshowhideliu": "logged-in ဝင်နေသော အသုံးပြုသူ $1ရန်",
+       "rcshowhideminor-show": "ပြသရန်",
+       "rcshowhideminor-hide": "ဝှက်",
+       "rcshowhidebots": "ဘော့များ $1ရန်",
+       "rcshowhidebots-show": "ပြ",
+       "rcshowhidebots-hide": "ဝှက်ရန်",
+       "rcshowhideliu": "မှတ်ပုံတင်ထားသော အသုံးပြုသူများ $1",
+       "rcshowhideliu-show": "ပြသရန်",
+       "rcshowhideliu-hide": "ဝှက်",
        "rcshowhideanons": "အမည်မသိ အသုံးပြုသူ $1ရန်",
+       "rcshowhideanons-show": "ပြသရန်",
+       "rcshowhideanons-hide": "ဝှက်",
        "rcshowhidepatr": "စောင့်ြကပ်တည်းဖြတ်မှု $1ရန်",
-       "rcshowhidemine": "ကျွနု်ပ်တည်းဖြတ်ထားသည်များ $1ရန်",
+       "rcshowhidepatr-show": "ပြသရန်",
+       "rcshowhidepatr-hide": "ဝှက်ရန်",
+       "rcshowhidemine": "ကျွန်ုပ်တည်းဖြတ်ထားသည်များ $1",
+       "rcshowhidemine-show": "ပြသရန်",
+       "rcshowhidemine-hide": "ဝှက်",
+       "rcshowhidecategorization-show": "ပြသရန်",
+       "rcshowhidecategorization-hide": "ဝှက်ရန်",
        "rclinks": "$2 ရက်အတွင်းမှ နောက်ဆုံးပြင်ဆင်ချက် $1 ခုကို ပြရန်</br> $3",
        "diff": "ကွဲပြားမှု",
        "hist": "မှတ်တမ်း",
        "newpageletter": "အသစ်",
        "boteditletter": "ဘော့",
        "number_of_watching_users_pageview": "[စောင့်ကြည့်နေသော အသုံးပြုသူ $1 {{PLURAL:$1|ဦး|ဦး}}]",
-       "rc_categories_any": "á\80\99á\80\8aá\80ºá\80\9eá\80\8aá\80ºမဆို",
+       "rc_categories_any": "á\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\81á\80¶á\80\9bá\80\9eá\80±á\80¬ á\80\99á\80\8aá\80ºá\80\9eá\80°မဆို",
        "rc-change-size-new": "$1 {{PLURAL:$1|byte|bytes}} ပြောင်းလဲပြီးနောက်",
        "newsectionsummary": "/* $1 */ အပိုင်းသစ်",
-       "rc-enhanced-expand": "အသေးစိတ် ပြရန် (JavaScript လိုအပ်သည်)",
+       "rc-enhanced-expand": "အသေးစိတ် ပြရန်",
        "rc-enhanced-hide": "အသေးစိတ် မပြရန်",
+       "rc-old-title": "\"$1\" အဖြစ် မူလက ဖန်တီးခဲ့သည်",
        "recentchangeslinked": "ဆက်​စပ်သော​ အ​ပြောင်း​အ​လဲ​များ​",
        "recentchangeslinked-feed": "ဆက်စပ်သော ​အ​ပြောင်း​အ​လဲ​များ​",
-       "recentchangeslinked-toolbox": "ဆက်​စပ်​သော​အ​ပြောင်း​အ​လဲ​များ​",
+       "recentchangeslinked-toolbox": "ဆက်စပ်သော အပြောင်းအလဲများ",
        "recentchangeslinked-title": "\"$1\" နှင့် ဆက်စပ်သော အပြောင်းအလဲများ",
        "recentchangeslinked-summary": "ဤသည်မှာ သီးသန့်ပြထားသော စာမျက်နှာ (သို့ သီးသန့်ကဏ္ဍများ) မှ ညွှန်းထားသော စာမျက်နှာများ၏ လတ်တလော ပြောင်းလဲမှုများ၏ စာရင်းဖြစ်သည်။ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ စာမျက်နှာများကို စာလုံးမည်းဖြင့် ပြထားသည်။",
        "recentchangeslinked-page": "စာမျက်နှာ အမည် -",
        "recentchangeslinked-to": "ပေးထားသော စာမျက်နှာများအစား လင့်များနှင့် ဆက်စပ်နေသာ စာမျက်နှာများ၏ အပြောင်းအလဲများကို ပြရန်",
+       "recentchanges-page-added-to-category": "ကဏ္ဍထဲသို့ [[:$1]] ကို ပေါင်းထည့်ခဲ့သည်",
+       "recentchanges-page-added-to-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာ $2 ခု}}ကို ကဏ္ဍထဲသို့ ပေါင်းထည့်ခဲ့သည်",
+       "recentchanges-page-removed-from-category": "ကဏ္ဍထဲမှ [[:$1]] ကို ဖယ်ရှားခဲ့သည်",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာ $2 ခု}}ကို ကဏ္ဍထဲမှ ဖယ်ရှားခဲ့သည်",
        "upload": "ဖိုင်​တင်​ရန်​",
        "uploadbtn": "ဖိုင်​တင်​ရန်​",
        "reuploaddesc": "Upload တင်နေခြင်းကို ဖျက်သိမ်းပြီး upload တင်သည့် ပုံစံသို့ ပြန်သွားရန်",
        "upload-tryagain": "ပြုပြင်ထားသောဖိုင်၏ ဖော်ပြချက်ကို ထည့်သွင်းရန်",
        "uploadnologin": "logged in ဝင်မထားပါ",
-       "uploadnologintext": "ဖိုင်များကို Upload တင်ရန် [[Special:UserLogin|logged in ဝင်ပြီး]] ဖြစ်ရမည်။",
+       "uploadnologintext": "ဖိုင်များကို တင်ရန် ကျေးဇူးပြု၍ $1 ပါ။",
        "uploaderror": "အပ်လုပ်တင်ခြင်း အမှား",
-       "upload-permitted": "ခွင့်ပြုထားသော ဖိုင်အမျိုးအစား - $1။",
-       "upload-preferred": "á\80¡á\80\9cá\80±á\80¸á\80\95á\80±á\80¸á\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80ºá\80¡á\80\99á\80»á\80­á\80¯á\80¸á\80¡á\80\85á\80¬á\80¸ - $1။",
-       "upload-prohibited": "တားမြစ်ထားသော ဖိုင်အမျိုးအစား - $1။",
+       "upload-permitted": "ခွင့်ပြုထားသော ဖိုင် {{PLURAL:$2|အမျိုးအစား|အမျိုးအစားများ}}: $1။",
+       "upload-preferred": "á\80¡á\80\9cá\80±á\80¸á\80\95á\80±á\80¸á\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\96á\80­á\80¯á\80\84á\80º {{PLURAL:$2|á\80¡á\80\99á\80»á\80­á\80¯á\80¸á\80¡á\80\85á\80¬á\80¸|á\80¡á\80\99á\80»á\80­á\80¯á\80¸á\80¡á\80\85á\80¬á\80¸á\80\99á\80»á\80¬á\80¸}}: $1။",
+       "upload-prohibited": "တားမြစ်ထားသော ဖိုင် {{PLURAL:$2|အမျိုးအစား|အမျိုးအစားများ}}: $1။",
        "uploadlogpage": "Upload တင်သည့် မှတ်တမ်း",
        "filename": "ဖိုင်အမည်",
        "filedesc": "အ​ကျဉ်း​ချုပ်​",
        "license-header": "လိုင်စင်သတ်မှတ်ခြင်း",
        "nolicense": "ဘာမှရွေးချယ်မထားပါ",
        "license-nopreview": "(နမူနာ မရနိုင်ပါ)",
-       "upload_source_url": "(တရားဝင်၍ အများပြည်သူ ရယူသုံးစွဲနိုင်သော URL လင့်တစ်ခု)",
-       "upload_source_file": "(သင့်ကွန်ပျူတာမှ ဖိုင်တစ်ခု)",
+       "upload_source_url": "တရားဝင်၍ အများပြည်သူ သုံးစွဲခွင့်ရှိသော URL လင့်တစ်ခုမှ သင်ရွေးချယ်ထားသည့် File",
+       "upload_source_file": "(á\80\9eá\80\84á\80ºá\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80\9eá\80\84á\80·á\80ºá\80\80á\80½á\80\94á\80ºá\80\95á\80»á\80°á\80\90á\80¬á\80\99á\80¾ á\80\96á\80­á\80¯á\80\84á\80ºá\80\90á\80\85á\80ºá\80\81á\80¯)",
        "listfiles_search_for": "မီဒီယာအမည်ကို ရှာရန် -",
        "imgfile": "ဖိုင်",
        "listfiles": "ဖိုင်စာရင်း",
        "filehist-dimensions": "မှတ်တမ်း ဒိုင်မန်းရှင်းများ",
        "filehist-filesize": "ဖိုင်ဆိုက်",
        "filehist-comment": "မှတ်ချက်",
-       "imagelinks": "á\80\96á\80­á\80¯á\80\84á\80ºá\80¡á\80\86á\80\80á\80ºá\80¡á\80\9eá\80½á\80\9aá\80ºá\80\99á\80»á\80¬á\80¸",
+       "imagelinks": "á\80\96á\80­á\80¯á\80\84á\80ºá\80\9eá\80¯á\80¶á\80¸á\80\85á\80½á\80²á\80\99á\80¾á\80¯",
        "linkstoimage": "ဤဖိုင်သို့ အောက်ပါ {{PLURAL:$1|စာမျက်နှာလင့်|စာမျက်နှာလင့် $1 ခု}} -",
        "nolinkstoimage": "ဤဖိုင်သို့လင့်ထားသော စာမျက်နှာမရှိပါ။",
        "morelinkstoimage": "ဤဖိုင်သို့[[Special:WhatLinksHere/$1|နောက်ထပ်လင့်များ]] ကိုကြည့်ပါ။",
        "filepage-nofile-link": "ဤအမည်ဖြင့် မည်သည့်ဖိုင်မှ မရှိပါ။ သိုရာတွင် ယင်းကို [$1 upload တင်]နိုင်သည်။",
        "uploadnewversion-linktext": "ဤဖိုင်၏ နောက်ဆုံး version ကို upload တင်ရန်",
        "shared-repo-from": "$1 ထံမှ",
+       "upload-disallowed-here": "သင်သည် ဤဖိုင်အား ထပ်၍ ရေးသားမရနိုင်ပါ။",
        "filerevert": "$1 ကို ပြန်ပြောင်းရန်",
        "filerevert-legend": "ဖိုင်ကို ပြန်ပြောင်းရန်",
        "filerevert-comment": "အ​ကြောင်း​ပြ​ချက် -",
-       "filerevert-defaultcomment": "$2 ရက်နေ့ $1 အချိန်မှ မူသို့ ပြန်ပြောင်းရန်",
+       "filerevert-defaultcomment": "$2 ရက်နေ့ $1 ($3) အချိန်မှ မူသို့ ပြန်ပြောင်းရန်",
        "filerevert-submit": "ပြောင်းရန်",
        "filedelete": "$1 ကိုဖျက်ပါ",
        "filedelete-legend": "ဖိုင်ကိုဖျက်ပါ",
        "statistics-users": "မှတ်ပုံတင်ထားသော [[Special:ListUsers|အသုံးပြုသူများ]]",
        "statistics-users-active": "လက်ရှိလုပ်ကိုင်နေသော အသုံးပြုသူများ",
        "doubleredirects": "နှစ်ဆင့်ပြန် ပြန်ညွှန်းများ",
-       "double-redirect-fixed-move": "[[$1]] á\80\80á\80­á\80¯ á\80\9bá\80½á\80¾á\80±á\80·á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\95á\80¼á\80®á\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\9aá\80\81á\80¯á\80¡á\80\81á\80« [[$2]] သို့ ပြန်ညွှန်းထားသည်။",
+       "double-redirect-fixed-move": "[[$1]] á\80\80á\80­á\80¯ á\80\9bá\80½á\80¾á\80±á\80·á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\95á\80¼á\80®á\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\81\8eá\80\84á\80ºá\80¸á\80¡á\80¬á\80¸ á\80¡á\80\9cá\80­á\80¯á\80¡á\80\9cá\80»á\80±á\80¬á\80\80á\80º á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80ºá\80\95á\80¼á\80®á\80¸ [[$2]] သို့ ပြန်ညွှန်းထားသည်။",
        "brokenredirects": "ကျိုးပျက်နေသော ပြန်ညွှန်းများ",
        "brokenredirectstext": "အောက်ပါ ပြန်ညွှန်းများသည် မရှိသောစာမျက်နှာများသို့ လင့်ထားသည် -",
        "brokenredirects-edit": "ပြင်​ဆင်​ရန်",
        "withoutinterwiki-submit": "ပြ",
        "fewestrevisions": "တည်းဖြတ်မူအနည်းဆုံး စာမျက်နှာများ",
        "nbytes": "$1 {{PLURAL:$1|ဘိုက်|ဘိုက်}}",
+       "nlinks": "{{PLURAL:$1|လင့်|လင့်ခ်များ}} $1",
        "nmembers": "အဖွဲ့ဝင် $1 {{PLURAL:$1|ခု|ခု}}",
        "specialpage-empty": "ဤသတင်းပို့ချက်အတွက် ရလဒ်မရှိပါ။",
        "uncategorizedpages": "အမျိုးအစား ခွဲမထားသော စာမျက်နှာများ",
        "wantedtemplates": "အလိုရှိသော တမ်းပလိတ်များ",
        "mostcategories": "ကဏ္ဍအများဆုံးပါသော စာမျက်နှာများ",
        "prefixindex": "ရှေ့ဆုံးမှ prefix ပါသော စာမျက်နှာ အားလုံး",
+       "prefixindex-submit": "ပြသရန်",
        "shortpages": "စာမျက်နှာတို",
        "longpages": "ရှည်လျားသောစာမျက်နှာများ",
        "deadendpages": "လမ်းပိတ်နေသော (လင့်မရှိသော) စာမျက်နှာများ",
        "protectedpages": "ကာကွယ်ထားသော စာမျက်နှာများ",
+       "protectedpages-noredirect": "ပြန်ညွှန်းများအား ဝှက်ရန်",
        "protectedtitles": "ကာကွယ်ထားသော ခေါင်းစဉ်များ",
        "listusers": "အသုံးပြုသူစာရင်း",
        "listusers-editsonly": "တည်းဖြတ်ထားဖူးသော အသုံးပြုသူများကိုသာ ဖော်ပြရန်",
        "listusers-creationsort": "စတင်ရေးသားသည့်ရက်စွဲအလိုက် စီရန်",
        "usereditcount": "တည်းဖြတ်မှု $1 {{PLURAL:$1|ခု|ခု}}",
-       "usercreated": "$1 ရက် $2 အချိန်တွင် ဖန်တီးခဲ့သည်",
+       "usercreated": "$1 $2 အချိန်တွင် {{GENDER:$3|ဖန်တီးခဲ့သည်}}",
        "newpages": "စာမျက်နှာအသစ်",
+       "newpages-submit": "ပြသရန်",
        "newpages-username": "မှတ်​ပုံ​တင်​အ​မည်:",
        "ancientpages": "အဟောင်းဆုံးစာမျက်နှာ",
        "move": "ရွှေ့ရန်",
        "pager-older-n": "{{PLURAL:$1|ပိုဟောင်းသော တစ်ခု|ပိုဟောင်းသော $1 ခု}}",
        "booksources": "မှီငြမ်း စာအုပ်များ",
        "booksources-search-legend": "စာအုပ်ရင်းမြစ်များကို ရှာရန်",
-       "specialloguserlabel": "အသုံးပြုသူ -",
-       "speciallogtitlelabel": "ခေါင်းစဉ် -",
+       "booksources-search": "ရှာဖွေရန်",
+       "specialloguserlabel": "ဆောင်ရွက်သူ -",
+       "speciallogtitlelabel": "ရည်ရွယ်ရာ (ခေါင်းစဉ် သို့ {{ns:user}}:အသုံးပြုသူအတွက် အသုံးပြုအမည်):",
        "log": "မှတ်​တမ်း​များ​",
+       "logeventslist-submit": "ပြသရန်",
        "all-logs-page": "အများနှင့်ဆိုင်သောမှတ်တမ်းအားလုံး",
        "allpages": "စာမျက်နှာအားလုံး",
        "nextpage": "နောက်ထပ်စာမျက်နှာ ($1)",
        "allarticles": "စာမျက်နှာအားလုံး",
        "allinnamespace": "စာမျက်နှာအားလုံး (အမည်ညွှန်း $1)",
        "allpagessubmit": "သွား​ပါ​",
+       "allpages-hide-redirects": "ပြန်ညွှန်းများအား ဝှက်ရန်",
        "categories": "အမျိုးအစားများ",
+       "categories-submit": "ပြသရန်",
        "categoriesfrom": "ဤမှစသော အမျိုးအစားများကို ပြရန် -",
        "special-categories-sort-count": "အနည်းအများအလိုက်စီရန်",
        "special-categories-sort-abc": "အက္ခရာစဉ်အလိုက်စီရန်",
        "deletedcontributions": "ဖျက်လိုက်သော ပံ့ပိုးမှုများ",
        "deletedcontributions-title": "ဖျက်လိုက်သော ပံ့ပိုးမှုများ",
        "sp-deletedcontributions-contribs": "ပံ့ပိုးထားမှုများ",
-       "linksearch": "ပြင်ပ လင့်များ",
+       "linksearch": "ပြင်ပလင့်ခ်များ ရှာဖွေ",
        "linksearch-pat": "ရှာသည့်ပုံစံ -",
        "linksearch-ns": "အမည်ညွှန်း -",
        "linksearch-ok": "ရှာ​ဖွေ​ရန်​",
        "listgrouprights-addgroup-self-all": "အုပ်စုအားလုံးကို မိမိ၏အကောင့်သို့ ပေါင်းထည့်ရန်",
        "listgrouprights-removegroup-self-all": "မိမိ၏အကောင့်မှ အုပ်စုအားလုံးကို ဖယ်ရှားရန်",
        "mailnologin": "ပို့ရန်လိပ်စာ မရှိပါ",
-       "emailuser": "ဤ​အ​သုံး​ပြု​သူ​အား​အီး​မေး​ပို့​ပါ​",
-       "emailpage": "အီးမေးအသုံးပြုသူ",
-       "defemailsubject": "{{SITENAME}} အီးမေး",
+       "emailuser": "ဤ​အ​သုံး​ပြု​သူ​အား​ အီး​မေး​ပို့​ပါ​",
+       "emailuser-title-target": "{{GENDER:$1|အသုံးပြုသူ}}ကို အီးမေးပို့ရန်",
+       "defemailsubject": "{{SITENAME}} á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80° \"$1\" á\80\91á\80¶á\80\99á\80¾ á\80¡á\80®á\80¸á\80\99á\80±á\80¸",
        "usermaildisabled": "အသုံးပြုသူအီးမေးကို ပိတ်ထားသည်",
        "noemailtitle": "အီးမေးလိပ်စာ မရှိပါ",
        "noemailtext": "ဤအသုံးပြုသူသည် တရားဝင်သော အီးမေးလိပ်စာကို ဖောိပြထားခြင်း မရှိပါ။",
        "watchlistfor2": "$1 အတွက် $2",
        "nowatchlist": "သင့်စောင့်ကြည့်စာရင်းမှာ ဘာမှ မရှိပါ။",
        "watchnologin": "logged in ဝင်မထားပါ",
-       "addedwatchtext": "\"[[:$1]]\" á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ [[Special:Watchlist|á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸]]á\80\91á\80² á\80\95á\80±á\80«á\80\84á\80ºá\80¸á\80\91á\80\8aá\80·á\80ºá\80\95á\80¼á\80®á\80¸á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\94á\80±á\80¬á\80\80á\80ºá\80\95á\80­á\80¯á\80\84á\80ºá\80¸á\80¡á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80¡á\80\9cá\80²á\80\99á\80»á\80¬á\80¸á\80\94á\80¾á\80\84á\80·á\80º á\81\8eá\80\84á\80ºá\80¸á\80\94á\80¾á\80\84á\80·á\80º á\80\86á\80\80á\80ºá\80\94á\80½á\80\9aá\80ºá\80\94á\80±á\80\9eá\80±á\80¬ á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9aá\80\84á\80ºá\80¸á\80\94á\80±á\80\9bá\80¬á\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸á\80\95á\80¼á\80¯á\80\85á\80¯á\80\91á\80¬á\80¸á\80\99á\80\8aá\80º á\80\96á\80¼á\80\85á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\9bá\80½á\80±á\80¸á\80\81á\80»á\80\9aá\80ºá\80\9b á\80\9cá\80½á\80\9aá\80ºá\80\80á\80°á\80\85á\80±á\80\9bá\80\94á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\9eá\80\8aá\80º [[Special:RecentChanges|á\80\9cá\80\90á\80ºá\80\90á\80\9cá\80±á\80¬ á\80¡á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80¡á\80\9cá\80²á\80\99á\80»á\80¬á\80¸á\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸]]á\80\90á\80½á\80\84á\80º á\80\85á\80¬á\80\9cá\80¯á\80¶á\80¸á\80\99á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\84á\80·á\80º á\80\95á\80±á\80«á\80ºá\80\9cá\80¬á\80\99á\80\8aá\80ºဖြစ်သည်။",
-       "removedwatchtext": "\"[[:$1]]\" á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ ဖယ်ထုတ်ပြီး ဖြစ်သည်။",
+       "addedwatchtext": "\"[[:$1]]\" á\80\94á\80¾á\80\84á\80·á\80º á\81\8eá\80\84á\80ºá\80¸á\81\8f á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80º á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9eá\80\84á\80ºá\81\8f [[Special:Watchlist|á\80\85á\80±á\80¬á\80\84á\80·á\80ºá\80\80á\80¼á\80\8aá\80·á\80ºá\80\85á\80¬á\80\9bá\80\84á\80ºá\80¸]]á\80\91á\80²á\80\9eá\80­á\80¯á\80· á\80\95á\80±á\80«á\80\84á\80ºá\80¸á\80\91á\80\8aá\80·á\80ºá\80\95á\80¼á\80®á\80¸ဖြစ်သည်။",
+       "removedwatchtext": "\"[[:$1]]\" á\80\94á\80¾á\80\84á\80·á\80º á\81\8eá\80\84á\80ºá\80¸á\81\8f á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸á\80\81á\80»á\80\80á\80ºá\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\9eá\80\84á\80ºá\81\8f [[Special:Watchlist|စောင့်ကြည့်စာရင်း]] မှ ဖယ်ထုတ်ပြီး ဖြစ်သည်။",
        "watch": "စောင့်ကြည့်ရန်",
        "watchthispage": "ဤစာမျက်နှာကို စောင့်ကြည့်ရန်",
        "unwatch": "ဆက်လက် စောင့်မကြည့်တော့ရန်",
        "unwatchthispage": "စောင့်ကြည့်ခြင်းကို ရပ်တန့်ရန်",
        "notanarticle": "မာတိကာစာမျက်နှာတစ်ခု မဟုတ်",
-       "watchlist-details": "{{PLURAL:$1|စာမျက်နှာ $1 ခု|စာမျက်နှာ $1 ခု}} သည် သင့်စောင့်ကြည့်စာရင်းတွင် ရှိသည်။ ဆွေးနွေးချက်စာမျက်နှာများကို ထည့်တွက် မထားပါ။",
+       "watchlist-details": "{{PLURAL:$1|စာမျက်နှာ $1 ခု|စာမျက်နှာ $1 ခု}} သည် သင့်စောင့်ကြည့်စာရင်းတွင် ရှိပြီး ဆွေးနွေးချက်စာမျက်နှာများကို ထည့်တွက် မထားပါ။",
+       "wlheader-showupdated": "သင် နောက်ဆုံးကြည့်ရှုခဲ့ပြီးနောက် ပြောင်းလဲမှုရှိခဲ့သော စာမျက်နှာများကို <strong>စာလုံးမဲ</strong> ဖြင့် ပြသထားသည်",
        "wlshowlast": "နောက်ဆုံး $1 နာရီ $2 ရက်  ကိုပြရန်",
+       "watchlistall2": "အားလုံး",
+       "watchlist-hide": "ဝှက်",
+       "watchlist-submit": "ပြသရန်",
+       "wlshowhideminor": "အရေးမကြီးသော ပြင်ဆင်မှုများ",
+       "wlshowhideliu": "မှတ်ပုံတင်ထားသော အသုံးပြုသူများ",
+       "wlshowhideanons": "အမည်မသိ အသုံးပြုသူများ",
        "watchlist-options": "စောင့်ကြည့်စာရင်းအတွက် ရွေးချယ်စရာများ",
        "watching": "စောင့်ကြည့်လျက်ရှိ...",
        "unwatching": "စောင့်မကြည့်တော့...",
        "changed": "ပြောင်းလဲလိုက်သည်",
        "deletepage": "စာမျက်နှာကိုဖျက်ပါ",
        "confirm": "အတည်ပြု",
+       "excontentauthor": "ပါဝင်အကြောင်းအရာမှာ - \"$1\"၊ ဖြစ်ပြီး တစ်ဦးတည်းသော အကူအညီပေးအပ်သူမှာ \"[[Special:Contributions/$2|$2]]\" ([[User talk:$2|ဆွေးနွေး]])  ဖြစ်သည်",
        "delete-confirm": "\"$1\"ကို ဖျက်ပါ",
        "delete-legend": "ဖျက်",
+       "historyaction-submit": "ပြသရန်",
        "confirmdeletetext": "သင်သည် စာမျက်နှာတစ်ခုကို ယင်း၏ မှတ်တမ်းများနှင့်တကွ ဖျက်ပစ်တော့မည် ဖြစ်သည်။\nဤသို့ ဖျက်ပစ်ရန် သင် အမှန်တကယ် ရည်ရွယ်လျက်  နောက်ဆက်တွဲ အကျိုးဆက်များကို သိရှိနားလည်ပြီး [[{{MediaWiki:Policy-url}}|မူဝါဒ]] အတိုင်းလုပ်ဆောင်နေခြင်းဖြစ်ကြောင်းကို အတည်ပြုပေးပါ။",
        "actioncomplete": "လုပ်ဆောင်ချက် ပြီးပြီ",
        "actionfailed": "ဆောင်ရွက်မှုမအောင်မြင်ပါ",
        "deletereasonotherlist": "အခြား အကြောင်းပြချက်",
        "delete-edit-reasonlist": "ဖျက်ပစ်ရသော အကြောင်းရင်းများကို တည်းဖြတ်ရန်",
        "rollbacklink": "နောက်ပြန် ပြန်သွားရန်",
+       "rollbacklinkcount": "{{PLURAL:$1|တည်းဖြတ်မှု|တည်းဖြတ်မှုများ}} $1 ကို နောက်ပြန်ပြင်ရန်",
        "protectlogpage": "ကာကွယ်မှုများ၏ မှတ်တမ်း",
        "protectedarticle": "\"[[$1]]\" ကို ကာကွယ်ထားသည်",
        "modifiedarticleprotection": "\"[[$1]]\" ၏ ကာကွယ်မှု အဆင့်ကို ပြောင်းရန်",
+       "protect-title": "\"$1\" ၏ ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲရန်",
        "prot_1movedto2": "[[$1]]  မှ​ [[$2]] သို့​",
        "protectcomment": "အ​ကြောင်း​ပြ​ချက်:",
        "protectexpiry": "သက်တမ်းကုန်လွန်မည် -",
        "protect-locked-access": "သင့်အကောင့်သည် စာမျက်နှာ၏ ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲနိုင်ရန် ခွင့်ပြုချက် မရှိပါ။\nဤသည်မှာ '''$1''' စာမျက်နှာအတွက် လက်ရှိ settings သတ်မှတ်ချက်များ ဖြစ်သည်။",
        "protect-cascadeon": "ပြန်စီစဉ်ခြင်း cascading ကို ကာကွယ်ထားသော အောက်ပါ{{PLURAL:$1|စာမျက်နှာ|စာမျက်နှာများ}} ပါဝင်နေသောကြောင့် ဤစာမျက်နှာကို လက်ရှိတွင် ကာကွယ်ထားသည်။\nဤစာမျက်နှာ၏ ကာကွယ်မှုအဆင့်ကို ပြောင်းလဲသော်လည်း ပြန်စီစဉ်ခြင်းကို အကျိုးသက်ရောက်လိမ့်မည် မဟုတ်။",
        "protect-default": "အသုံးပြုသူ အားလုံးကို ခွင့်ပြုရန်",
-       "protect-fallback": "\"$1\" á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\81á\80»á\80\80á\80º á\80\9cá\80­á\80¯á\80¡á\80\95á\80ºသည်",
-       "protect-level-autoconfirmed": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80° á\80¡á\80\9eá\80\85á\80ºá\80\94á\80¾á\80\84á\80·á\80º á\80\99á\80¾á\80\90á\80ºá\80\95á\80¯á\80¶á\80\99á\80\90á\80\84á\80ºá\80\9bá\80\9eá\80±á\80¸á\80\9eá\80°á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\90á\80¬á\80¸á\80\86á\80®á\80¸á\80\91á\80¬á\80¸á\80\9bá\80\94်",
-       "protect-level-sysop": "á\80¡á\80\80á\80ºá\80\92á\80\99á\80\84á\80ºများသာ",
+       "protect-fallback": "\"$1\" á\80¡á\80\81á\80½á\80\84á\80·á\80ºá\80¡á\80\9bá\80±á\80¸á\80\9bá\80¾á\80­á\80\9eá\80±á\80¬ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯á\80\9eá\80¬ á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯သည်",
+       "protect-level-autoconfirmed": "á\80¡á\80\9cá\80­á\80¯á\80¡á\80\9cá\80»á\80±á\80¬á\80\80á\80º á\80¡á\80\90á\80\8aá\80ºá\80\95á\80¼á\80¯á\80\91á\80¬á\80¸á\80\9eá\80±á\80¬ á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯á\80\9eá\80¬ á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\9eá\80\8a်",
+       "protect-level-sysop": "á\80\85á\80®á\80\99á\80¶á\80\81á\80\94á\80·á\80ºá\80\81á\80½á\80²á\80\9eá\80°များသာ",
        "protect-summary-cascade": "အစီအစဉ်ကျအောင် စီနေသည်",
        "protect-expiring": "$1 (UTC) တွင် သက်တမ်းကုန်မည်",
-       "protect-cascade": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\91á\80²á\80\90á\80½á\80\84á\80ºá\80\95á\80«á\80\9eá\80±á\80¬ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9bá\80\94á\80º (á\80\85á\80®á\80\85á\80¥á\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\90á\80¬á\80¸á\80\86á\80®á\80¸á\80\81á\80¼á\80\84á\80ºá\80¸)",
+       "protect-cascade": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80¡á\80\90á\80½á\80\84á\80ºá\80¸ á\80\95á\80«á\80\9dá\80\84á\80ºá\80\9eá\80±á\80¬ á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\91á\80­á\80\94á\80ºá\80¸á\80\9eá\80­á\80\99á\80ºá\80¸á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\95á\80« (á\80¡á\80\99á\80»á\80¬á\80¸á\80\81á\80½á\80\84á\80·á\80ºá\80\95á\80¼á\80¯á\80\81á\80»á\80\80á\80ºá\80\96á\80¼á\80\84á\80·á\80ºá\80\9eá\80¬ á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80ºá\80\9eá\80\84á\80·á\80ºá\80\9eá\80\8aá\80º)",
        "protect-cantedit": "ကာကွယ်ထားသောစာမျက်နှာဖြစ်သည့်အတွက် ပြင်ဆင်၍ မရနိုင်ပါ။ အဘယ့်ကြောင့်ဆိုသော် သင့်မှာ တည်းဖြတ်ပိုင်ခွင့် မရှိ၍ ဖြစ်ပါသည်။",
        "protect-otherreason": "အခြားသော/နောက်ထပ် အကြောင်းပြချက် -",
        "protect-otherreason-op": "အခြား အကြောင်းပြချက်",
-       "protect-edit-reasonlist": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9bá\80\9eá\80±á\80¬ á\80¡á\80±á\80¬á\80·ာင်းရင်းများကို တည်းဖြတ်ရန်",
+       "protect-edit-reasonlist": "á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\91á\80¬á\80¸á\80\9bá\80\9eá\80±á\80¬ á\80¡á\80\80á\80¼á\80±ာင်းရင်းများကို တည်းဖြတ်ရန်",
        "protect-expiry-options": "၁ နာရီ:1 hour,၁ နေ့:1 day,၁ ပတ်:1 week,၂ ပတ်:2 weeks,၁ လ:1 month,၃ လ:3 months,၆ လ:6 months,၁ နှစ်:1 year,အနန္တ:infinite",
        "restriction-type": "ခွင့်ပြုချက် -",
        "restriction-level": "ကန့်သတ်ထားသော level:",
        "undeletebtn": "ပြန်လည် ထိန်းသိမ်းရန်",
        "undeletelink": "စောင့်ကြည့်ရန်/ပြန်လည်ထိန်းသိမ်းရန်",
        "undeleteviewlink": "ကြည့်ရန်",
-       "undeleteinvert": "selection ကို ပြောင်းပြန်လှန်ရန်",
+       "undeleteinvert": "ရွေးချယ်ထားခြင်းကို ပြောင်းပြန်လှန်ရန်",
        "undeletecomment": "အ​ကြောင်း​ပြ​ချက် -",
        "undeletedrevisions": "{{PLURAL:$1|မူတစ်ခု|မူ $1 ခု}} ကိုပြန်လည် ထိန်းသိမ်းပြီး",
        "undelete-search-box": "ဖျက်ပစ်သည့် စာမျက်နှာများမှ ရှာရန်",
        "undelete-search-submit": "ရှာ​ဖွေ​ရန်​",
        "undelete-show-file-submit": "မှန်",
        "namespace": "အမည်ညွှန်း -",
-       "invert": "selection ကို ပြောင်းပြန်လှန်ရန်",
+       "invert": "ရွေးချယ်ထားခြင်းကို ပြောင်းပြန်လှန်ရန်",
+       "namespace_association": "ဆက်စပ်နေသော အမည်ညွှန်း",
        "blanknamespace": "(ပင်မ)",
-       "contributions": "အသုံးပြုသူတို့၏ ပံ့ပိုးပေးမှုများ",
-       "contributions-title": "$1 အတွက် အသုံးပြုသူ၏ ပံ့ပိုးမှုများ",
-       "mycontris": "ပံ့ပိုးထားမှုများ",
-       "contribsub2": "$1အတွက် ($2)",
-       "uctop": "(ထိပ်)",
+       "contributions": "{{GENDER:$1|အသုံးပြုသူ}}၏ ဆောင်ရွက်ချက်များ",
+       "contributions-title": "$1 အတွက် အသုံးပြုသူ၏ ဆောင်ရွက်ချက်များ",
+       "mycontris": "ဆောင်ရွက်ပေးထားမှုများ",
+       "anoncontribs": "ဆောင်ရွက်ချက်များ",
+       "contribsub2": "{{GENDER:$3|$1}}အတွက် ($2)",
+       "uctop": "(လက်ရှိ)",
        "month": "အဆိုပါ လမှစ၍ ( အဆိုပါလထက်လည်း စောသော) :",
        "year": "အဆိုပါ နှစ်မှစ၍ ( အဆိုပါနှစ်ထက်လည်း စောသော) :",
        "sp-contributions-newbies": "အကောင့်အသစ်များ၏ ပံ့ပိုးမှုများကိုသာ ပြရန်",
        "whatlinkshere-hideredirs": "redirect ပြန်ညွှန်း $1 ခု",
        "whatlinkshere-hidetrans": "ထည့်သွင်းကူးယူချက် $1 ခု",
        "whatlinkshere-hidelinks": "လင့် $1 ခု",
-       "whatlinkshere-hideimages": "á\80\95á\80¯á\80¶á\80\9cá\80\84á\80·á\80º $1 ခု",
+       "whatlinkshere-hideimages": "á\80\96á\80­á\80¯á\80\84á\80ºá\80¡á\80\81á\80»á\80­á\80\90á\80ºá\80¡á\80\86á\80\80á\80ºá\80\99á\80»á\80¬á\80¸ $1 ခု",
        "whatlinkshere-filters": "စိစစ်မှုများ",
-       "blockip": "အသုံးပြုသူကို ပိတ်ပင်ရန်",
+       "blockip": "{{GENDER:$1|အသုံးပြုသူ}} ပိတ်ပင်ရန်",
        "blockip-legend": "အသုံးပြုသူကို ပိတ်ပင်ရန်",
        "ipaddressorusername": "အိုင်ပီလိပ်စာ သို့ အသုံးပြုသူအမည် -",
        "ipbexpiry": "သက်တမ်းကုန်လွန်ရက် -",
        "ipb-unblock-addr": "$1 ကို ပိတ်ထားရာမှ ပြန်ဖွင့်ရန်",
        "ipb-unblock": "အသုံးပြုသူအမည် သို့ IP address ကို ပိတ်ထားရာမှ ပြန်ဖွင့်ပေးရန်",
        "ipb-blocklist": "ရှိနှင့်ပြီးသား ပိတ်ပင်မှုများကို ကြည့်ရန်",
-       "ipb-blocklist-contribs": "$1 အတွက် ပံ့ပိုးမှုများ",
-       "unblockip": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\80á\80­á\80¯ á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9bá\80¬á\80\99á\80¾ á\80\95á\80¼á\80\94á\80ºá\80\96á\80½á\80\84á\80ºá\80¼á\80·ပေးရန်",
+       "ipb-blocklist-contribs": "{{GENDER:$1|$1}} အတွက် ဆောင်ရွက်ချက်များ",
+       "unblockip": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80\80á\80­á\80¯ á\80\95á\80­á\80\90á\80ºá\80\95á\80\84á\80ºá\80\91á\80¬á\80¸á\80\9bá\80¬á\80\99á\80¾ á\80\95á\80¼á\80\94á\80ºá\80\96á\80½á\80\84á\80·á\80ºပေးရန်",
        "ipusubmit": "ဤပိတ်ပင်မှုကို ဖယ်ရှားရန်",
        "unblocked": "[[User:$1|$1]] ကို ပိတ်ပင်ထားရာမှ ပြန်ဖွင့်ပေးလိုက်သည်",
        "unblocked-id": "$1 ကို ပိတ်ပင်ထားမှုကို ဖယ်ရှာလိုက်သည်",
+       "blocklist": "ပိတ်ပင်ထားသော အသုံးပြုသူများ",
        "ipblocklist": "ပိတ်ပင်ထားသော အသုံးပြုသူများ",
        "ipblocklist-legend": "ပိတ်ပင်ထားသော အသုံးပြုသူတစ်ဦးကို ရှာရန်",
        "ipblocklist-submit": "ရှာ​ဖွေ​ရန်​",
        "emailblock": "အီးမေးကို ပိတ်ပင်ထားသည်",
        "blocklist-nousertalk": "မိမိ၏ဆွေးနွေးချက်စာမျက်နှာကို တည်းဖြတ်မရနိုင်ပါ",
        "ipblocklist-empty": "ပိတ်ပင်ထားမှုစာရင်းသည် ဗလာဖြစ်နေသည်။",
-       "blocklink": "á\80\90á\80¬á\80¸á\80\86á\80®á\80¸á\80\80á\80\94á\80·á\80ºá\80\9eá\80\90á\80ºá\80\9bá\80\94်",
+       "blocklink": "á\80\95á\80­á\80\90á\80ºá\80\95á\80\84်",
        "unblocklink": "လင့် ပြန်ဖြေရန်",
        "change-blocklink": "စာကြောင်းအမည် ပြောင်းရန်",
        "contribslink": "ပံ့ပိုး",
        "ipb_already_blocked": "\"$1\" ကို အစကတည်းက ပိတ်ထားသည်",
        "move-page": "$1 ကို ရွှေ့ရန်",
        "move-page-legend": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန်",
-       "movepagetext": "အောက်ပါပုံစံကို အသုံးပြုပါက စာမျက်နှာကို အမည်ပြောင်းလဲပေးမည် ဖြစ်ပြီး အမည်သစ်သို့ ယင်း၏ မှတ်တမ်းနှင့်တကွ ရွှေ့ပေးမည် ဖြစ်သည်။\nအမည်ဟောင်းသည် အမည်သစ်သို့ ပြန်ညွှန်းပေးမည် ဖြစ်သည်။\nသင်သည် မူလခေါင်းစဉ်သို့ ပြန်ညွှန်းများကို အလိုအလျောက် အပ်ဒိတ် update လုပ်နိုင်သည်။\nအကယ်၍ မပြုလုပ်လိုပါက [[Special:DoubleRedirects|နှစ်ခါထပ်]][[Special:BrokenRedirects|ပြန်ညွှန်း အပျက်များ]] ကို မှတ်သားရန် မမေ့ပါနှင့်။\nလင့်များ ညွှန်းလိုသည့် နေရာသို့ ညွှန်ပြနေရန် သင့်တွင် တာဝန် ရှိသည်။\n\nအကယ်၍ ခေါင်းစဉ်အသစ်တွင် စာမျက်နှာတစ်ခု ရှိနှင့်ပြီး ဖြစ်ပါက (သို့) ယင်းစာမျက်နှာသည် အလွတ်မဖြစ်ပါက (သို့) ပြန်ညွှန်းတစ်ခု မရှိပါက (သို့) ယခင်က ပြုပြင်ထားသော မှတ်တမ်း မရှိပါက စာမျက်နှာသည် '''ရွေ့မည်မဟုတ်''' သည်ကို သတိပြုပါ။ \nဆိုလိုသည်မှာ သင်သည် အမှားတစ်ခု ပြုလုပ်မိပါက စာမျက်နှာကို ယခင်အမည်ကို ပြန်လည် ပြောင်းလဲပေးနိုင်သည်။ ရှိပြီသားစာမျက်နှာတစ်ခုကို စာမျက်နှာ အသစ်နှင့် ပြန်အုပ် overwrite ခြင်း မပြုနိုင်။\n\n'''သတိပေးချက်!'''\nဤသည်မှာ လူဖတ်များသော စာမျက်နှာတစ်ခုဖြစ်ပါက မမျှော်လင့်ထားသော၊ ကြီးမားသော အပြောင်းအလဲတစ်ခု ဖြစ်ပေါ်လာနိုင်သည်။\nထို့ကြောင့် ဆက်လက် မဆောင်ရွက်မီ သင်သည် နောက်ဆက်တွဲ အကျိုးဆက်များကို နားလည်ကြောင်း ကျေးဇူးပြု၍ သေချာပါစေ။",
-       "movepagetalktext": "ဆက်နွယ်နေသော ဆွေးနွေးချက် စာမျက်နှာကို '''အောက်ပါအကြောင်းများ မရှိခဲ့ပါက''' အလိုအလျောက် ရွှေ့ပစ်မည် ဖြစ်သည်။\n*အကယ်၍ ဗလာမဟုတ်သော ဆွေးနွေးချက်စာမျက်နှာသည် အမည်အသစ်အောက်တွင် ရှိနှင့်ပြီး ဖြစ်ခြင်း (သို့)\n*အောက်ပါ သေတ္တာငယ် box ကို မှတ်သားခြင်း။\n\nဤကိစ္စမျိုး ကြုံလာခဲ့ပါက သင် ဆန္ဒရှိလျှင် စာမျက်နှာကို မိမိကိုယ်တိုင် သွားရောက်ရွှေ့ပြောင်း ပေါင်းစပ်နိုင်သည်။",
-       "movearticle": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန် -",
-       "newtitle": "ခေါင်းစဉ်အသစ်သို့:",
+       "movepagetext": "အောက်ပါပုံစံကို အသုံးပြုခြင်းသည် စာမျက်နှာကို အမည်ပြောင်းလဲပေးမည် ဖြစ်ပြီး အမည်သစ်သို့ ယင်း၏ မှတ်တမ်းနှင့်တကွ ရွှေ့ပေးမည် ဖြစ်သည်။\nအမည်ဟောင်းသည် အမည်သစ်သို့ ပြန်ညွှန်းစာမျက်နှာ ဖြစ်လာမည်။\nသင်သည် မူလခေါင်းစဉ်သို့ ပြန်ညွှန်းများကို အလိုအလျောက် အပ်ဒိတ် update လုပ်နိုင်သည်။\nအကယ်၍ မပြုလုပ်လိုပါက [[Special:DoubleRedirects|နှစ်ခါထပ်]][[Special:BrokenRedirects|ပြန်ညွှန်း အပျက်များ]] ကို မှတ်သားရန် မမေ့ပါနှင့်။\nလင့်များ ညွှန်းလိုသည့် နေရာသို့ ညွှန်ပြနေရန် သင့်တွင် တာဝန် ရှိသည်။\n\nအကယ်၍ ခေါင်းစဉ်အသစ်တွင် စာမျက်နှာတစ်ခု ရှိနှင့်ပြီး ဖြစ်ပါက (သို့) ယင်းစာမျက်နှာသည် အလွတ်မဖြစ်ပါက (သို့) ပြန်ညွှန်းတစ်ခု မရှိပါက (သို့) ယခင်က ပြုပြင်ထားသော မှတ်တမ်း မရှိပါက စာမျက်နှာသည် <strong>ရွေ့မည်မဟုတ်</strong> သည်ကို သတိပြုပါ။ \nဆိုလိုသည်မှာ သင်သည် အမှားတစ်ခု ပြုလုပ်မိပါက စာမျက်နှာကို ယခင်အမည်ကို ပြန်လည် ပြောင်းလဲပေးနိုင်သည်။ ရှိပြီသားစာမျက်နှာတစ်ခုကို စာမျက်နှာ အသစ်နှင့် ပြန်အုပ် overwrite ခြင်း မပြုနိုင်။\n\n<strong>သတိပေးချက်!</strong>\nဤသည်မှာ လူဖတ်များသော စာမျက်နှာတစ်ခုဖြစ်ပါက မမျှော်လင့်ထားသော၊ ကြီးမားသော အပြောင်းအလဲတစ်ခု ဖြစ်ပေါ်လာနိုင်သည်။\nထို့ကြောင့် ဆက်လက် မဆောင်ရွက်မီ သင်သည် နောက်ဆက်တွဲ အကျိုးဆက်များကို နားလည်ကြောင်း ကျေးဇူးပြု၍ သေချာပါစေ။",
+       "movepagetalktext": "ဤအကွက်ကို အမှန်ခြစ်လိုက်ခြင်းဖြင့် ဗလာမဟုတ်သော ဆွေးနွေးချက်စာမျက်နှာသည် ရှိနှင့်ပြီး မဟုတ်လျှင် ဆက်နွယ်နေသော ဆွေးနွေးချက် စာမျက်နှာကို ခေါင်းစဉ်အသစ်သို့  အလိုအလျောက် ရွှေ့ပစ်မည် ဖြစ်သည်။\n\nဤကိစ္စရပ်တွင် သင် ဆန္ဒရှိလျှင် စာမျက်နှာကို မိမိကိုယ်တိုင် သွားရောက်ရွှေ့ပြောင်း ပေါင်းစပ်နိုင်သည်။",
+       "newtitle": "ခေါင်းစဉ်အသစ်:",
        "move-watch": "မူရင်းစာမျက်နှာနှင့် ဦးတည်ထားသော စာမျက်နှာတို့ကို စောင့်ကြည့်ရန်",
        "movepagebtn": "စာ​မျက်​နှာ​ကို ရွှေ့ပြောင်းရန်",
        "pagemovedsub": "ပြောင်းရွှေ့ခြင်းအောင်မြင်သည်",
        "movelogpage": "ရွှေ့ပြောင်းခြင်း မှတ်တမ်း",
        "movereason": "အ​ကြောင်း​ပြ​ချက် -",
        "revertmove": "ပြောင်းရန်",
-       "delete_and_move": "ဖျက်ပြီးရွှေ့ရန်",
        "delete_and_move_confirm": "ဟုတ်ပါသည်။ စာမျက်နှာကို ဖျက်ပါ။",
        "immobile-source-page": "ဤစာမျက်နှာကို ရွှေ့မရပါ။",
        "export": "စာမျက်နှာများကို Export ထုတ်ရန်",
        "importsuccess": "ထည့်သွင်းခြင်း ပြီးဆုံးပါပြီ။",
        "import-noarticle": "မည်သည့်စာမျက်နှာမှ ထည့်သွင်းခြင်းမရှိပါ။",
        "importlogpage": "ထည့်သွင်းသည့် မှတ်တမ်း",
-       "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-login": "á\80\99á\80¾á\80\90á\80ºá\80\95á\80¯á\80¶á\80\90á\80\84á\80ºá\80\96á\80¼á\80\84á\80·á\80º log in á\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º á\80¡á\80¬á\80¸á\80\95á\80±á\80¸á\80\95á\80«á\80\9eá\80\8aá\80ºá\81\8b á\80\9eá\80­á\80¯á\80·á\80\9eá\80±á\80¬á\80º á\80\99á\80¾á\80\90á\80ºá\80\95á\80¯á\80¶á\80\99á\80\90á\80\84á\80ºá\80\99á\80\94á\80±á\80\9b မဟုတ်ပါ။",
+       "tooltip-pt-mycontris": "{{GENDER:|သင်၏}} ဆောင်ရွက်ချက်များ စာရင်း",
+       "tooltip-pt-login": "á\80\9eá\80\84á\80·á\80ºá\80¡á\80¬á\80¸ á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\9dá\80\84á\80ºá\80\9bá\80\94á\80º á\80\90á\80­á\80¯á\80\80á\80ºá\80\90á\80½á\80\94á\80ºá\80¸á\80\95á\80«á\80\9eá\80\8aá\80ºá\81\8b á\80\9eá\80­á\80¯á\80·á\80\9eá\80±á\80¬á\80º á\80\99á\80\96á\80¼á\80\85á\80ºá\80\99á\80\94á\80± á\80\9cá\80¯á\80\95á\80ºá\80\86á\80±á\80¬á\80\84á\80ºá\80\9bá\80\94á\80º မဟုတ်ပါ။",
        "tooltip-pt-logout": "ထွက်​ပါ​",
+       "tooltip-pt-createaccount": "အကောင့်တစ်ခုကို ဖန်တီးပြီး ဝင်ရောက်ရန် သင့်အား တိုက်တွန်းပါသည်။ သို့သော် မဖြစ်မနေ မဟုတ်ပါ။",
        "tooltip-ca-talk": "မာတိကာ စာမျက်နှာအတွက် ဆွေးနွေးချက်များ",
-       "tooltip-ca-edit": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90á\80ºá\80\94á\80­á\80¯á\80\84á\80ºá\80\9eá\80\8aá\80ºá\81\8b á\80\80á\80»á\80±á\80¸á\80\87á\80°á\80¸á\80\95á\80¼á\80¯á\81\8d á\80\99á\80\9eá\80­á\80\99á\80ºá\80¸á\80\81á\80\84á\80º á\80\94á\80\99á\80°á\80\94á\80¬ á\80\81á\80\9cá\80¯á\80\90á\80ºá\80\80á\80­á\80¯á\80\94á\80¾á\80­á\80\95á\80ºá\80\95á\80¼á\80®á\80¸ á\80\80á\80¼á\80\8aá\80·á\80ºá\80\95á\80±á\80¸á\80\95á\80«á\81\8b",
+       "tooltip-ca-edit": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\95á\80¼á\80\84á\80ºá\80\9bá\80\94á\80º",
        "tooltip-ca-addsection": "အပိုင်းသစ်တစ်ခု စရန်",
        "tooltip-ca-viewsource": "ဤစာမျက်နှာကို တည်းဖြတ်ခြင်းမှ တားဆီးထားသည်။\nရင်းမြစ် စာသားများကို ကြည့်ရှုနိုင်သည်။",
        "tooltip-ca-history": "ဤစာမျက်နှာ၏ ယခင်မူများ",
        "tooltip-ca-protect": "ဤစာမျက်နှာကို ထိန်းသိမ်းပါ",
-       "tooltip-ca-unprotect": "á\80¤á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\80á\80­á\80¯ á\80\99á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\90á\80±á\80¬á\80·ရန်",
+       "tooltip-ca-unprotect": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬ á\80\80á\80¬á\80\80á\80½á\80\9aá\80ºá\80\81á\80¼á\80\84á\80ºá\80¸á\80\80á\80­á\80¯ á\80\95á\80¼á\80±á\80¬á\80\84á\80ºá\80¸á\80\9cá\80²ရန်",
        "tooltip-ca-delete": "ဤစာမျက်နှာဖျက်ပါ",
        "tooltip-ca-move": "ဤစာမျက်နှာကို ရွှေ့ပြောင်းရန်",
        "tooltip-ca-watch": "ဤစာမျက်နှာကို စောင့်ကြည့်စာရင်းသို့ ထည့်ရန်",
        "tooltip-n-mainpage-description": "ဗဟိုစာမျက်နှာသို့ သွားရန်",
        "tooltip-n-portal": "ပရောဂျက်အကြောင်း၊ သင်ဘာလုပ်ပေးနိုင်သည် နှင့် ဘယ်နေရာတွင် ရှာဖွေရန်များ",
        "tooltip-n-currentevents": "လက်ရှိ အဖြစ်အပျက်များမှ နောက်ခံ အချက်အလက်များကို ရှာရန်",
-       "tooltip-n-recentchanges": "ဝီကီမှ လတ်တလောအပြောင်းအလဲများ စာရင်း",
+       "tooltip-n-recentchanges": "ဝီကီမှ လတ်တလော အပြောင်းအလဲများ စာရင်း",
        "tooltip-n-randompage": "ကျပန်းစာမျက်နှာ ပြရန်",
        "tooltip-n-help": "ရှာဖွေဖော်ထုတ်ရန်နေရာ",
        "tooltip-t-whatlinkshere": "ဤနေရာသို့ လမ်းညွှန် လင့်ထားသည့် ဝီကီစာမျက်နှာများ၏ စာရင်း",
        "tooltip-t-recentchangeslinked": "ဤစာမျက်နှာမှ ညွှန်းထားသည့် စာမျက်နှာများမှ လတ်တလော အပြောင်းအလဲများ",
        "tooltip-feed-rss": "ဤစာမျက်နှာအတွက် RSS feed",
        "tooltip-feed-atom": "ဤစာမျက်နှာအတွက် Atom feed",
-       "tooltip-t-contributions": "ဤအသုံးပြုသူ၏ ပံ့ပိုးပေးမှုများကို ကြည့်ရန်",
-       "tooltip-t-emailuser": "ဤအသုံးပြုသူထံ အီးမေး ပို့ရန်",
+       "tooltip-t-contributions": "{{GENDER:$1|ဤအသုံးပြုသူ}}၏ ဆောင်ရွက်ချက်များ စာရင်း",
+       "tooltip-t-emailuser": "{{GENDER:$1|ဤအသုံးပြုသူထံ}} အီးမေးပေးပို့ရန်",
        "tooltip-t-upload": "ဖိုင်တင်ရန်",
        "tooltip-t-specialpages": "အထူး စာမျက်နှာ စာရင်းများ",
        "tooltip-t-print": "ဤစာမျက်နှာ၏ ပရင့်ထုတ်နိုင်သောမူ",
        "tooltip-preferences-save": "ရေးချယ်စရာများကို သိမ်းရန်",
        "tooltip-summary": "အတိုချုပ်ထည့်ရန်",
        "others": "အခြား",
+       "pageinfo-toolboxlink": "စာမျက်နှာ အချက်အလက်များ",
        "filedeleteerror-short": "ဖိုင်ဖျက်ရာတွင် အမှားအယွင်း - $1",
        "previousdiff": "← တည်းဖြတ်မူ အဟောင်း",
        "nextdiff": "ပိုသစ်သော တည်းဖြတ်မှု",
        "file-info-size": "$1 × $2 pixels, ဖိုင်အရွယ်အစား - $3, MIME အမျိုးအစား $4",
        "file-nohires": "သည်ထက်ကြီးသော resolution မရှိပါ.",
        "svg-long-desc": "SVG ဖိုင်, $1 × $2 pixels ကို အကြံပြုသည်, ဖိုင်အရွယ်အစား - $3",
-       "show-big-image": "resolution အပြည့်",
+       "show-big-image": "မူရင်းဖိုင်",
+       "show-big-image-preview": "ဤနမူနာ၏ အရွယ်အစား - $1။",
        "newimages": "ပုံအသစ်များပြခန်း",
        "newimages-legend": "စိစစ်မှု",
        "newimages-label": "ဖိုင်အမည် (သို့ ယင်း၏အစိတ်အပိုင်း) -",
        "exif-imagewidth": "အကျယ်",
        "exif-imagelength": "အမြင့်",
        "exif-bitspersample": "အစိတ်အပိုင်းတစ်ခုတွင်ပါဝင်သော အပိုင်းငယ်များ",
+       "exif-xresolution": "အလျားလိုက် ပုံရိပ်ပြတ်သားမှု",
+       "exif-yresolution": "ဒေါင်လိုက် ပုံရိပ်ပြတ်သားမှု",
+       "exif-datetime": "ဖိုင်အပြောင်းအလဲ ရက်စွဲနှင့် အချိန်",
        "exif-imagedescription": "ပုံခေါင်းစဉ်",
        "exif-make": "ကင်မရာ ထုတ်လုပ်သူ",
        "exif-model": "ကင်မရာ မော်ဒယ်",
        "exif-software": "အသုံးပြုထားသော ဆော့ဝဲ",
        "exif-artist": "ဖန်တီးသူ",
        "exif-copyright": "မူပိုင်ခွင့်ပိုင်ရှင်",
-       "exif-pixelydimension": "á\80\90á\80\9bá\80¬á\80¸á\80\9dá\80\84á\80ºá\80\95á\80¯á\80¶á\80¡á\80\80á\80»á\80\9aá\80º",
-       "exif-pixelxdimension": "á\80\90á\80\9bá\80¬á\80¸á\80\9dá\80\84á\80º á\80\95á\80¯á\80¶á\80¡á\80\99á\80¼á\80\84á\80·á\80º",
+       "exif-pixelydimension": "ပုံအကျယ်",
+       "exif-pixelxdimension": "ပုံအမြင့်",
        "exif-usercomment": "အသုံးပြုသူ မှတ်ချက်များ",
        "exif-relatedsoundfile": "ဆက်နွယ်သော အသံဖိုင်",
        "exif-datetimeoriginal": "ဒေတာဖန်တီးခဲ့သော နေ့စွဲနှင့် အချိန်",
        "exif-exposuretime-format": "$1 စက္ကန့် ($2)",
-       "exif-shutterspeedvalue": "ရှပ်တာ အမြန်နှုန်း",
+       "exif-shutterspeedvalue": "APEX ရှပ်တာ အမြန်နှုန်း",
        "exif-flash": "ဖလက်ရှ်",
        "exif-filesource": "ဖိုင်ရင်းမြစ်",
        "exif-gpslatitude": "လတ္တီကျု",
        "exif-subjectdistancerange-1": "မက်ကရို",
        "exif-gpslongitude-w": "အနောက်လောင်ဂျီကျု",
        "exif-gpsspeed-m": "တစ်နာရီလျှင် ရှိသည့် မိုင်နှုန်း",
+       "exif-dc-contributor": "ဆောင်ရွက်ပေးထားသူများ",
        "namespacesall": "အားလုံး",
        "monthsall": "အားလုံး",
        "confirmemail": "အီးမေးကိုအတည်ပြုပါ",
        "watchlisttools-view": "ကိုက်ညီသော အပြောင်းအလဲများကို ကြည့်ရန်",
        "watchlisttools-edit": "စောင့်ကြည့်စာရင်းများကို ကြည့်ပြီး တည်းဖြတ်ပါ။",
        "watchlisttools-raw": "စောင့်ကြည့်စာရင်း အကြမ်းကို တည်းဖြတ်ရန်",
+       "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|ဆွေးနွေး]])",
        "duplicate-defaultsort": "'''သတိပေးချက် -''' ပုံမှန် sort key \"$2\" oသည် ယခင်ပုံမှန်ဖြစ်သော sort key \"$1\" ကို override ထပ်ရေးမည်ဖြစ်သည်.",
        "version": "ဗားရှင်း",
        "version-specialpages": "အ​ထူး ​စာ​မျက်​နှာ​များ",
        "version-other": "အခြား",
-       "version-license": "လိုင်စင်",
+       "version-license": "á\80\99á\80®á\80\92á\80®á\80\9aá\80¬á\80\9dá\80®á\80\80á\80® á\80\9cá\80­á\80¯á\80\84á\80ºá\80\85á\80\84á\80º",
        "version-software": "သွင်းထားသော ဆော့ဝဲ",
        "version-software-product": "ထုတ်ကုန်",
        "version-software-version": "ဗားရှင်း",
        "specialpages": "အ​ထူး ​စာ​မျက်​နှာ​များ",
        "specialpages-group-maintenance": "ထိန်းသိမ်းမှု အစီရင်ခံချက်များ",
        "specialpages-group-other": "အခြားအထူးစာမျက်နှာများ",
-       "specialpages-group-login": "Login ဝင်ရန်/ အကောင့်လုပ်ရန်",
+       "specialpages-group-login": "Log in ဝင်ရန်/ အကောင့် ဖန်တီးရန်",
        "specialpages-group-changes": "လတ်တလောအေပြောင်းအလဲနှင့် မှတ်တမ်းများ",
        "specialpages-group-media": "မီဒီယာ အစီရင်ခံချက်များနှင့် Upload တင်ထားသည်များ",
        "specialpages-group-users": "အသုံးပြုသူများနှင့် အခွင့်အရေးများ",
        "specialpages-group-highuse": "အသုံးများသော စာမျက်နှာများ",
        "specialpages-group-pages": "စာမျက်နှာစာရင်းများ",
        "specialpages-group-pagetools": "စာမျက်နှာအတွက် ကိရိယာများ",
-       "specialpages-group-wiki": "á\80\9dá\80®á\80\80á\80®á\80\92á\80±á\80\90á\80¬á\80\94á\80¾á\80\84á\80·á\80º á\80\80á\80­á\80\9bá\80­á\80\9aá\80¬á\80\99á\80»á\80¬á\80¸",
+       "specialpages-group-wiki": "ဒေတာနှင့် ကိရိယာများ",
        "specialpages-group-redirects": "အထူးစာမျက်နှာများကို ပြန်ညွှန်းနေသည်",
        "specialpages-group-spam": "စပမ်းကိရိယာများ",
        "blankpage": "ဗလာစာမျက်နှာ",
        "tags-title": "အမည်တွဲ",
        "tags-tag": "အမည်တွဲ အမည်",
        "tags-edit": "ပြင်​ဆင်​ရန်",
-       "comparepages": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\94á\80¾á\80¯á\80­င်းယှဉ်ရန်",
+       "comparepages": "á\80\85á\80¬á\80\99á\80»á\80\80á\80ºá\80\94á\80¾á\80¬á\80\99á\80»á\80¬á\80¸á\80\80á\80­á\80¯ á\80\94á\80¾á\80­á\80¯င်းယှဉ်ရန်",
        "compare-page1": "စာမျက်နှာတစ်",
        "compare-page2": "စာမျက်နှာနှစ်",
        "compare-rev1": "မူ တစ်",
        "htmlform-submit": "ထည့်သွင်းရန်",
        "htmlform-reset": "ပြောင်းလဲထားသည်များ မလုပ်တော့ရန်",
        "htmlform-selectorother-other": "အခြား",
+       "logentry-delete-delete": "$3 စာမျက်နှာကို $1 က {{GENDER:$2|ဖျက်ပစ်ခဲ့သည်}}",
        "revdelete-restricted": "အက်ဒမင်များသို့ ကန့်သတ်ချက်များ သက်ရောက်ရန်",
        "revdelete-unrestricted": "အက်ဒမင်များအတွက် ကန့်သတ်ချက်များကို ဖယ်ရှားရန်",
+       "logentry-move-move": "$3 စာမျက်နှာကို $4 သို့ $1က {{GENDER:$2|ရွှေ့ခဲ့သည်}}",
+       "logentry-newusers-create": "အသုံးပြုသူအကောင့် $1 ကို {{GENDER:$2|ဖန်တီးခဲ့သည်}}",
+       "logentry-upload-upload": "$1 သည် $3 ကို {{GENDER:$2|upload တင်ခဲ့သည်}}",
        "rightsnone": "(ဘာမှမရှိ)",
        "revdelete-summary": "အကျဉ်းချုပ်ကို တည်းဖြတ်ရန်",
        "searchsuggest-search": "ရှာဖွေရန်",
index 8d21fde..419cd72 100644 (file)
        "createaccount-title": "Cuentah ītlachīhualiz ic {{SITENAME}}",
        "loginlanguagelabel": "Tlahtōlli: $1",
        "pt-login": "Xicalaqui",
+       "pt-login-button": "Xicalaqui",
        "pt-createaccount": "Xicchīhua motlapōhual",
        "changepassword": "Xicpatla motlahtōlichtacāyo",
        "resetpass_header": "Xicpatla motlahtōlichtacāyo",
        "rcshowhideminor": "$1 tlapatlalitzintli",
        "rcshowhideminor-show": "Ticnēxtīz",
        "rcshowhidebots": "$1 tepoztlācah",
+       "rcshowhidebots-show": "Xicnēxti",
        "rcshowhidebots-hide": "Tiquihyānaz",
        "rcshowhideliu": "$1 tēmachiyōmacalli tlatequitiltilīltin",
        "rcshowhideanons": "$1 ahtōcā tlatequitiltilīlli",
        "linksearch-ok": "Tictēmōz",
        "linksearch-line": "$1 tzonhuīlo īxquichca $2",
        "listusers-submit": "Tiquittāz",
+       "activeusers-submit": "Xiquitta",
        "listgrouprights-group": "Olōlli",
        "listgrouprights-rights": "Huelītiliztli",
        "emailuser": "Tiquēhualtlīz maltzinteyōtl netitlaniztli inīn tlatequitiltilīlli",
        "rollback-success": "Ōmotlacuep $1 ītlahcuilōl; āxcān achto $2 ītlahcuilōl.",
        "changecontentmodel-title-label": "Tlaīxtōcāitl",
        "changecontentmodel-reason-label": "Tleīpampa:",
+       "protectlogpage": "Tlapiyaliztlīlmachiyōtīlli",
        "protectedarticle": "ōmoquīxti \"[[$1]]\"",
        "unprotectedarticle": "ōahmoquīxtih «[[$1]]»",
        "prot_1movedto2": "[[$1]] ōmozacac īhuīc [[$2]]",
        "movelogpage": "Tlazacaliztli tlahcuilōlloh",
        "movereason": "Īxtlamatiliztli:",
        "revertmove": "tlacuepāz",
-       "delete_and_move": "Ticpolōz auh ticzacāz",
        "delete_and_move_confirm": "Quēmah, ticpolōz in zāzanilli",
        "immobile-source-namespace": "Ahmo huelīti mozaca zāzanilli tōcātzimpan \"$1\"",
        "immobile-target-namespace": "Ahmo huelīti mozaca zāzanilli tōcātzinhuīc \"$1\"",
        "exif-imagedescription": "Īxiptli ītōcā",
        "exif-software": "Software ōmotēquitilti",
        "exif-artist": "Chīhualōni",
+       "exif-exifversion": "Exif-cuepaliztli",
        "exif-usercomment": "Quihtoa tlatequitiltilīlli",
        "exif-exposuretime": "Cāuhcāyōtl",
        "exif-fnumber": "F Tlapōhualli",
index 9b7557e..106ac6d 100644 (file)
@@ -52,7 +52,7 @@
        "underline-default": "Tòe liû-lám-khì ê siat-piān",
        "editfont-style": "Pian-chi̍p sî ēng ê jī-thé hêng-sek:",
        "editfont-default": "Tòe liû-lám-khì ê default",
-       "editfont-monospace": "Monospaced jī-thé",
+       "editfont-monospace": "Kāng-khoán-khoah ê jī-thé",
        "editfont-sansserif": "Sans-serif jī-thé",
        "editfont-serif": "Serif jī-thé",
        "sunday": "Lé-pài",
        "passwordreset-username": "Lí ê iōng-chiá miâ-chheng:",
        "passwordreset-email": "Tiān-chú-phoe tē-chí:",
        "passwordreset-emailelement": "Iōng-chiá: \n$1\n\nLîm-sî ê bi̍t-bé: \n$2",
-       "passwordreset-emailsent": "Têng siat bi̍t-bé ê tiān-chú-phoe í-keng kià chhut-khì ah.",
+       "passwordreset-emailsentemail": "Têng siat bi̍t-bé ê tiān-chú-phoe í-keng kià chhut-khì ah.",
        "changeemail": "Kái tiān-chú-phoe ê tē-chí",
        "changeemail-oldemail": "Chit-má ê E-mail tē-chí:",
        "changeemail-newemail": "Sin E-mail ê chū-chí:",
index 0c9e0b0..db2a66f 100644 (file)
@@ -30,7 +30,7 @@
        "tog-watchdefault": "Azzecca 'e paggene e li files cagnàte a l'elenco 'e cuntrollo",
        "tog-watchmoves": "Azzecca 'e paggene e li files spustate a l'elenco 'e cuntrollo",
        "tog-watchdeletion": "Azzecca 'e paggene e li files scancellate a l'elenco 'e cuntrollo",
-       "tog-watchrollback": "Azzecca 'e paggene addò aggio fatto nu rollback a l'elenco 'e cuntrollo",
+       "tog-watchrollback": "Azzecca 'e paggene addò aggio fatto nu rollback ('o torna arreto) a l'elenco 'e cuntrollo",
        "tog-minordefault": "Indica ogne cagnamento comme piccerillo (predefinito)",
        "tog-previewontop": "Vide previsióne primma d' 'a casella 'e modifica",
        "tog-previewonfirst": "Vide previsióne 'a primma vota",
@@ -54,7 +54,7 @@
        "tog-ccmeonemails": "Famme na masciata pùre c' 'a copia 'e le mail mannate a l'ati utente",
        "tog-diffonly": "Nun me fà vedé cuntenute aropp'o cunfronto nfra verziune",
        "tog-showhiddencats": "Fa' vedé 'e categurie annascunnute",
-       "tog-norollbackdiff": "Nun fà vedé 'o cunfronto nfra verziune quanno se fà nu rollback",
+       "tog-norollbackdiff": "Nun fà vedé 'o cunfronto nfra verziune quanno se fà nu rollback ('o torna arreto)",
        "tog-useeditwarning": "Famme sapé quanno lasso na paggena 'e mudifeca senza sarvà 'e cagnamiente",
        "tog-prefershttps": "Usa sempe na connessione sicura quanno s'accummincia sessione",
        "underline-always": "Sèmpe",
        "october-date": "$1 ottovre",
        "november-date": "$1 nuvembre",
        "december-date": "$1 dicembre",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categurìa|Categurìe}}",
        "category_header": "Paggene rìnt'a categurìa \"$1\"",
        "subcategories": "Categurìe secunnarie",
        "searcharticle": "Vàje",
        "history": "Verziune 'e primma",
        "history_short": "Cronologgia",
-       "updatedmarker": "cagnamiénte 'e mija urdema visita",
+       "updatedmarker": "cagnamiénte 'e ll'urdema visita d' 'a mia",
        "printableversion": "Verzione pe' stampa",
        "permalink": "Jonta permanente",
        "print": "Stampà",
        "databaseerror-query": "Richiesta: $1",
        "databaseerror-function": "Funzione: $1",
        "databaseerror-error": "Sbaglio: $1",
-       "transaction-duration-limit-exceeded": "Pe' putè scanzà 'e crià n'auto tiempo e replica, sta transazziona fuje fernuta pecché pe' tramente ca se faceva chesto ($1) s'appassaje 'o lemmeto $2 secondo.\nSi state a cagnà nu cuofeno 'elemente a na vota, tentate e fà nu cuofeno 'operaziune cchiù piccerille mmece.",
+       "transaction-duration-limit-exceeded": "Pe' putè scanzà 'e crià n'auto tiempo e replica, sta transazziona fuje fernuta pecché pe' tramente ca se faceva chesto ($1) s'appassaje 'o lemmeto 'e $2 {{PLURAL:$2|secondo|secunde}}.\nSi state a cagnà nu cuofeno 'elemente a na vota, tentate e fà nu cuofeno 'operaziune cchiù piccerille mmece.",
        "laggedslavemode": "'''Attenzione:''' 'a paggena putesse nun fà vedé ll'aggiornamente cchiù recente.",
        "readonly": "Database bloccato",
        "enterlockreason": "Miette 'o mutivo 'e blocco, nzieme a 'o mumento quanno se penza ca 'o blocco se sarrà fernuto",
        "virus-scanfailed": "scanziona fallita (codece $1)",
        "virus-unknownscanner": "antivirus scanusciuto:",
        "logouttext": "'''Site asciùte.'''\n\nNota ca arcune paggene putessero cuntinuà ad cumparì comme se 'o logout nun fosse affettuato fin quanno nun sarrà pulezzata 'a cache d\"o proprio browser.",
+       "cannotlogoutnow-title": "Nun se pò ascì mò",
+       "cannotlogoutnow-text": "'A disconessione nun è possibbele quanno s'ausa $1.",
        "welcomeuser": "Bemmenuto, $1!",
        "welcomecreation-msg": "'O cunto vuosto è stato criato.\nMo' putite cagnà 'e [[Special:Preferences|preferenze 'e {{SITENAME}}]].",
        "yourname": "Nomme utente",
        "remembermypassword": "Allicuordate d\"a password (for a maximum of $1 {{PLURAL:$1|day|days}})",
        "userlogin-remembermypassword": "Mantienime cullegato",
        "userlogin-signwithsecure": "Usa na conessione sicura",
+       "cannotloginnow-title": "Nun se pò trasì mò",
+       "cannotloginnow-text": "'A connessione nun è possibbele quanno s'ausa $1.",
        "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.",
        "resetpass_submit": "Stabbelite 'a password e trasite",
        "changepassword-success": "'A password è stata cagnata currettamente!",
        "changepassword-throttled": "Songo state fatte troppe tentative 'a trasì.\nAspetta nu $1 apprimma 'e pruvà n'ata vota.",
+       "botpasswords": "Password bot",
+       "botpasswords-summary": "<em>Password bot</em> premmettesse 'e trasì a n'utente pe' bbìa d'API senza ausà 'e credenziale d'acciesso prencepale 'e ll'utenza. 'E deritte utente disponibbele quanno s'affettuasse l'accesso cu na password 'e bot se ponn'apprisentà lemmetate.\n\nSi nun canuscite 'o mutivo p' 'o quale 'o putite fà, allora può darse ca nun l'avissev'a ffà. Nisciuno avesse maje 'addimannà 'e crià una 'e chiste p' 'e ddà a chille.",
+       "botpasswords-disabled": "'E password 'e bot songo stutate.",
+       "botpasswords-no-central-id": "Pe' puté ausà na password bot, avit'a trasì dint'a nu cunto centralizzato.",
+       "botpasswords-existing": "Password bot esistente",
+       "botpasswords-createnew": "Crèa na password bot nòva",
+       "botpasswords-editexisting": "Cagna na password bot esistente",
+       "botpasswords-label-appid": "Nomme d' 'o bot:",
+       "botpasswords-label-create": "Crèa",
+       "botpasswords-label-update": "Agghiuorna",
+       "botpasswords-label-cancel": "Canciella",
+       "botpasswords-label-delete": "Scancèlla",
+       "botpasswords-label-resetpassword": "Riabbìa 'a password",
+       "botpasswords-label-grants": "Assegnaziune apprecabbele:",
+       "botpasswords-help-grants": "Ogne assegnazione dà acciesso a 'e deritte utente elencate ca n'utenza avesse già. Vedite 'a [[Special:ListGrants|tabbella 'e ll'assegnaziune]] pe' ne mòvere cchiù nfurmaziune.",
+       "botpasswords-label-restrictions": "Restriziune d'uso:",
+       "botpasswords-label-grants-column": "Assegnaziune date",
+       "botpasswords-bad-appid": "'O nomme bot \"$1\" nun è bbuono.",
+       "botpasswords-insert-failed": "Nun se pò azzeccà 'o nomme bot \"$1\". Fosse stato già azzeccato?",
+       "botpasswords-update-failed": "Se scassaje a carrecà 'o nomme bot \"$1\". È stato scancellato?",
+       "botpasswords-created-title": "Password bot criata",
+       "botpasswords-created-body": "'A password bot \"$1\" fuje criata cu' succiesso.",
+       "botpasswords-updated-title": "Password bot agghiurnata",
+       "botpasswords-updated-body": "'A password bot \"$1\" è fuje agghiurnata cu' succiesso.",
+       "botpasswords-deleted-title": "Password bot scancellata",
+       "botpasswords-deleted-body": "'A password bot \"$1\" è stata scancellata.",
+       "botpasswords-newpassword": "'A password nòva pe' puté trasì cu <strong>$1</strong> è <strong>$2</strong>. <em>Pe' piacere signatevello chesto pe' ve ffà conzurtaziune future.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider nun è disponibbele.",
+       "botpasswords-restriction-failed": "'E restriziune 'e password bot nun ve permettessero st'acciesso.",
+       "botpasswords-invalid-name": "'O nomme utente nnecato nun cuntenesse nu spartetóre 'e bot password (\"$1\").",
+       "botpasswords-not-exist": "L'utente \"$1\" nun téne na password bot chiammata \"$2\".",
        "resetpass_forbidden": "'E password nun se ponno cagnà",
        "resetpass-no-info": "Avite 'a trasì ('o login) pe ffà l'acciesso a sta paggena direttamente.",
        "resetpass-submit-loggedin": "Cagna password",
        "passwordreset-emailtext-ip": "Coccherun (può darse ca sì tu, cu n'indirizzo IP $1) ha addimannato na mmasciata c' 'a password nova pe' putè trasì a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associato|L'utente associate}} a st'indirizze e-mail songo:\n\n$2\n\n{{PLURAL:$3|Sta password temporanea ammaturarrà|Sti password temporanee ammaturarranno}} aropp'a {{PLURAL:$5|nu juorno|$5 ghiuorne}}.\nHè 'a trasì e scegliere na password nova mò. \n\nSi nun sì stato tu a fà sta richiesta, o te sì scurdat' 'a password origginale e nun 'a buò cagnà cchiù, lassa perde sta mmasciata e usa 'a password viecchia.",
        "passwordreset-emailtext-user": "L'utente $1 di {{SITENAME}} ha addimannato na mmasciata c' 'a password nova pe' putè trasì a {{SITENAME}} ($4). {{PLURAL:$3|L'utente associato|L'utente associate}} a st'indirizze e-mail songo:\n\n$2\n\n{{PLURAL:$3|Sta password temporanea ammaturarrà|Sti password temporanee ammaturarranno}} aropp'a {{PLURAL:$5|nu juorno|$5 ghiuorne}}.\nHè 'a trasì e scegliere na password nova mò. \n\nSi nun sì stato tu a fà sta richiesta, o te sì scurdat' 'a password origginale e nun 'a buò cagnà cchiù, lassa perde sta mmasciata e usa 'a password viecchia.",
        "passwordreset-emailelement": "Nomme utente: \n$1\n\nPassword temporanea: \n$2",
-       "passwordreset-emailsentemail": "Si chesto fosse nu cunto e-mail riggistrato, allora buò dicere ca se mannarrà na mmasciata e-mail pe' riabbià 'a password.",
-       "passwordreset-emailsentusername": "Si esistesse nu cunto e-mail riggistrato ca currispunnesse a chesto, allora se mannarrà na mmasciata pe' riabbià 'a password.",
+       "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",
        "changeemail": "Cagna o lèva l'indirizzo e-mail",
        "userpage-userdoesnotexist": "'O cunto utente \"<nowiki>$1</nowiki>\" nun è riggistrato. Cuntrolla ca si buò overo crià o cagnà sta paggena.",
        "userpage-userdoesnotexist-view": "'O cunto utente \"$1\" nun è riggistrato.",
        "blocked-notice-logextract": "St'utente è bloccato mò.\nL'urdemo elemento d' 'o riggistro 'e blocche è ripurtato ccà abbascio p'avé nu riferimento:",
-       "clearyourcache": "'''Nota:''' aropo sarvate putisse necessità 'e pulezzà 'a caché d' 'o navigatóre pe' vedé 'e cagnamiente. \n*'''Firefox / Safari''': sprémme 'o buttóne maiuscole e ffà clic ncopp'a ''Recarreca'', o pure spremme ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' ncopp'a Mac)\n*'''Google Chrome''': spremme ''Ctrl-Shift-R'' (''⌘-Shift-R'' ncopp'a nu Mac)\n*'''Internet Explorer''': spremme 'o buttóne ''Ctrl'' pe' tramente ca faie click ncopp'a ''Refresh'', o pure spremmere ''Ctrl-F5''\n*'''Opera''': abbacanta tutt' 'a cache addò menu ''Strumiente → Preferenze''",
+       "clearyourcache": "'''Nota:''' aroppo sarvate putisse necessità 'e pulezzà 'a caché d' 'o navigatóre pe' vedé 'e cagnamiente. \n*'''Firefox / Safari''': sprémme 'o buttóne maiuscole e ffà clic ncopp'a ''Recarreca'', o pure spremme ''Ctrl-F5'' o ''Ctrl-R'' (''⌘-R'' ncopp'a Mac)\n*'''Google Chrome''': spremme ''Ctrl-Shift-R'' (''⌘-Shift-R'' ncopp'a nu Mac)\n*'''Internet Explorer''': spremme 'o buttóne ''Ctrl'' pe' tramente ca faie click ncopp'a ''Refresh'', o pure spremmere ''Ctrl-F5''\n*'''Opera''': sbacanta tutt' 'a cache addò menu ''Strumiente → Preferenze''",
        "usercssyoucanpreview": "'''Cunziglio:''' spremme 'o buttone 'Vide anteprimma' pe' pruvà 'o CSS nuovo apprimma d' 'o sarvà.",
        "userjsyoucanpreview": "'''Cunziglio:''' spremme 'o buttone 'Vide anteprimma' pe' pruvà 'o JavaScript nuovo apprimma d' 'o sarvà.",
        "usercsspreview": "'''Arricuordate ca chest'è sulamente n'anteprimma p' 'o CSS perzunale. 'E cagnamiente nun so' state ancora sarvate!'''",
        "userrights": "Gestione d' 'e permesse 'e l'utente",
        "userrights-lookup-user": "Gestione 'e gruppe d'utenza",
        "userrights-user-editname": "Nzertàte nu nomme utente:",
-       "editusergroup": "Cagnate 'e gruppe d'utenze",
+       "editusergroup": "Cagnate 'e gruppe d'{{GENDER:$1|utenze}}",
        "editinguser": "Cagnamiento d' 'e deritte d'{{GENDER:$1|utente}} '''[[User:$1|$1]]''' $2",
        "userrights-editusergroup": "Cagnate 'e gruppe d'utenze",
-       "saveusergroups": "Sarvate 'e gruppe d'utenza",
+       "saveusergroups": "Sarvate 'e gruppe d'{{GENDER:$1|utenza}}",
        "userrights-groupsmember": "Ffà parte {{PLURAL:$1|d' 'o gruppo|d' 'e gruppe}}:",
        "userrights-groupsmember-auto": "Membro mplicito 'e:",
        "userrights-groups-help": "Putite cagnà 'e gruppe assegnate a l'utente:\n* Na cascia 'e spunta scigliuta significasse ca appartenenza 'e l'utente a 'o gruppo\n* Na cascia 'e spunta nun scigliuta significasse 'a nun appartenenza a 'o gruppo.\n* 'O simmolo * significasse ca nun se può scancellà l'appartenenza a 'o gruppo aropp'a ll'avé miso (o viceversa).",
        "right-createpage": "Crìa paggene (ca nun songo paggene 'e chiacchiera)",
        "right-createtalk": "Crìa chiacchiere 'e paggena",
        "right-createaccount": "Crìa utenze nove",
+       "right-autocreateaccount": "Tràse automaticamente cu n'utenza 'e fore",
        "right-minoredit": "Nzigna 'e cagnamiente comme minure",
        "right-move": "Muove 'e paggene",
        "right-move-subpages": "Muove 'e paggene nzieme ê sottopaggene suje",
        "right-blockemail": "Blocca n'utente a mannà e-mail",
        "right-hideuser": "Blocca n'utente e fallo sparì 'a 'o pubbreco",
        "right-ipblock-exempt": "Ignora 'e blocche 'e l'IP, 'e blocche automatece e li blocche 'e range 'e l'IP",
-       "right-proxyunbannable": "Passa 'e blocche automatiche d' 'e proxy",
        "right-unblockself": "Sblocca se stesso",
        "right-protect": "Cagna 'e livelle 'e prutezione 'e cagna paggene prutette ricurzivamente",
        "right-editprotected": "Cagna 'e paggene prutette cu \"{{int:protect-level-sysop}}\"",
        "right-editmyprivateinfo": "Cagna 'e date perzunale proprie (p'esempio: e-mail, nomme overo)",
        "right-editmyoptions": "Cagna 'e preferenze proprie",
        "right-rollback": "Annulla ampresso 'e cagnamiente 'e ll'urdem'utente c'avesse cagnato na paggena particulare",
-       "right-markbotedits": "Nzégna 'e cagnamiente suggette a rollback comme affettuate 'a nu bot",
+       "right-markbotedits": "Nzégna 'e cagnamiente suggette a rollback ('o torna arreto) comme affettuate 'a nu bot",
        "right-noratelimit": "Nun suggetto a lemmeto d'aziune",
        "right-import": "Carreca paggene 'a n'ati wiki",
        "right-importupload": "Carreca paggene 'a n'upload 'e file",
        "right-managechangetags": "Crìa e scancella 'e [[Special:Tags|tag]] d' 'o database",
        "right-applychangetags": "Appreca [[Special:Tags|tag]] pe' tramente ca se fanno 'e cagnamiente 'e coccheruno",
        "right-changetags": "Azzecca o lèva a caso 'e [[Special:Tags|tag]] dint'a verziune nnividuale e riggistre 'e log",
+       "grant-generic": "Pacco 'e deritte \"$1\"",
+       "grant-group-page-interaction": "Interagisce ch' 'e paggene",
+       "grant-group-file-interaction": "Intergische ch' 'e file 'e media",
+       "grant-group-watchlist-interaction": "Interagisce cu l'elenco 'e cuntrollo vuosto",
+       "grant-group-email": "Manna e-mail",
+       "grant-group-high-volume": "Secuta attività 'e volume massivo",
+       "grant-group-customization": "Personalizzaziona e preferenze",
+       "grant-group-administration": "Secuta aziune ammenistrative",
+       "grant-group-other": "Attività differénte",
+       "grant-blockusers": "Blocca e sblocca utente",
+       "grant-createaccount": "Crìa cunte",
+       "grant-createeditmovepage": "Créa, cagna e móve paggene",
+       "grant-delete": "Scancella paggene, verziune, trasute 'e riggistro",
+       "grant-editinterface": "Cagna 'o namespace MediaWiki e CSS/JavaScript 'e ll'utente",
+       "grant-editmycssjs": "Cagna 'e CSS/JavaScript 'e ll'utenza d' 'a vosta",
+       "grant-editmyoptions": "Cagna 'e preferenze utente proprie",
+       "grant-editmywatchlist": "Cagna l'elenco 'e cuntrolo 'o tuojo",
+       "grant-editpage": "Cagna 'e paggene esistente",
+       "grant-editprotected": "Cagna 'e paggine prutette",
+       "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-protect": "Prutegge e sprutegge paggene",
+       "grant-rollback": "Torna arrèto 'e cagnamiente a 'e paggene",
+       "grant-sendemail": "Manna na mail a ll'at'utente",
+       "grant-uploadeditmovefile": "Carreca, scagna, e mòve file",
+       "grant-uploadfile": "Carreca file nuove",
+       "grant-basic": "Deritte 'e base",
+       "grant-viewdeleted": "Vide 'e file e paggene scancellate",
+       "grant-viewmywatchlist": "Vide l'elenco 'e cuntrullate",
        "newuserlogpage": "Riggistro 'e nuove utente",
        "newuserlogpagetext": "Chest'è nu riggistro 'e criazione d'utenze.",
        "rightslog": "Deritte 'e ll'utente",
        "action-createpage": "crìa paggene",
        "action-createtalk": "crìa chiacchiere 'e paggena",
        "action-createaccount": "crìa stu cunto utente",
+       "action-autocreateaccount": "automaticamente crèa stu cunto utente 'e fore",
        "action-history": "vide 'a cronologgia 'e sta paggena",
        "action-minoredit": "nzegnà stu cagnamiento comme minore",
        "action-move": "mòve sta paggena",
        "enhancedrc-history": "cronologgia",
        "recentchanges": "Urdeme nove",
        "recentchanges-legend": "Opzione urdeme cagnamiénte",
-       "recentchanges-summary": "Ncoppa chesta paggena song' appresentate ll'urdeme cagnamiente fatte ê cuntenute d\"o sito.",
+       "recentchanges-summary": "Ncopp'a chesta paggena song' appresentate ll'urdeme cagnamiente fatte ê cuntenute d\"o sito.",
        "recentchanges-noresult": "Nisciuno cagnamiento dint'o periodo dato ca soddisfà sti criterie.",
        "recentchanges-feed-description": "Ncoppa chistu feed song' appresentate ll'urdeme cagnamiente fatte ê cuntenute d\"o sito.",
        "recentchanges-label-newpage": "Chista modifica ha criato 'na nova paggena",
        "upload-form-label-select-file": "Sceglie file",
        "upload-form-label-infoform-title": "Dettaglie",
        "upload-form-label-infoform-name": "Nomme",
+       "upload-form-label-infoform-name-tooltip": "Nu titolo unico e distintivo p' 'o file, ca serverrà comm'o nomme file. Putite ausà lenguaggio semprice ch' 'e spazi. Nun azzeccà l'estensione d' 'o file.",
        "upload-form-label-infoform-description": "Descrizzione",
+       "upload-form-label-infoform-description-tooltip": "Facite 'a descriziona sintetica 'e tuttuquanto fosse degno 'e nota a proposito 'e st'opera. P' 'e foto, facite assapé 'e ccosi principale ca songo rappresentate, l'accasione e/o luogo dint' 'o quale so' state scattate.",
        "upload-form-label-usage-title": "Aúso",
        "upload-form-label-usage-filename": "Nomme d' 'o file",
        "foreign-structured-upload-form-label-own-work": "Chest'è fatica mia",
        "log-title-wildcard": "Ascìa titole c'accummencieno cu stu testo",
        "showhideselectedlogentries": "Cagna visibbelità d' 'e riggistre scigliute",
        "log-edit-tags": "Cagna 'e tag d' 'e riggistre 'e log scigliute",
+       "checkbox-select": "Sceglie: $1",
+       "checkbox-all": "Tutte",
+       "checkbox-none": "Nisciuna",
+       "checkbox-invert": "Nvèrte",
        "allpages": "Tutte 'e ppaggene",
        "nextpage": "Paggena appriesso ($1)",
        "prevpage": "Paggena apprima ($1)",
        "listgrouprights-namespaceprotection-header": "Restriziune d' 'o namespace",
        "listgrouprights-namespaceprotection-namespace": "Namespace:",
        "listgrouprights-namespaceprotection-restrictedto": "Diritto 'e cagnamiento 'e l'utente",
+       "listgrants": "Assegnaziune",
+       "listgrants-summary": "Ccà abbascio è ripurtato n'elenco 'e cuncessiune, ch' 'e deritte utente suoccie a chiste. Ll'utente ponno autorizzà ll'apprecaziune 'ausann'a propria utenza, ma cu st'auturizzaziune lemmetate 'n base a 'e nzegne ca l'utente mpustaje a ll'apprecaziona. N'apprecaziona ca se pigliasse 'o cunto 'e n'utente nun putesse effettivamente ausà 'e deritte ca n'utente nun téne.\nPutite truvà [[{{MediaWiki:Listgrouprights-helppage}}|cchiù nfurmaziune]] ncopp' 'e deritte ndividuale.",
+       "listgrants-grant": "Assegnaziuna",
+       "listgrants-rights": "Deritte",
        "trackingcategories": "Categurìe 'e cuntrollo",
        "trackingcategories-summary": "Sta paggena elenca 'e categurìe ca stann'automaticamente popolate 'a 'o software Mediawiki. 'E nomme 'e lloro se ponno cagnà quanno se cagnano 'e relative mmasciate 'e sistema dint' 'o namespace {{ns:8}}.",
        "trackingcategories-msg": "Categurìe 'e cuntrollo",
        "wlshowhideanons": "utente anonime",
        "wlshowhidepatr": "cagnamiente cuntrullate",
        "wlshowhidemine": "cagnamiente d' 'e mieie",
+       "wlshowhidecategorization": "categorizzaziona d' 'a paggena",
        "watchlist-options": "Opziune 'a l'elenco 'e paggene cuntrullate",
        "watching": "Cuntrullanno...",
        "unwatching": "Lassanno 'e cuntrullà...",
        "unblock": "Sblocca l'utente",
        "blockip": "Blocca {{GENDER:$1|utente}}",
        "blockip-legend": "Blocca l'utente",
-       "blockiptext": "Ausa 'o modulo ccà abbascio pe' bluccà l'acciesso 'e scrittura a n'indirizzo IP o utente.\nChisto s'avesse 'a ffà sulamente pe' se pruteggere d' 'o vandalismo, d'accordo ch' [[{{MediaWiki:Policy-url}}|'e reole]].\nMettite pure nu mutivo specifico ccà abbascio (p'esempio, facenno 'o nomme 'e paggene addò se so' fatte 'e vandalisme).",
+       "blockiptext": "Ausa 'o modulo ccà abbascio pe' bluccà l'acciesso 'e scrittura a n'indirizzo IP o utente.\nChisto s'avesse 'a ffà sulamente pe' se pruteggere d' 'o vandalismo, d'accordo ch' [[{{MediaWiki:Policy-url}}|'e reole]].\nMettite pure nu mutivo specifico ccà abbascio (p'esempio, facenno 'o nomme 'e paggene addò se so' fatte 'e vandalisme).\nPutite bluccà ntervalle IP ausanno 'a sintasse [https://it.wikipedia.org/wiki/CIDR CIDR]; l'intervallo cchiù ampio cunzentito è /$1 pe' IPv4 e /$2 pe' IPv6.",
        "ipaddressorusername": "Nnerizzo IP o nomme utente",
        "ipbexpiry": "Ammatura:",
        "ipbreason": "Mutivo:",
        "block-log-flags-hiddenname": "nomme utente annascunnuto",
        "range_block_disabled": "'A possibbilità 'e bluccà ntervalle 'e indirizze IP nun è appicciata.",
        "ipb_expiry_invalid": "Tiempo d'ammaturamiento invalido.",
+       "ipb_expiry_old": "'O tiempo d'ammaturamiento è passato già.",
        "ipb_expiry_temp": "'E blocche d' 'e nomme utente annascunnute hanna essere nfinite.",
        "ipb_hide_invalid": "Nun se può scancellà stu cunto; tène cchiù 'e {{PLURAL:$1|nu cagnamiento|$1 cagnamiente}}.",
        "ipb_already_blocked": "\"$1\" è già bloccato.",
        "export-download": "Astipa comm'a nu file",
        "export-templates": "Include 'e template",
        "export-pagelinks": "Include 'e paggene cullegate ca spuntassero nfin' 'a na prufunnità 'e:",
+       "export-manual": "Nzérta paggene manualmente:",
        "allmessages": "'Mmasciate d''o sistema",
        "allmessagesname": "Nomme",
        "allmessagesdefault": "Mmasciata 'e testo predefinita",
        "javascripttest-pagetext-frameworks": "Pe' piacere sciglite uno 'e ll'ambiente 'e test ccà abbascio: $1",
        "javascripttest-pagetext-skins": "Sciglite nu skin pe' ne fà 'e test:",
        "javascripttest-qunit-intro": "Vedite 'a [$1 documentaziona d' 'o test] ncopp'a mediawiki.org.",
-       "tooltip-pt-userpage": "A toja paggena utente",
+       "tooltip-pt-userpage": "'A paggena {{GENDER:|utente}} vòsta",
        "tooltip-pt-anonuserpage": "'A paggena utente pe l'IP ca vuje state cagnanno cumme",
-       "tooltip-pt-mytalk": "A toja paggena 'e discussione",
+       "tooltip-pt-mytalk": "'A paggena 'e chiacchiera {{GENDER:|vòsta}}",
        "tooltip-pt-anontalk": "Chiacchieria ncopp' 'e cagnamiente 'a st'indirizzo IP",
-       "tooltip-pt-preferences": "Preferenze d''e mmeje",
+       "tooltip-pt-preferences": "Preferenze d''e {{GENDER:|vòste}}",
        "tooltip-pt-watchlist": "'A lista d' 'e paggene ca state a cuntrullà",
-       "tooltip-pt-mycontris": "Elenco dde tuje contributte",
+       "tooltip-pt-mycontris": "N'elenco 'e cuntribbute {{GENDER:|vòste}}",
        "tooltip-pt-anoncontribs": "N'elenco 'e cagnamiente fatte 'a st'indirizzo IP",
        "tooltip-pt-login": "A reggistrazione è cunsigliata",
        "tooltip-pt-logout": "Jésce (logout)",
        "tooltip-t-recentchangeslinked": "Urdeme cagnamiénte dde paggene ca cullegano a chesta",
        "tooltip-feed-rss": "RSS feed pe sta pàggena",
        "tooltip-feed-atom": "Atom feed pe sta pàggena",
-       "tooltip-t-contributions": "Lista dde contributte 'e chisto utente",
-       "tooltip-t-emailuser": "Manna 'nu email a chisto utente",
+       "tooltip-t-contributions": "Lista dde contributte fatte 'a {{GENDER:$1|chist'utente}}",
+       "tooltip-t-emailuser": "Manna 'nu email a {{GENDER:$1|chist'utente}}",
        "tooltip-t-info": "Cchiù nfurmaziune ncopp'a sta paggena",
        "tooltip-t-upload": "Carreca file",
        "tooltip-t-specialpages": "Lista 'e tutte e paggene speciale",
        "pageinfo-category-files": "Nummero 'e file",
        "markaspatrolleddiff": "Nzègna comme cuntrullata",
        "markaspatrolledtext": "Nzegna sta paggena comme cuntrullata",
+       "markaspatrolledtext-file": "Nzegna stu file comme verificato",
        "markedaspatrolled": "Nzegnata comme cuntrullata",
        "markedaspatrolledtext": "'A verziona scigliuta 'e [[:$1]] è stata nzegnata comme cuntrullata.",
        "rcpatroldisabled": "Funzione cuntrollo 'e ll'urdeme cagnamiente stutata",
        "newimages-legend": "Filtro",
        "newimages-label": "Nomme d' 'o file (o nu piezz' 'e chesto):",
        "newimages-showbots": "Mmusta cárreche 'e robbot",
+       "newimages-hidepatrolled": "Annascunne 'e carreche verificate",
        "noimages": "Nun nc'è nind' 'a veré.",
        "ilsubmit": "Truova",
        "bydate": "pe' data",
        "scarytranscludefailed-httpstatus": "[L'analisi d' 'o template s'è scassato pe' $1: HTTP $2]",
        "scarytranscludetoolong": "[URL è troppo luonga]",
        "deletedwhileediting": "Attenziòne: quaccherùno have scancellàto chesta pàggena doppo ca tu accuminciàste â scrìvere!",
-       "confirmrecreate": "L'utente [[User:$1|$1]] ([[User talk:$1|Chiacchiera]]) ha scancellato sta paggena aroppo ca l'avite accumminciato a cagnà, cu stu mutivo:\n: <em>$2</em>\nPe' piacere cunfermate ca vulite overamente crià sta paggena n'ata vota.",
-       "confirmrecreate-noreason": "L'utente [[User:$1|$1]] ([[User talk:$1|Chiacchiera]]) ha scancellato sta paggena aroppo ca l'avite accumminciato a cagnà. Pe' piacere cunfermate ca vulite overamente crià sta paggena n'ata vota.",
+       "confirmrecreate": "L'utente [[User:$1|$1]] ([[User talk:$1|Chiacchiera]]) {{GENDER:$1|ha scancellato}} sta paggena aroppo ca l'avite accumminciato a cagnà, cu stu mutivo:\n: <em>$2</em>\nPe' piacere cunfermate ca vulite overamente crià sta paggena n'ata vota.",
+       "confirmrecreate-noreason": "L'utente [[User:$1|$1]] ([[User talk:$1|Chiacchiera]]) {{GENDER:$1|ha scancellato}} sta paggena aroppo ca l'avite accumminciato a cagnà. Pe' piacere cunfermate ca vulite overamente crià sta paggena n'ata vota.",
        "recreate": "Crìa n'ata vota",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Pulezza 'a cache 'e sta paggena?",
        "watchlistedit-clear-done": "L'elenco 'e paggene cuntrullate vuosto è stat'abbacantato.",
        "watchlistedit-clear-removed": "{{PLURAL:$1|nu titolo è stato luvato|$1 titule so' state luvate}}:",
        "watchlistedit-too-many": "Ce stanno troppe paggene 'a veré ccà.",
-       "watchlisttools-clear": "Abbacanta l'elenco 'e paggene cuntrullate",
+       "watchlisttools-clear": "Sbacanta l'elenco 'e paggene cuntrullate",
        "watchlisttools-view": "Vide 'e cagnamiente mpurtante",
        "watchlisttools-edit": "Vide e cagna l'elenco 'e paggene cuntrullate",
        "watchlisttools-raw": "Cagna l'elenco 'e paggene cuntrullate ncruro",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|chiacchiere]])",
+       "timezone-local": "Lucale",
        "duplicate-defaultsort": "<strong>Attenziò:</strong> A chiave d'arricetto \"$2\" se miette ncuollo a nu valore 'e primma \"$1\".",
        "duplicate-displaytitle": "<strong>Attenziò:</strong> A chiave d'arricetto \"$2\" se scagna p' 'o valore 'e primma \"$1\".",
        "invalid-indicator-name": "<strong>Errore:</strong> attribbuto <code>name</code> 'e ll'innecature d' 'o stato d' 'a paggena nu può rummanè abbacante.",
        "version-libraries-license": "Licienza",
        "version-libraries-description": "Descrizzione",
        "version-libraries-authors": "Auture",
-       "redirect": "Rediretto 'a nu file, n'utente, na paggena o n'ID 'e na verziona",
+       "redirect": "Rediretto 'a nu file, n'utente, na paggena, na verziona o nu riggistro ID",
        "redirect-legend": "Rediretto ca spuntasse a nu file o na paggena",
-       "redirect-summary": "Sta pàggena speciale redireziona a nu file (dato 'o nomme d' 'o file), na pàggene (dato n'ID 'e verziona), o 'na pàggene utente (dato n'ID nummereca 'e l'utende). Ause: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], o [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Sta pàggena speciale redireziona a nu file (dato 'o nomme d' 'o file), na pàggene (dato n'ID 'e verziona), o 'na pàggene utente (dato n'ID nummereca 'e l'utende), o na trasuta a 'o riggistro (dato 'o riggistro ID). Ause: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/revision/328429]], o [[{{#Special:Redirect}}/user/101]], o [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Vaje",
        "redirect-lookup": "Ascìa:",
        "redirect-value": "Valore:",
        "redirect-page": "ID d' 'a paggena",
        "redirect-revision": "Sarva revisione",
        "redirect-file": "Nomme d\"o file",
+       "redirect-logid": "Riggistro ID",
        "redirect-not-exists": "Valore nun accucchiato",
        "fileduplicatesearch": "Ascìa 'e file duprecate",
        "fileduplicatesearch-summary": "Circa pe' file duprecate cu bbase 'o valore hash.",
        "tags-deactivate": "stuta",
        "tags-hitcount": "$1 {{PLURAL:$1|cagnamiento|cagnamiente}}",
        "tags-manage-no-permission": "Nun tenite 'o permesso pe' cagnà 'e tag.",
+       "tags-manage-blocked": "Nun putite cagnà sti tag quanno site bluccato/a.",
        "tags-create-heading": "Crìa nu tag nuovo",
        "tags-create-explanation": "Comme predefinito, 'e tag criate nuove nuove se farranno disponibbele pe ll'ausà ll'utente e re bot",
        "tags-create-tag-name": "Nomme 'e ll'etichetta ('o tag):",
        "tags-deactivate-not-allowed": "Nun se pò stutà 'o tag \"$1\".",
        "tags-deactivate-submit": "Stuta",
        "tags-apply-no-permission": "Nun tenite premmesse pe' putè apprecà tag 'e cagnamiente pe' tramente ca facite cagnamiente.",
+       "tags-apply-blocked": "Nun putite apprecà cagnamiente 'e tag pe' tramente ca facite cagnamiente quanno site bluccato/a.",
        "tags-apply-not-allowed-one": "'O tag \"$1\" nun è premmesso a se ffà manualmente apprecà.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|'O tag ccà abbascio nun è|'E tag ccà abbascio nun songo}} premmesse 'e s'apprecà manualmente: $1",
        "tags-update-no-permission": "Nun tenite premmesse pe' putè azzeccà o luvà tag 'e cagnamiento 'a 'e verziune nnividuale o entrate 'o log.",
+       "tags-update-blocked": "Nun putite azzeccà o luvà tag quanno site bluccato/a.",
        "tags-update-add-not-allowed-one": "'O tag \"$1\" nun è premmesso 'e s'azzeccà 'n manuale.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|'O tag ccà abbascio nun è|'E tag ccà abbascio nu so'}} premmesse 'e s'azzeccà manualmente: $1",
        "tags-update-remove-not-allowed-one": "'O tag \"$1\" nun è permesso d' 'o luvà.",
        "expand_templates_preview": "Anteprimma",
        "expand_templates_preview_fail_html": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro appicciato e se songhe spierze 'e date d' 'a sessiona, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite n'ata vota.</strong>\nSi nun funziona ancora, facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.",
        "expand_templates_preview_fail_html_anon": "<em>Siccomme {{SITENAME}} téne 'o HTML 'ncruro e vuje nun site trasute 'o sito, 'a previsualizzaziona s'è annascunnuta comm'a na prutezione annanz'e uerre 'e JavaScript.</em>\n\n<strong>Si chist'è nu tentativo giustificato 'e previsualizzaziona, pe' piacere facite d'[[Special:UserLogout|ascì]] e trasì n'ata vota.</strong>",
+       "expand_templates_input_missing": "Avita dà minimo nu poco 'e testo scritto.",
        "pagelanguage": "Scigliete 'a lengua d' 'a paggena pe' bbìa e stu strumiento",
        "pagelang-name": "Paggena",
        "pagelang-language": "Lengua",
        "pagelang-use-default": "Aùsa 'a lengua predefinita",
        "pagelang-select-lang": "Selezziona lengua",
+       "pagelang-submit": "Manna",
        "right-pagelang": "Cagnate 'a lengua d' 'a paggena",
        "action-pagelang": "càgna 'a lengua d' 'a paggena",
        "log-name-pagelang": "Càgna 'o riggistro 'e llengue",
        "mediastatistics": "Statistiche d' 'e media",
        "mediastatistics-summary": "Statistiche ncopp' 'e tipe d' 'e file carrecate. Ce truvate azzeccata sulamente 'a verziona cchiù recente d' 'o file. Verziune viecchie o scancellate se so' luvate.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Dimenziona d' 'o file 'e sta seziona: {{PLURAL:$1|$1 byte}}  ($2; $3%).",
+       "mediastatistics-allbytes": "Dimenziona sana 'e file pe' tuttuquante 'e file: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "Tipo 'e MIME",
        "mediastatistics-table-extensions": "Estenziune pussibbele",
        "mediastatistics-table-count": "Nummero 'e file",
        "mediastatistics-header-text": "Testuale",
        "mediastatistics-header-executable": "File eseguetàbbele",
        "mediastatistics-header-archive": "Furmate compresse",
+       "mediastatistics-header-total": "Tuttuquante 'e file",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|virgola finale è stata luvata|virgule finale so' state luvate}} 'a 'o JSON",
        "json-error-unknown": "Ce sta nu probblema c' 'o JSON. Errore: $1",
        "json-error-depth": "'O funno massimo 'e stack è stato appassàto",
        "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."
+       "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",
+       "sessionprovider-nocookies": "'E cookie ponno stà stutate. Vedite si 'e cookie stann'appicciate e accumminciate n'ata vota.",
+       "randomrootpage": "Paggena 'e rareca a ccaso"
 }
index 401db85..7e99e3b 100644 (file)
@@ -46,7 +46,8 @@
                        "Macofe",
                        "Kingu",
                        "Tarjeimo",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "SuperPotato"
                ]
        },
        "tog-underline": "Strek under lenker:",
@@ -81,6 +82,7 @@
        "tog-watchlisthidebots": "Skjul robotendringer fra overvåkningslisten",
        "tog-watchlisthideminor": "Skjul mindre endringer fra overvåkningslisten",
        "tog-watchlisthideliu": "Skjul endringer av innloggede brukere fra overvåkningslisten",
+       "tog-watchlistreloadautomatically": "Oppdater oversiktslisten automatisk når et filter er endret (JavaScript kreves)",
        "tog-watchlisthideanons": "Skjul endringer av anonyme brukere fra overvåkningslisten",
        "tog-watchlisthidepatrolled": "Skjul patruljerte endringer fra overvåkningslisten",
        "tog-watchlisthidecategorization": "Skjul kategorisering av sider",
        "october-date": "$1. oktober",
        "november-date": "$1. november",
        "december-date": "$1. desember",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategorier}}",
        "category_header": "Sider i kategorien «$1»",
        "subcategories": "Underkategorier",
        "morenotlisted": "Denne lista er ufullstendig.",
        "mypage": "Min brukerside",
        "mytalk": "Min diskusjonsside",
-       "anontalk": "Brukerdiskusjon for denne IP-adressen",
+       "anontalk": "Brukerdiskusjon",
        "navigation": "Navigasjon",
        "and": "&#32;og",
        "qbfind": "Finn",
        "databaseerror-query": "Spørring: $1",
        "databaseerror-function": "Funksjon: $1",
        "databaseerror-error": "Feil: $1",
+       "transaction-duration-limit-exceeded": "For å unngå høye replikeringsforsinkelser ble denne transaksjonen avsluttet på grunn av at tiden det tok å lagre ($1) oversteg maksimumsgrensen på $2 {{PLURAL:$2|sekund|sekunder}}.\nHvis du endrer på mange ting på en gang, prøv å dele dette opp i mindre operasjoner.",
        "laggedslavemode": "'''Advarsel:''' Dette kan være en eldre versjon av siden.",
        "readonly": "Databasen er skrivebeskyttet",
        "enterlockreason": "Skriv en begrunnelse for skrivebeskyttelsen, inkludert et estimat for når den blir opphevet",
        "mypreferencesprotected": "Du har ikke tillatelse til å redigere innstillingene dine.",
        "ns-specialprotected": "Spesialsider kan ikke redigeres.",
        "titleprotected": "Denne tittelen har blitt låst for oppretting av [[User:$1|$1]].\nDen angitte grunnen er «''$2''».",
-       "filereadonlyerror": "Kan ikke endre filen «$1» fordi filsamlingen «$2» er skrivebeskyttet.\n\nAdministrators nærmere begrunnelse: «$3».",
+       "filereadonlyerror": "Kan ikke endre filen «$1» fordi filsamlingen «$2» er skrivebeskyttet.\n\nSystemadministrator ga følgende begrunnelse: «$3».",
        "invalidtitle-knownnamespace": "Ugyldig tittel med navnerommet «$2» og teksten «$3»",
        "invalidtitle-unknownnamespace": "Ugyldig tittel med ukjent navneromsnummer $1 og teksten «$2»",
        "exception-nologin": "Ikke innlogget",
        "virus-scanfailed": "skanning mislyktes (kode $1)",
        "virus-unknownscanner": "ukjent antivirusprogram:",
        "logouttext": "'''Du er nå logget ut.'''\n\nVær oppmerksom på at noen sider kan fortsette å dukke opp som om du fortsatt var innlogget, helt til du nullstiller nettleserens mellomlager (cache).",
+       "cannotlogoutnow-title": "Kan ikke logge ut nå",
+       "cannotlogoutnow-text": "Å logge ut er ikke mulig ved bruk av $1.",
        "welcomeuser": "Velkommen $1!",
        "welcomecreation-msg": "Kontoen din har blitt opprettet.\nIkke glem å endre [[Special:Preferences|innstillingene dine]] på {{SITENAME}}.",
        "yourname": "Brukernavn:",
        "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å",
+       "cannotloginnow-text": "Å logge inn er ikke mulig ved bruk av $1.",
        "yourdomainname": "Ditt domene",
        "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.",
        "wrongpasswordempty": "Du oppga ikke noe passord. Prøv igjen.",
        "passwordtooshort": "Passord må ha minst {{PLURAL:$1|ett tegn|$1 tegn}}.",
        "passwordtoolong": "Passord kan ikke overskride {{PLURAL:$1|1 character|$1 characters}}.",
+       "passwordtoopopular": "Nylig valgt passord kan ikke brukes. Vennligst bruk et mer unikt passord.",
        "password-name-match": "Passord og brukernavn kan ikke være det samme.",
        "password-login-forbidden": "Bruken av dette brukernavnet og passordet er forbudt.",
        "mailmypassword": "Tilbakestill passord",
        "resetpass_submit": "Angi passord og logg inn",
        "changepassword-success": "Passordet ditt ble korrekt 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-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-createnew": "Opprett et nytt robotpassord",
+       "botpasswords-editexisting": "Redigere et eksisterende robotpassord",
+       "botpasswords-label-appid": "Robotnavn:",
+       "botpasswords-label-create": "Opprett",
+       "botpasswords-label-update": "Oppdater",
+       "botpasswords-label-cancel": "Avbryt",
+       "botpasswords-label-delete": "Slett",
+       "botpasswords-label-resetpassword": "Tilbakestill passord",
+       "botpasswords-label-grants": "Tilgjengelige tildelinger:",
+       "botpasswords-help-grants": "Hver tildeling gir tilgang til opplistede brukerrettigheter som brukerkontoen allerede har. Se [[Special:ListGrants|tildelingstabellen]] for mer informasjon.",
+       "botpasswords-label-restrictions": "Bruksbegrensninger:",
+       "botpasswords-label-grants-column": "Bevilget",
+       "botpasswords-bad-appid": "Robotnavnet \"$1\" er ikke gyldig.",
+       "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-updated-title": "Robotpassord oppdatert",
+       "botpasswords-updated-body": "Robotpassordet \"$1\" ble oppdatert.",
+       "botpasswords-deleted-title": "Robotpassord slettet",
+       "botpasswords-deleted-body": "Robotpassordet \"$1\" 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-no-info": "Du må være logget inn for å gå til denne siden direkte",
        "resetpass-submit-loggedin": "Endre passord",
        "passwordreset-emailtext-ip": "Noen (sannsynligvis deg fra IP-adressen $1) ba om en tilbakestilling av ditt passord for {{SITENAME}} ($4). {{PLURAL:$3|Den følgende brukerkontoen|De følgende brukerkontoene}} er\ntilknyttet denne e-postadressen:\n\n$2\n\n{{PLURAL:$3|Dette midlertidige passordet|Disse midlertidige passordene}} utløper om {{PLURAL:$5|én dag|$5 dager}}.\nDu bør logge på og velge et nytt passord nå. Dersom noen andre kom med denne\nforespørselen, eller du har kommet på ditt opprinnelige passord, og ikke lenger\nønsker å endre det, kan du ignorere denne meldingen og fortsette å bruke ditt gamle\npassord.",
        "passwordreset-emailtext-user": "Brukeren $1 på {{SITENAME}} ba om en tilbakestilling av passordet ditt for {{SITENAME}}\n($4). {{PLURAL:$3|Den følgende brukerkontoen|De følgende brukerkontoene}} er tilknyttet denne e-postadressen:\n\n$2\n\n{{PLURAL:$3|Dette midlertidige passordet|Disse midlertidige passordene}} utløper om {{én dag|$5 dager}}.\nDu bør logge på og velge et nytt passord nå. Dersom noen andre kom med denne\nforespørselen, eller du har kommet på ditt opprinnelige passord, og ikke lenger\nønsker å endre det, kan du ignorere denne meldingen og fortsette å bruke ditt gamle\npassord.",
        "passwordreset-emailelement": "Brukernavn: \n$1\n\nMidlertidig passord: \n$2",
-       "passwordreset-emailsentemail": "Hvis dette er en registrert epostadresse vil en passordtilbakestillingsepost bli sendt.",
+       "passwordreset-emailsentemail": "Hvis dette er en registrert epostadresse for din konto, vil det bli sendt ut en passordtilbakestillingsepost.",
+       "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",
        "changeemail": "Endre eller fjerne epostadresse",
        "copyrightwarning2": "Vennligst merk at alle bidrag til {{SITENAME}} kan bli redigert, endret eller fjernet av andre bidragsytere.\nOm du ikke vil at dine bidrag skal kunne redigeres fritt, ikke legg det til her.<br />\nDu lover også at du har skrevet dette selv, eller kopiert det fra en ressurs som er i offentlig eie eller en lignende fri ressurs (se $1 for detaljer).\n'''Ikke legg til opphavsrettsbeskyttet materiale uten tillatelse!'''",
        "editpage-cannot-use-custom-model": "Innholdsmodellen for denne siden kan ikke endres.",
        "longpageerror": "'''Feil: Teksten du ønsker å lagre er {{PLURAL:$1|én kilobyte|$1 kilobyte}} stor. Dette er større enn det tillatte maksimum på {{PLURAL:$2|én kilobyte|$2 kilobyte}}.'''\nDen kan ikke lagres.",
-       "readonlywarning": "'''ADVARSEL: Databasen er låst på grunn av vedlikehold,\nså du kan ikke lagre dine endringer akkurat nå. Det kan være en god idé å\nkopiere teksten din til en tekstfil, så du kan lagre den til senere.'''\n\nSystemadministratoren som låste databasen oppga følgende årsak: $1",
+       "readonlywarning": "<strong>ADVARSEL: Databasen er låst på grunn av vedlikehold,\nså du kan ikke lagre dine endringer akkurat nå.</strong>\nDet kan være en god idé å kopiere teksten din til en tekstfil og lagre den til senere.\n\nSystemadministratoren som låste databasen ga følgende begrunnelse: $1",
        "protectedpagewarning": "'''Advarsel: Denne siden har blitt låst slik at kun brukere med administratorrettigheter kan redigere den.'''\nDet siste loggelementet er oppgitt under som referanse:",
        "semiprotectedpagewarning": "'''Merk:''' Denne siden har blitt låst slik at kun registrerte brukere kan endre den.\nDet siste loggelementet er oppgitt under som referanse:",
        "cascadeprotectedwarning": "<strong>Advarsel:</strong> Denne siden har blitt låst slik at kun brukere med administratorrettigheter kan redigere den, fordi den inkluderes på følgende dypbeskyttede {{PLURAL:$1|side|sider}}:",
        "permissionserrors": "Rettighetsfeil",
        "permissionserrorstext": "Du har ikke tillatelse til å utføre dette, av følgende {{PLURAL:$1|grunn|grunner}}:",
        "permissionserrorstext-withaction": "Du har ikke tillatelse til å $2 {{PLURAL:$1|fordi|av følgende grunner}}:",
-       "contentmodelediterror": "Du kan ikke redigere denne revisjonen fordi innholdsmodellen er <code>$1</code>, og den nåværende innholdsmodellen til siden er <code>$2</code>.",
+       "contentmodelediterror": "Du kan ikke redigere denne revisjonen fordi innholdsmodellen er <code>$1</code>, som avviker fra den nåværende innholdsmodellen til siden <code>$2</code>.",
        "recreate-moveddeleted-warn": "Advarsel: Du er i ferd med å opprette en side som tidligere har blitt slettet.'''\n\nDu bør vurdere om det er passende å fortsette å redigere denne siden.\nSlette- og flytteloggen for denne siden gjengis her:",
        "moveddeleted-notice": "Denne siden har blitt slettet.\nSlette- og flytteloggen vises nedenfor.",
        "moveddeleted-notice-recent": "Beklager, denne siden er nylig blitt slettet (i løpet av de siste 24 timer)\nSlette- og flytteloggen for siden er angitt nedenfor for referanse.",
        "badsig": "Ugyldig råsignatur; sjekk HTML-elementer.",
        "badsiglength": "Signaturen er for lang.\nDen kan maks inneholde $1 {{PLURAL:$1|tegn|tegn}}.",
        "yourgender": "Hvordan ønsker du å bli omtalt?",
-       "gender-unknown": "Når du omtales, vil programvaren bruke kjønnsnøytrale ord så ofte som mulig.",
+       "gender-unknown": "La programvaren omtale meg med mest mulig kjønnsnøytrale ord",
        "gender-male": "Han redigerer wikisider",
        "gender-female": "Hun redigerer wikisider",
        "prefs-help-gender": "Det er valgfritt å angi dette.\nProgramvaren bruker verdien for å anvende riktig grammatikalsk kjønn ved henvendelser til deg og i omtale av deg for andre brukere.\nInformasjonen vil være offentlig.",
        "userrights": "Brukerrettighetskontroll",
        "userrights-lookup-user": "Ordne brukergrupper",
        "userrights-user-editname": "Fyll inn et brukernavn:",
-       "editusergroup": "Endre brukergrupper",
+       "editusergroup": "Endre {{GENDER:$1|brukergrupper}}",
        "editinguser": "Endrer brukerrettighetene for {{GENDER:$1|bruker}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Rediger brukergrupper",
-       "saveusergroups": "Lagre brukergrupper",
+       "saveusergroups": "Lagre {{GENDER:$1|brukergrupper}}",
        "userrights-groupsmember": "Medlem av:",
        "userrights-groupsmember-auto": "Implisitt medlem av:",
        "userrights-groups-help": "Du kan endre hvilke grupper denne brukeren er medlem av.\n* En avkrysset boks betyr at brukeren er medlem av gruppen.\n* En uavkrysset boks betyr at brukeren ikke er medlem av gruppen.\n* En * betyr at du ikke kan fjerne gruppemedlemskapet når du har lagt det til, eller vice versa.",
        "right-createpage": "Opprette sider (som ikke er diskusjonssider)",
        "right-createtalk": "Opprette diskusjonssider",
        "right-createaccount": "Opprette nye kontoer",
+       "right-autocreateaccount": "Logg inn automatisk med en ekstern brukerkonto",
        "right-minoredit": "Markere endringer som mindre",
        "right-move": "Flytte sider",
        "right-move-subpages": "Flytte sider med undersider",
        "right-blockemail": "Blokkere brukere fra å sende e-post",
        "right-hideuser": "Blokkere et brukernavn og skjule det fra det offentlige",
        "right-ipblock-exempt": "Kan redigere fra blokkerte IP-adresser",
-       "right-proxyunbannable": "Kan redigere fra blokkerte proxyer",
        "right-unblockself": "Fjerne blokkering av seg selv",
        "right-protect": "Endre beskyttelsesnivåer og redigere beskyttete sider",
        "right-editprotected": "Redigere beskyttede sider som «{{int:protect-level-sysop}}»",
        "right-managechangetags": "Opprette og slette [[Special:Tags|tagger]] fra databasen",
        "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",
+       "grant-generic": "Rettighetspakken «$1»",
+       "grant-group-page-interaction": "Interagere med sider",
+       "grant-group-file-interaction": "Interagere med media",
+       "grant-group-watchlist-interaction": "Interagere med overvåkningslisten din",
+       "grant-group-email": "Sende e-post",
+       "grant-group-high-volume": "Utføre høyvolumaktivitet",
+       "grant-group-customization": "Tilpasninger og innstillinger",
+       "grant-group-administration": "Utføre administrative handlinger",
+       "grant-group-other": "Andre ting",
+       "grant-blockusers": "Blokkere og avblokkere brukere",
+       "grant-createaccount": "Opprette kontoer",
+       "grant-createeditmovepage": "Opprette, redigere eller flytte sider",
+       "grant-delete": "Slette sider, revisjoner og logginnlegg",
+       "grant-editinterface": "Redigere i MediaWiki-navnerommet og CSS/JavaScript i brukernavnerommet",
+       "grant-editmycssjs": "Redigere din bruker-CSS/JavaScript",
+       "grant-editmyoptions": "Rediger dine brukerinnstillinger",
+       "grant-editmywatchlist": "Redigere overvåkningslisten din",
+       "grant-editpage": "Redigere eksisterende sider",
+       "grant-editprotected": "Redigere beskyttede sider",
+       "grant-highvolume": "Høyvolumredigering",
+       "grant-oversight": "Skjule brukere og undertrykke revisjoner",
+       "grant-patrol": "Patruljere sideendringer",
+       "grant-protect": "Beskytte og avbeskytte sider",
+       "grant-rollback": "Tilbakestille sideendringer",
+       "grant-sendemail": "Sende e-post til andre brukere",
+       "grant-uploadeditmovefile": "Laste opp, erstatte, og flytte filer",
+       "grant-uploadfile": "Laste opp nye filer",
+       "grant-viewdeleted": "Vise slettede filer og sider",
+       "grant-viewmywatchlist": "Vise overvåkningslisten din",
        "newuserlogpage": "Brukeropprettelseslogg",
        "newuserlogpagetext": "Dette er en logg over brukeropprettelser.",
        "rightslog": "Brukerrettighetslogg",
        "recentchanges-legend-heading": "'''Tegnforklaring:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (se også [[Special:NewPages|liste over nye sider]])",
        "recentchanges-legend-plusminus": "«(±123)»",
+       "recentchanges-submit": "Vis",
        "rcnotefrom": "Nedenfor er vist {{PLURAL:$5|endringen|endringene}} som er gjort siden <strong>$3, $4</strong> (frem til <strong>$1</strong>).",
        "rclistfrom": "Vis nye endringer fra og med $3 $2",
        "rcshowhideminor": "$1 mindre endringer",
        "upload-options": "Opplastingsvalg",
        "watchthisupload": "Overvåk denne filen",
        "filewasdeleted": "Ei fil ved dette navnet har blitt lastet opp tidligere, og så slettet. Sjekk $1 før du forsøker å laste det opp igjen.",
+       "filename-thumb-name": "Dette ser ut som tittelen til et miniatyrbilde (thumbnail). Miniatyrbilder skal ikke lastes opp igjen til den samme wikien. Hvis det ikke er et miniatyrbilde må du endre filnavnet til noe mer meningsfullt og fjerne miniatyrbilde-prefikset.",
        "filename-bad-prefix": "Navnet på filen du laster opp begynner med '''«$1»''', hvilket er et ikke-beskrivende navn som vanligvis brukes automatisk av digitalkameraer. Vennligst bruk et mer beskrivende navn på filen.",
        "filename-prefix-blacklist": " #<!-- leave this line exactly as it is --> <pre>\n# Syntaksen er som følger:\n#   * Alt fra tegnet «#» til slutten av linja er en kommentar\n#   * Alle linjer som ikke er blanke er et prefiks som vanligvis brukes automatisk av digitale kameraer\nCIMG # Casio\nDSC_ # Nikon\nDSCF # Fuji\nDSCN # Nikon\nDUW # noen mobiltelefontyper\nIMG # generisk\nJD # Jenoptik\nMGP # Pentax\nPICT # div.\n #</pre> <!-- leave this line exactly as it is -->",
        "upload-success-subj": "Opplastingen er gjennomført",
        "foreign-structured-upload-form-label-own-work": "Dette er mitt eget verk",
        "foreign-structured-upload-form-label-infoform-categories": "Kategorier",
        "foreign-structured-upload-form-label-infoform-date": "Dato",
+       "foreign-structured-upload-form-label-own-work-message-local": "Jeg bekrefter at jeg ved å laste opp denne filen følger bruksvilkårene og lisensieringspolitikken på {{SITENAME}}.",
+       "foreign-structured-upload-form-label-not-own-work-message-local": "Hvis filen ikke lar seg laste opp under {{SITENAME}}s politikk må du lukke denne dialogboksen og prøve en annen metode.",
+       "foreign-structured-upload-form-label-not-own-work-local-local": "Du kan eventuelt forsøke [[Special:Upload|den ordinære opplastingssiden]].",
+       "foreign-structured-upload-form-label-own-work-message-default": "Jeg forstår at jeg laster opp denne filen til et delt arkiv. Jeg bekrefter at dette gjøres i tråd med bruksvilkårene og lisensieringspolitikken der.",
+       "foreign-structured-upload-form-label-not-own-work-message-default": "Hvis filen ikke lar seg laste opp under arkivets politikk må du lukke denne dialogboksen og prøve en annen metode.",
+       "foreign-structured-upload-form-label-not-own-work-local-default": "Du kan eventuelt forsøke [[Special:Upload|den ordinære opplastingssiden]] på {{SITENAME}} hvis filen kan lastes opp under politikken som gjelder der.",
+       "foreign-structured-upload-form-label-own-work-message-shared": "Jeg bekrefter at jeg har opphavsretten til denne filen, samtykker til å ugjenkallelig slippe filen til Wikimedia Commons under lisensen [https://creativecommons.org/licenses/by-sa/4.0/deed.no Creative Commons Navngivelse-DelPåSammeVilkår 4.0], og samtykker til [https://wikimediafoundation.org/wiki/Terms_of_Use bruksvilkårene].",
+       "foreign-structured-upload-form-label-not-own-work-message-shared": "Hvis du ikke sitter på opphavsretten til filen, eller ønsker å slippe den under en annen lisens, prøv [https://commons.wikimedia.org/wiki/Special:UploadWizard Opplastingsveiviseren på Commons].",
+       "foreign-structured-upload-form-label-not-own-work-local-shared": "Du kan eventuelt forsøke [[Special:Upload|den ordinære opplastingssiden på {{SITENAME}}]] hvis filen kan lastes opp under politikken som gjelder der.",
+       "foreign-structured-upload-form-2-label-intro": "Takk for at du donerer et bilde til bruk på {{SITENAME}}. Du kan kun fortsette hvis det oppfyller følgende krav:",
+       "foreign-structured-upload-form-2-label-ownwork": "Det må være <strong>ditt eget verk</strong>, ikke tatt fra internett",
+       "foreign-structured-upload-form-2-label-noderiv": "Det må <strong>ikke inneholde eller være sterkt inspirert av andres verk</strong>",
+       "foreign-structured-upload-form-2-label-useful": "Det bør være <strong>illustrerende og nyttig</strong>",
+       "foreign-structured-upload-form-2-label-ccbysa": "Det må være <strong>OK å publisere for evig tid</strong> på internett under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0]-lisensen",
+       "foreign-structured-upload-form-2-label-alternative": "Hvis ikke alle kriteriene ovenfor er oppfylt, kan du i stedet laste opp filen med [https://commons.wikimedia.org/wiki/Special:UploadWizard Opplastingsveiviseren på Commons], gitt at filen er tilgjengelig under en fri lisens.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Ved å laste opp filen går du god for at du har opphavsretten til den, du samtykker til å ugjenkallelig slippe filen til Wikimedia Commons under lisensen Creative Commons Navngivelse-DelPåSammeVilkår 4.0, og du samtykker til [https://wikimediafoundation.org/wiki/Terms_of_Use bruksvilkårene].",
+       "foreign-structured-upload-form-3-label-question-website": "Er bildet hentet fra nettside eller fra bildesøk?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Har du laget (fotografert, skisset, tegnet, …) bildet selv?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Inneholder det, eller er det sterkt inspirert av, arbeid som eies av noen andre, som f.eks. en logo?",
+       "foreign-structured-upload-form-3-label-yes": "Ja",
+       "foreign-structured-upload-form-3-label-no": "Nei",
+       "foreign-structured-upload-form-3-label-alternative": "Uheldigvis kan ikke filen lastes opp med dette verktøyet i dette tilfellet. Du kan fremdeles prøve å laste opp filen med [https://commons.wikimedia.org/wiki/Special:UploadWizard opplastingsveiviseren på Commons], så lenge filen er tilgjengelig under en fri lisens.",
+       "foreign-structured-upload-form-4-label-good": "Med dette verktøyet kan du laste opp illustrasjoner du selv har laget selv og fotografier du selv har tatt, som ikke inneholder andres arbeid.",
+       "foreign-structured-upload-form-4-label-bad": "Du kan ikke laste opp bilder du har funnet ved søk på internett eller lastet ned fra andre nettsider.",
        "backend-fail-stream": "Kunne ikke strømme filen $1.",
        "backend-fail-backup": "Kunne ikke sikkerhetskopiere filen $1.",
        "backend-fail-notexists": "Filen $1 finnes ikke.",
        "unusedimages": "Ubrukte filer",
        "wantedcategories": "Ønskede kategorier",
        "wantedpages": "Etterspurte sider",
-       "wantedpages-summary": "Liste av ikke-eksisterende sider med flest lenker mot dem, bortsett fra omdirigeringer. Den siste gruppen finnes gjennom [[{{#special:BrokenRedirects}}]].",
+       "wantedpages-summary": "Liste av ikke-eksisterende sider med flest innkommende lenker, unntatt sider som kun er lenket fra omdirigeringer. For en liste over ikke-eksisterende sider som er lenket fra omdirigeringer, se [[{{#special:BrokenRedirects}}|listen over ødelagte omdirigeringer]].",
        "wantedpages-badtitle": "Ugyldig tittel i resultatene: $1",
        "wantedfiles": "Ønskede filer",
        "wantedfiletext-cat": "Følgende filer refereres, men eksisterer ikke. Filer fra fremmede samlinger kan listes selv om de ikke finnes. Alle slik falske treff vil <del>strykes</del>. I tillegg er sider som har innebygde, ikke-eksisterende filer listet opp i [[:$1]].",
        "mostrevisions": "Artikler med flest revisjoner",
        "prefixindex": "Alle sider med prefiks",
        "prefixindex-namespace": "All sider med prefiks ($1 navnerom)",
+       "prefixindex-submit": "Vis",
        "prefixindex-strip": "Fjern prefiks fra listen",
        "shortpages": "Korte sider",
        "longpages": "Lange sider",
        "protectedpages-performer": "Beskytter bruker",
        "protectedpages-params": "Beskyttelsesparametre",
        "protectedpages-reason": "Årsak",
+       "protectedpages-submit": "Vis sider",
        "protectedpages-unknown-timestamp": "Ukjent",
        "protectedpages-unknown-performer": "Ukjent bruker",
        "protectedtitles": "Beskyttede titler",
        "protectedtitles-summary": "Denne siden viser en liste av eksisterende sider som for tiden er beskyttet mot opprettelse. For å se en liste av sider som er beskyttet, se [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Ingen titler beskyttes med disse parameterne for øyeblikket.",
+       "protectedtitles-submit": "Vis titler",
        "listusers": "Brukerliste",
        "listusers-editsonly": "Vis bare brukere med redigeringer",
        "listusers-creationsort": "Sorter etter opprettelsesdato",
        "usereditcount": "{{PLURAL:$1|én redigering|$1 redigeringer}}",
        "usercreated": "{{GENDER:$3|Opprettet}} $2 $1",
        "newpages": "Nye sider",
+       "newpages-submit": "Vis",
        "newpages-username": "Brukernavn:",
        "ancientpages": "Eldste sider",
        "move": "Flytt",
        "specialloguserlabel": "Utøver:",
        "speciallogtitlelabel": "Mål (tittel eller {{ns:user}}:brukernavn for brukeren):",
        "log": "Logger",
+       "logeventslist-submit": "Vis",
        "all-logs-page": "Alle offentlige logger",
        "alllogstext": "Kombinert visning av alle loggene på {{SITENAME}}.\nDu kan minske antallet resultater ved å velge loggtype, brukernavn eller den siden som er påvirket (husk å skille mellom store og små bokstaver).",
        "logempty": "Ingen elementer i loggen.",
        "cachedspecial-viewing-cached-ts": "Du ser på en mellomlagret versjon av denne siden, som kan være ikke helt oppdatert",
        "cachedspecial-refresh-now": "Vis siste.",
        "categories": "Kategorier",
+       "categories-submit": "Vis",
        "categoriespagetext": "Følgende {{PLURAL:$1|kategori|kategorier}} inneholder sider eller media.\n[[Special:UnusedCategories|Ubrukte kategorier]] vises ikke her.\nSe også [[Special:WantedCategories|ønskede kategorier]].",
        "categoriesfrom": "Vis kategorier fra og med:",
        "special-categories-sort-count": "soter etter antall",
        "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-rights": "Tildeling",
        "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.",
        "trackingcategories-msg": "Sporingskategori",
        "wlshowlast": "Vis siste $1 timer $2 dager",
        "watchlistall2": "alle",
        "watchlist-hide": "Skjul",
-       "wlshowtime": "Vis siste:",
+       "watchlist-submit": "Vis",
+       "wlshowtime": "Tidsperiode som skal vises:",
        "wlshowhideminor": "mindre redigeringer",
        "wlshowhidebots": "boter",
        "wlshowhideliu": "registrerte brukere",
        "wlshowhideanons": "anonyme brukere",
        "wlshowhidepatr": "patruljerte redigeringer",
        "wlshowhidemine": "mine redigeringer",
+       "wlshowhidecategorization": "sidekategorisering",
        "watchlist-options": "Alternativ for overvåkningslisten",
        "watching": "Overvåker…",
        "unwatching": "Fjerner fra overvåkningsliste…",
        "delete-confirm": "Slett «$1»",
        "delete-legend": "Slett",
        "historywarning": "<strong>Advarsel:</strong> Siden du er i ferd med å slette har en historikk med $1 {{PLURAL:$1|revisjon|revisjoner}}:",
+       "historyaction-submit": "Vis",
        "confirmdeletetext": "Du holder på å slette en side sammen med historikken.\nBekreft at du virkelig vil slette denne siden, at du forstår konsekvensene og at du gjør det i samsvar med [[{{MediaWiki:Policy-url}}|retningslinjene]].",
        "actioncomplete": "Gjennomført",
        "actionfailed": "Handling mislyktes",
        "cantrollback": "Kan ikke fjerne redigering; den siste brukeren er den eneste forfatteren.",
        "alreadyrolled": "Kan ikke fjerne den siste redigeringen på [[$1]] av [[User:$2|$2]] ([[User talk:$2|diskusjon]]{{int:pipe-separator}}[[Special:Contributions/$2|{{int:contribslink}}]]); en annen har allerede redigert siden eller fjernet redigeringen.\n\nDen siste redigeringen ble foretatt av [[User:$3|$3]] ([[User talk:$3|diskusjon]]{{int:pipe-separator}}[[Special:Contributions/$3|{{int:contribslink}}]]).",
        "editcomment": "Redigeringskommentaren var: «''$1''»",
-       "revertpage": "Tilbakestilte endring av [[Brukerdiskusjon:$2|$2]] ([[Spesial:Contributions/$2|bidrag]]) til siste versjon av $1",
+       "revertpage": "Tilbakestilte endringer av [[Special:Contributions/$2|$2]] ([[User talk:$2|brukerdiskusjon]]) til siste versjon av [[User:$1|$1]]",
        "revertpage-nouser": "Tilbakestilt endringer av skjult bruker til siste versjon av\n{{GENDER:$1|[[User:$1|$1]]}}",
        "rollback-success": "Tilbakestilte endringer av $1; endret til siste versjon av $2.",
        "sessionfailure-title": "Sesjonsfeil",
        "whatlinkshere-hidelinks": "$1 lenker",
        "whatlinkshere-hideimages": "$1 fillenker",
        "whatlinkshere-filters": "Filtre",
+       "whatlinkshere-submit": "Hent liste",
        "autoblockid": "Autoblokker #$1",
        "block": "Blokker bruker",
        "unblock": "Fjern blokkering av bruker",
        "tooltip-pt-preferences": "Dine innstillinger",
        "tooltip-pt-watchlist": "Liste over sider du overvåker for endringer.",
        "tooltip-pt-mycontris": "Liste over dine bidrag",
+       "tooltip-pt-anoncontribs": "En liste over redigeringer gjort fra denne IP-adressen",
        "tooltip-pt-login": "Du oppfordres til å logge inn, men det er ikke obligatorisk",
        "tooltip-pt-logout": "Logg ut",
        "tooltip-pt-createaccount": "Du oppfordres til å opprette en konto og logge inn, men det er ikke obligatorisk.",
        "svg-long-error": "Ugyldig SVG-fil: $1",
        "show-big-image": "Opprinnelig fil",
        "show-big-image-preview": "Størrelse på denne forhåndsvisningen: $1.",
+       "show-big-image-preview-differ": "Størrelse for denne $3-forhåndsvisningen av denne $2-filen: $1",
        "show-big-image-other": "{{PLURAL:$2|Annen oppløsning|Andre oppløsninger}}: $1.",
        "show-big-image-size": "$1 × $2 piksler",
        "file-info-gif-looped": "gjentas",
        "exif-compression-4": "CCITT Gruppe 4 faks-koding",
        "exif-copyrighted-true": "Opphavsrettsbeskyttet",
        "exif-copyrighted-false": "Opphavsrettstatus er ikke angitt",
+       "exif-photometricinterpretation-1": "Sort og hvitt (Sort er 0)",
        "exif-unknowndate": "Ukjent dato",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Snudd horisontalt",
        "logentry-suppress-block": "$1 {{GENDER:$2|blokkerte}} {{GENDER:$4|$3}} med en utløpstid på $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|endret}} blokkeringsinnstillingen for {{GENDER:$4|$3}} med en utløpstid på $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|importert}} $3 gjennom filopplastning",
+       "logentry-import-upload-details": "$1 {{GENDER:$2|importerte}} $3 gjennom filopplasting ($4 {{PLURAL:$4|revisjon|revisjoner}})",
        "logentry-import-interwiki": "$1 {{GENDER:$2|importerte}} $3 fra en annen wiki",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2|importerte}} $3 fra $5 ($4 {{PLURAL:$4|revisjon|revisjoner}})",
        "logentry-merge-merge": "$1 {{GENDER:$2|slo sammen}} $3 i $4 (versjonene t.o.m. $5)",
        "logentry-move-move": "$1 {{GENDER:$2|flyttet}} siden $3 til $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|flyttet}} siden $3 til $4 uten å etterlate en omdirigering",
        "pagelang-language": "Språk",
        "pagelang-use-default": "Bruk standardspråk",
        "pagelang-select-lang": "Velg språk",
+       "pagelang-submit": "Lagre",
        "right-pagelang": "Endre sidespråk",
        "action-pagelang": "endre sidespråket",
        "log-name-pagelang": "Endre språklogg",
        "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-table-mimetype": "MIME-type",
        "mediastatistics-table-extensions": "Mulige filtyper",
        "mediastatistics-table-count": "Antall filer",
        "mediastatistics-header-text": "Tekstlig",
        "mediastatistics-header-executable": "Kjørbare filer",
        "mediastatistics-header-archive": "Komprimerte formater",
+       "mediastatistics-header-total": "Alle filer",
        "json-warn-trailing-comma": "$1 etterfølgende {{PLURAL:$1|komma|kommaer}} ble fjernet fra JSON",
        "json-error-unknown": "Det var et problem med JSON. Feil: $1",
        "json-error-depth": "Maksimal stakkdybde har blitt overskredet",
        "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."
+       "api-error-blacklisted": "Vennligst velg en annen beskrivende tittel.",
+       "randomrootpage": "Tilfeldig rotside"
 }
index 4607026..c4e4918 100644 (file)
@@ -20,7 +20,8 @@
                        "बिप्लब आनन्द",
                        "Nirjal stha",
                        "राम प्रसाद जोशी",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "जनक राज भट्ट"
                ]
        },
        "tog-underline": "रेखाङ्कित लिङ्क:",
        "morenotlisted": "यो सूची पूर्ण हैन।",
        "mypage": "पृष्ठ",
        "mytalk": "वार्ता",
-       "anontalk": "यस IP à¤\95à¥\8b à¤µà¤¾à¤°à¥\87मा à¤µà¤¾à¤°à¥\8dतालाप à¤\97रà¥\8dनà¥\81हà¥\8bसà¥\8d",
+       "anontalk": "वारà¥\8dता",
        "navigation": "अन्वेषण",
        "and": "&#32;र",
        "qbfind": "पत्ता लगाउनु",
        "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िà¤\8fà¤\95à¥\8bà¤\9b,, à¤\9cसलाà¤\88 à¤ªà¤\9bिबाà¤\9f à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\97रिनà¥\87à¤\9b। \nपà¥\8dरबनà¥\8dधà¤\95 à¤\9cसलà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\97रà¥\87à¤\95ाà¤\9bनà¥\8d, à¤¯à¥\8b à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¤¿à¤\8fà¤\95ाछन्: $1",
+       "readonlytext": "समà¥\8dभवतà¤\83 à¤¨à¤¿à¤¯à¤®à¤¿à¤¤ à¤¡à¥\87à¤\9fाबà¥\87स à¤°à¥\87à¤\96दà¥\87à¤\96à¤\95à¥\8b à¤\95ारण à¤\85हिलà¥\87लाà¤\88 à¤¨à¤¯à¤¾à¤\81 à¤¡à¥\87à¤\9fाबà¥\87स à¤ªà¥\8dरविषà¥\8dà¤\9fà¥\80 à¤° à¤\85नà¥\8dय à¤¸à¤\82शà¥\8bधनहरà¥\82 à¤¬à¤¨à¥\8dद à¤°à¤¾à¤\96िà¤\8fà¤\95à¥\8b à¤\9b, à¤\9cसलाà¤\88 à¤ªà¤\9bिबाà¤\9f à¤¸à¤¾à¤®à¤¾à¤¨à¥\8dय à¤\97रिनà¥\87à¤\9b। \nपà¥\8dरबनà¥\8dधà¤\95 à¤\9cसलà¥\87 à¤¯à¥\8b à¤¬à¤¨à¥\8dद à¤\97रà¥\87à¤\95ा à¤\9bनà¥\8d, à¤¯à¥\8b à¤¸à¥\8dपषà¥\8dà¤\9fà¥\80à¤\95रण à¤¦à¤¿à¤\8fà¤\95ा छन्: $1",
        "missing-article": "नाम \"$1\" $2 भएको भेटिनु पर्ने पृष्ठको पाठ डेटाबेसले  भेटाइएन, \n\nयस्तो प्राय: मिति नाघिसकेको भिन्न वा इतिहास वा कुनै मेटिसकेको पानाको लिंक पहिल्याउनाले हुन्छ ।\n\nयदि यस्तो भएको होइन भने सफ्टवेयरको त्रुटि पनि हुनसक्छ ।\nकृपया यसको url खुलाइ [[Special:ListUsers/sysop|प्रबन्धक]]लाई उजुरी गर्नुहोस्",
        "missingarticle-rev": "(संशोधन #: $1)",
        "missingarticle-diff": "(परि: $1, $2)",
        "right-blockemail": "एक प्रयोगकर्तालाई इमेल पठाउनबाट रोक्ने",
        "right-hideuser": "एक प्रयोगकर्ता नाम रोक्ने, सार्वजनिकहुनबाट लुकाउने",
        "right-ipblock-exempt": "IP रोक,स्वत: रोक तथा स्तर रोक कटेर जाने",
-       "right-proxyunbannable": "प्रोक्सिको स्वत: रोक कटेर जाने",
        "right-unblockself": "आफैंलाई खुल्ला गर्नुहोस्",
        "right-protect": "सुरक्षास्तरहरू परिवर्तन गर्ने र क्यासकेड-सुरक्षित पृष्ठहरूलाई सम्पादन गर्ने",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" को हैसियतले सुरक्षित पृष्ठहरू सम्पादन गर्ने",
        "right-managechangetags": "डाटाबेसबाट [[Special:Tags|tags]] बनाउने र हटाउने",
        "right-applychangetags": "एकको परिवर्तन सहित [[Special:Tags|tags]] लागु गर्ने",
        "right-changetags": "जोड्ने र हटाउने स्वतन्त्र [[Special:Tags|ट्याग]] व्यक्तिगत अवतरणहरू र लग इन्ट्रीहरूमा",
+       "grant-createeditmovepage": "पृष्ठहरूमा परिवर्तन गर्नुहोस्",
+       "grant-editmycssjs": "तपाईंको प्रयोगकर्ता CSS/JavaScript सम्पादन गर्नुहोस्",
+       "grant-editmyoptions": "तपाईंको प्रयोगकर्ता अभिरूचीहरूलाई सम्पादन गर्नुहोस्",
+       "grant-viewdeleted": "मेटाइएका फाइल तथा पृष्ठहरू हेर्ने",
        "newuserlogpage": "प्रयोगकर्ता श्रृजना लग",
        "newuserlogpagetext": "यो प्रयोगकर्ता सिर्जनाको लग हो ।",
        "rightslog": "प्रयोगकर्ता अधिकार लग",
        "log-title-wildcard": "पाठबाट सुरुहुने शीर्षकहरु खोज्नुहोस्",
        "showhideselectedlogentries": "चयन गरिएका लग प्रविष्टिहरूको दृश्यता परिवर्तन गर्ने",
        "log-edit-tags": "चयन गरिएको लग प्रविष्टिहरूको ट्यागहरू सम्पादन",
+       "checkbox-select": "छनौट:$1",
+       "checkbox-all": "सबै",
+       "checkbox-none": "कोहीपनि हैन",
+       "checkbox-invert": "ठाडो",
        "allpages": "सबै पृष्ठहरू",
        "nextpage": "अर्को पृष्ठ ($1)",
        "prevpage": "पहिलो पृष्ठ ($1)",
        "watchlist-submit": "देखाउनुहोस्",
        "wlshowhideminor": "सामान्य सम्पादनहरू",
        "wlshowhidebots": "बोटहरू",
+       "wlshowhideliu": "दर्ता गरिएका प्रयोगकर्ताहरू",
+       "wlshowhideanons": "अज्ञात प्रयोगकर्ताहरू",
+       "wlshowhidepatr": "गस्ती गरिएका सम्पादनहरू",
+       "wlshowhidemine": "मेरा सम्पादनहरू",
+       "wlshowhidecategorization": "पृष्ठ श्रेणीकरण",
        "watchlist-options": "निगरानि सूची विकल्प",
        "watching": "निगरानी गर्दै...",
        "unwatching": "निगरानीबाट हटाउँदै...",
        "unblock": "प्रयोगकर्ता माथिको प्रतिबन्ध हटाउने",
        "blockip": "{{GENDER:$1|प्रयोगकर्ता}}लाई निषेध गर्ने",
        "blockip-legend": "प्रयोगकर्ता रोक्नुहोस",
-       "blockiptext": "विशेष IP ठेगाना अथवा प्रयोगकर्तालाई रोक लगाउन निम्न प्रपत्र (form) प्रयोग गर्नुहोस्।\nयसो गर्नुको कारण [[{{MediaWiki:Policy-url}}|नीति]] अनुरुप विकिमा गरिने बर्बरताका कार्य रोक्नु मात्र हो।\nविशेष कारण देखाउँदै तलको प्रपत्र भर्नुहोस्  (उदाहरण, बर्बरताको कार्य गरिएको पृष्ठ दर्शाउँदै)",
+       "blockiptext": "विशेष IP ठेगाना अथवा प्रयोगकर्तालाई रोक लगाउन निम्न प्रपत्र (form) प्रयोग गर्नुहोस्।\nयसो गर्नुको कारण [[{{MediaWiki:Policy-url}}|नीति]] अनुरुप विकिमा गरिने बर्बरताका कार्य रोक्नु मात्र हो।\nविशेष कारण देखाउँदै तलको प्रपत्र भर्नुहोस्(उदाहरण, बर्बरताको कार्य गरिएको पृष्ठ दर्शाउँदै)",
        "ipaddressorusername": "आइपी ठेगाना वा प्रयोगकर्ता नाम :",
        "ipbexpiry": "सकिने:",
        "ipbreason": "कारण:",
        "hebrew-calendar-m12-gen": "एलल्",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|वार्ता]])",
        "timezone-utc": "युटिसी(UTC)",
+       "timezone-local": "स्थानीय",
        "duplicate-defaultsort": "'''चेतावनी:''' पूर्व निर्धारित छोटकरी \"$2\" ले पुरानो पूर्वनिर्धारित छोटकरी\"$1\"लाई विस्थापन गरेको छ ।",
        "duplicate-displaytitle": "<strong>चेतावनी:</strong> शीर्षक देखाउने \"$2\" पूर्व देखाइएको शीर्षक \"$1\" मा ओभररेड गरिंदै छ।",
        "invalid-indicator-name": "<strong>त्रुटि:</strong> पृष्ठ स्थिति सङ्केतक नाम गुण रित्तो हुनुहुँदैन।",
        "pagelang-language": "भाषा",
        "pagelang-use-default": "पूर्वनिर्धारित भाषा प्रयोग गर्ने",
        "pagelang-select-lang": "भाषा छान्ने",
+       "pagelang-submit": "बुझाउने",
        "right-pagelang": "पृष्ठको भाषा परिवर्तन गर्ने",
        "action-pagelang": "यस पृष्ठको भाषा परिवर्तन गर्ने",
        "log-name-pagelang": "लगको भाषा परिवर्तन गर्ने",
        "mediastatistics-header-text": "पाठ",
        "mediastatistics-header-executable": "कार्यान्वयन गर्न मिल्नेहरू",
        "mediastatistics-header-archive": "संकुचित ढाँचाहरू",
+       "mediastatistics-header-total": "सबै फाइलहरु",
        "json-warn-trailing-comma": "$1 पछाडी रहेको छ {{PLURAL:$1|कोमा को|कोमाहरूको}} जेएसओएनबाट हटाइयो",
        "json-error-unknown": "जेएसओएन मा समस्या छ । समस्याः $1",
        "json-error-depth": "स्ट्याकको अधिकतम गहिराई बढी सकेको छ",
index 3e5e9dc..f1d766a 100644 (file)
        "october-date": "$1 oktober",
        "november-date": "$1 november",
        "december-date": "$1 december",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categorie|Categorieën}}",
        "category_header": "Pagina’s in categorie \"$1\"",
        "subcategories": "Ondercategorieën",
        "category-media-header": "Media in categorie \"$1\"",
-       "category-empty": "''Deze categorie bevat geen pagina’s of media.''",
+       "category-empty": "<em>Deze categorie bevat geen pagina’s of media.</em>",
        "hidden-categories": "Verborgen {{PLURAL:$1|categorie|categorieën}}",
        "hidden-category-category": "Verborgen categorieën",
        "category-subcat-count": "{{PLURAL:$2|Deze categorie bevat de volgende ondercategorie.|Deze categorie bevat de volgende {{PLURAL:$1|ondercategorie|$1 ondercategorieën}}, van een totaal van $2.}}",
        "laggedslavemode": "<strong>Waarschuwing:</strong> in deze pagina zijn recente wijzigingen mogelijk nog niet verwerkt.",
        "readonly": "Database geblokkeerd",
        "enterlockreason": "Geef een reden op voor de blokkade en geef op wanneer die waarschijnlijk wordt opgeheven",
-       "readonlytext": "De database is geblokkeerd voor bewerkingen, waarschijnlijk voor regulier databaseonderhoud. Na afronding wordt de functionaliteit hersteld.\n\nDe beheerder heeft de volgende reden opgegeven: $1",
+       "readonlytext": "De database is geblokkeerd voor bewerkingen, waarschijnlijk voor regulier databaseonderhoud. Na afronding wordt de functionaliteit hersteld.\n\nDe systeembeheerder heeft de volgende reden opgegeven: $1",
        "missing-article": "In de database is geen inhoud aangetroffen voor de pagina \"$1\" die er wel zou moeten zijn ($2).\n\nDit kan voorkomen als u een verouderde koppeling naar het verschil tussen twee versies van een pagina volgt of een versie opvraagt die is verwijderd.\n\nAls dit niet het geval is, hebt u wellicht een fout in de software gevonden.\nMaak hiervan melding bij een [[Special:ListUsers/sysop|beheerder]] van {{SITENAME}} en vermeld daarbij de URL van deze pagina.",
        "missingarticle-rev": "(versienummer: $1)",
        "missingarticle-diff": "(Wijziging: $1, $2)",
        "virus-scanfailed": "scannen is mislukt (code $1)",
        "virus-unknownscanner": "onbekend antivirusprogramma:",
        "logouttext": "<strong>U bent nu afgemeld.</strong>\n\nSommige pagina's kunnen blijven weergegeven alsof u nog aangemeld bent, totdat u uw browsercache leegt.",
+       "cannotlogoutnow-title": "Niet mogelijk om nu uit te loggen",
+       "cannotlogoutnow-text": "Uitloggen is niet mogelijk bij het gebruik van $1.",
        "welcomeuser": "Welkom, $1!",
        "welcomecreation-msg": "Uw account is aangemaakt.\nIndien gewenst kunt u uw [[Special:Preferences|voorkeuren]] voor {{SITENAME}} aanpassen.",
        "yourname": "Gebruikersnaam:",
        "remembermypassword": "Aanmeldgegevens onthouden (maximaal $1 {{PLURAL:$1|dag|dagen}})",
        "userlogin-remembermypassword": "Aangemeld blijven",
        "userlogin-signwithsecure": "Beveiligde verbinding gebruiken",
+       "cannotloginnow-title": "Niet mogelijk om nu in te loggen",
+       "cannotloginnow-text": "Inloggen is niet mogelijk bij het gebruik van $1.",
        "yourdomainname": "Uw domein:",
        "password-change-forbidden": "U kunt uw wachtwoord niet wijzigen in deze wiki.",
        "externaldberror": "Er is een fout opgetreden bij het aanmelden bij de database of u hebt geen toestemming uw externe gebruiker bij te werken.",
        "resetpass_submit": "Wachtwoord instellen en aanmelden",
        "changepassword-success": "Uw wachtwoord is gewijzigd.",
        "changepassword-throttled": "U heeft recentelijk te veel mislukte aanmeldpogingen gedaan.\nWacht alstublieft $1 voordat u het opnieuw probeert.",
+       "botpasswords": "Botwachtwoorden",
+       "botpasswords-disabled": "Botwachtwoorden zijn uitgeschakeld.",
+       "botpasswords-no-central-id": "Om botwachtwoorden te gebruiken, moet u ingelogd zijn met een gecentraliseerd account",
+       "botpasswords-existing": "Bestaande botwachtwoorden",
+       "botpasswords-createnew": "Een nieuw botwachtwoord aanmaken",
+       "botpasswords-editexisting": "Een bestaand botwachtwoord bewerken",
+       "botpasswords-label-appid": "Naam van bot:",
+       "botpasswords-label-create": "Aanmaken",
+       "botpasswords-label-update": "Bijwerken",
+       "botpasswords-label-cancel": "Annuleren",
+       "botpasswords-label-delete": "Verwijderen",
+       "botpasswords-label-resetpassword": "Het wachtwoord opnieuw instellen",
+       "botpasswords-label-restrictions": "Gebruiksbeperkingen:",
+       "botpasswords-label-grants-column": "Toegewezen",
+       "botpasswords-bad-appid": "De botnaam \"$1\" is niet geldig.",
+       "botpasswords-insert-failed": "Toevoegen van botnaam \"$1\" mislukt. Is deze misschien al toegevoegd?",
+       "botpasswords-update-failed": "Bijwerken van botnaam \"$1\" mislukt. Is deze misschien verwijderd?",
+       "botpasswords-created-title": "Botwachtwoord aangemaakt",
+       "botpasswords-created-body": "Het botwachtwoord \"$1\" is succesvol aangemaakt.",
+       "botpasswords-updated-title": "Botwachtwoord bijgewerkt",
+       "botpasswords-updated-body": "Het botwachtwoord \"$1\" is succesvol bijgewerkt.",
+       "botpasswords-deleted-title": "Botwachtwoord verwijderd",
+       "botpasswords-deleted-body": "Het botwachtwoord \"$1\" is verwijderd.",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider is niet beschikbaar.",
+       "botpasswords-restriction-failed": "Botwachtwoord-beperkingen maken het aanmelden onmogelijk.",
+       "botpasswords-not-exist": "Gebruiker \"$1\" heeft geen botwachtwoord genaamd \"$2\"",
        "resetpass_forbidden": "Wachtwoorden kunnen niet gewijzigd worden",
        "resetpass-no-info": "U dient aangemeld zijn voordat u deze pagina kunt gebruiken.",
        "resetpass-submit-loggedin": "Wachtwoord wijzigen",
        "passwordreset-text-many": "{{PLURAL:$1|Vul een van de gegevensvelden in om per e-mail een tijdelijk wachtwoord te ontvangen.}}",
        "passwordreset-disabled": "Het is in deze wiki niet mogelijk uw wachtwoord opnieuw in te stellen.",
        "passwordreset-emaildisabled": "E-mailmogelijkheden zijn uitgeschakeld op deze wiki.",
-       "passwordreset-username": "Gebruiker:",
+       "passwordreset-username": "Gebruikersnaam:",
        "passwordreset-domain": "Domein:",
        "passwordreset-capture": "De resulterende e-mail bekijken?",
        "passwordreset-capture-help": "Als u dit vakje aanvinkt, wordt de e-mail (met het tijdelijke wachtwoord) naar de gebruiker verzonden en ook aan u weergegeven.",
        "passwordreset-emailtext-ip": "Iemand, waarschijnlijk u, heeft vanaf het IP-adres $1 een aanvraag gedaan om uw wachtwoord voor {{SITENAME}} ($4) opnieuw in te stellen. De volgende {{PLURAL:$3|gebruiker is|gebruikers zijn}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tijdelijke wachtwoord vervalt|Deze tijdelijke wachtwoorden vervallen}} over {{PLURAL:$5|een dag|$5 dagen}}. Meld u aan en wijzig het wachtwoord nu. Als u dit verzoek niet zelf heeft gedaan, of als u het oorspronkelijke wachtwoord nog kent en het niet wilt wijzigen, negeer dit bericht dan en blijf uw oude wachtwoord gebruiken.",
        "passwordreset-emailtext-user": "Gebruiker $1 op de site {{SITENAME}} heeft een aanvraag gedaan om uw wachtwoord voor {{SITENAME}} ($4) opnieuw in te stellen. De volgende {{PLURAL:$3|gebruiker is|gebruikers zijn}} gekoppeld aan dit e-mailadres:\n\n$2\n\n{{PLURAL:$3|Dit tijdelijke wachtwoord vervalt|Deze tijdelijke wachtwoorden vervallen}} over {{PLURAL:$5|een dag|$5 dagen}}.\nMeld u aan en wijzig het wachtwoord nu. Als u dit verzoek niet zelf heeft gedaan, of als u het oorspronkelijke wachtwoord nog kent en het niet wilt wijzigen, negeer dit bericht dan en blijf uw oude wachtwoord gebruiken.",
        "passwordreset-emailelement": "Gebruikersnaam: \n$1\n\nTijdelijk wachtwoord: \n$2",
-       "passwordreset-emailsentemail": "Als dit een geregistreerd e-mailadres is voor uw account, dan wordt er een e-mail verzonden om uw wachtwoord opnieuw in te stellen.",
+       "passwordreset-emailsentemail": "Als dit een e-mailadres is dat gekoppeld is aan uw account, 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",
        "changeemail-throttled": "U heeft recentelijk te veel mislukte aanmeldpogingen gedaan.\nWacht alstublieft $1 voordat u het opnieuw probeert.",
        "changeemail-nochange": "Geef een ander e-mailadres op.",
        "resettokens": "Tokens opnieuw instellen",
-       "resettokens-text": "U kunt hier tokens opnieuw instellen die toegang tot bepaalde persoonlijke gegevens die zijn gekoppeld aan uw gebruiker.\n\nDoe dit als u ze per ongeluk met iemand hebt gedeeld of als uw onbevoegden toegang hebben gekregen tot uw gebruiker.",
+       "resettokens-text": "U kunt tokens opnieuw instellen die toegang geven tot bepaalde persoonlijke gegevens die aan uw account zijn verbonden.\n\nU zou dit moeten doen als u ze per ongeluk gedeeld heeft met anderen of als onbevoegden toegang tot uw account hebben gehad.",
        "resettokens-no-tokens": "Er zijn geen tokens om opnieuw in te stellen.",
        "resettokens-tokens": "Tokens:",
        "resettokens-token-label": "$1 (huidige waarde: $2)",
        "loginreqtitle": "Aanmelden verplicht",
        "loginreqlink": "aanmelden",
        "loginreqpagetext": "U moet zich $1 om andere pagina's te kunnen bekijken.",
-       "accmailtitle": "Wachtwoord verzonden.",
+       "accmailtitle": "Wachtwoord verzonden",
        "accmailtext": "Een willekeurig gegenereerd wachtwoord voor [[User talk:$1|$1]] is verzonden naar $2. Het kan worden gewijzigd op de pagina \"[[Special:ChangePassword|wachtwoord wijzigen]]\" na het aanmelden.",
        "newarticle": "(Nieuw)",
        "newarticletext": "Deze pagina bestaat niet.\nTyp in het onderstaande veld om de pagina aan te maken (meer informatie staat op de [$1 hulppagina]).\nGebruik de knop <strong>Terug</strong> in uw browser als u hier per ongeluk terecht bent gekomen.",
        "sitejspreview": "'''Dit is alleen een voorvertoning van de JavaScriptcode.'''\n'''Deze is nog niet opgeslagen!'''",
        "userinvalidcssjstitle": "'''Waarschuwing:''' er is geen uiterlijk \"$1\".\nUw eigen .css- en .js-pagina's beginnen met een kleine letter, bijvoorbeeld {{ns:user}}:Naam/vector.css in plaats van {{ns:user}}:Naam/Vector.css.",
        "updated": "(Bijgewerkt)",
-       "note": "'''Opmerking:'''",
+       "note": "<strong>Opmerking:</strong>",
        "previewnote": "'''Let op: dit is een controlepagina.'''\nUw tekst is niet opgeslagen!",
        "continue-editing": "Naar het bewerkingsvenster gaan",
        "previewconflict": "Deze voorvertoning geeft aan hoe de tekst in het bovenste veld eruit ziet als u deze opslaat.",
        "copyrightwarning2": "Al uw bijdragen aan {{SITENAME}} kunnen bewerkt, gewijzigd of verwijderd worden door andere gebruikers.\nAls u niet wilt dat uw teksten rigoureus aangepast worden door anderen, plaats ze hier dan niet.<br />\nU belooft ook dat u de oorspronkelijke auteur bent van dit materiaal of dat u het hebt gekopieerd uit een bron in het publieke domein of een soortgelijke vrije bron (zie $1 voor details).\n'''Gebruik geen materiaal dat beschermd wordt door auteursrecht, tenzij u daarvoor toestemming hebt!'''",
        "editpage-cannot-use-custom-model": "Het inhoudsmodel van deze pagina kan niet worden gewijzigd.",
        "longpageerror": "'''Fout: de tekst die u hebt toegevoegd is {{PLURAL:$1|één kilobyte|$1 kilobyte}} groot, wat groter is dan het maximum van {{PLURAL:$2|één kilobyte|$2 kilobyte}}.'''\nOpslaan is niet mogelijk.",
-       "readonlywarning": "'''Waarschuwing: u kunt deze bewerking nu niet opslaan omdat de database is geblokkeerd voor bewerkingen wegens onderhoudswerkzaamheden.'''\nHet is misschien verstandig om uw tekst tijdelijk in een tekstbestand op te slaan om dit te bewaren voor wanneer de blokkering van de database opgeheven is.\n\nEen beheerder heeft de database geblokkeerd om de volgende reden: $1",
+       "readonlywarning": "<strong>Waarschuwing: u kunt deze bewerking nu niet opslaan omdat de database is geblokkeerd voor bewerkingen wegens onderhoudswerkzaamheden.</strong>\nHet is misschien verstandig om uw tekst tijdelijk in een tekstbestand op te slaan om dit te bewaren voor wanneer de blokkering van de database opgeheven is.\n\nDe systeembeheerder heeft de database geblokkeerd om de volgende reden: $1",
        "protectedpagewarning": "'''Waarschuwing: deze beveiligde pagina kan alleen door gebruikers met beheerdersrechten bewerkt worden.'''\nDe laatste logboekregel staat hieronder:",
        "semiprotectedpagewarning": "'''Let op:''' deze pagina is beveiligd en kan alleen door geregistreerde gebruikers bewerkt worden.\nDe laatste logboekregel staat hieronder:",
        "cascadeprotectedwarning": "<strong>Waarschuwing:</strong> deze pagina is beveiligd en kan alleen door beheerders bewerkt worden, omdat ze is opgenomen in de volgende {{PLURAL:$1|pagina|pagina's}} die beveiligd {{PLURAL:$1|is|zijn}} met de cascade-optie:",
        "edit-gone-missing": "De pagina is niet bijgewerkt.\nDeze lijkt verwijderd te zijn.",
        "edit-conflict": "Bewerkingsconflict.",
        "edit-no-change": "Uw bewerking is genegeerd, omdat er geen wijziging aan de tekst is gemaakt.",
-       "postedit-confirmation-created": "De pagina is gemaakt.",
+       "postedit-confirmation-created": "De pagina is aangemaakt.",
        "postedit-confirmation-restored": "De pagina is hersteld.",
-       "postedit-confirmation-saved": "Uw bewerking is opgeslagen",
+       "postedit-confirmation-saved": "Uw bewerking is opgeslagen.",
        "edit-already-exists": "De pagina is niet aangemaakt.\nDeze bestaat al.",
        "defaultmessagetext": "Standaardinhoud",
        "content-failed-to-parse": "Het was niet mogelijk de inhoud van het MIME-type $2 voor het model $1 te verwerken: $3.",
        "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 reeks <strong>$1</strong> is niet mogelijk doordat dit is ingesteld door [[User:$3|$3]]. Uw IP-adres $4 bevindt zich in deze reeks.\n\nDe reden voor de blokkade is <em>$2</em>",
+       "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",
        "nohistory": "Deze pagina is niet bewerkt.",
        "currentrev": "Huidige versie",
        "revdelete-unsuppress": "Beperkingen op teruggeplaatste wijzigingen verwijderen",
        "revdelete-log": "Reden:",
        "revdelete-submit": "Toepassen op de geselecteerde {{PLURAL:$1|bewerking|bewerkingen}}",
-       "revdelete-success": "'''De zichtbaarheid van de wijziging is bijgewerkt.'''",
-       "revdelete-failure": "'''De zichtbaarheid van de wijziging kon niet bijgewerkt worden:'''\n$1",
+       "revdelete-success": "Zichtbaarheid van de wijziging is succesvol bijgewerkt.",
+       "revdelete-failure": "Zichtbaarheid van de wijziging kon niet bijgewerkt worden:\n$1",
        "logdelete-success": "De zichtbaarheid van de gebeurtenis is ingesteld.",
-       "logdelete-failure": "'''De zichtbaarheid van de logboekregel kon niet ingesteld worden:'''\n$1",
-       "revdel-restore": "Zichtbaarheid wijzigen",
+       "logdelete-failure": "Zichtbaarheid van de logboekregel kon niet ingesteld worden:\n$1",
+       "revdel-restore": "zichtbaarheid wijzigen",
        "pagehist": "Geschiedenis",
        "deletedhist": "verwijderde geschiedenis",
        "revdelete-hide-current": "Er is een fout opgetreden bij het verbergen van het object van $1 om $2 uur: dit is de huidige versie.\nDeze versie kan niet verborgen worden.",
        "revdelete-otherreason": "Andere reden:",
        "revdelete-reasonotherlist": "Andere reden",
        "revdelete-edit-reasonlist": "Redenen voor verwijderen bewerken",
-       "revdelete-offender": "Auteur versie:",
+       "revdelete-offender": "Auteur van versie:",
        "suppressionlog": "Verbergingslogboek",
        "suppressionlogtext": "De onderstaande lijst bevat de verwijderingen en blokkades die voor beheerders verborgen zijn.\nIn de [[Special:BlockList|blokkadelijst]] zijn de huidige blokkades te bekijken.",
        "mergehistory": "Geschiedenis van pagina's samenvoegen",
        "prefs-common-css-js": "Gedeelde CSS/JavaScript voor elke vormgeving:",
        "prefs-reset-intro": "Gebruik deze functie om uw voorkeuren te herstellen naar de standaardinstellingen.\nDeze handeling kan niet ongedaan gemaakt worden.",
        "prefs-emailconfirm-label": "E-mailbevestiging:",
-       "youremail": "Uw e-mailadres:",
+       "youremail": "E-mailadres:",
        "username": "{{GENDER:$1|Gebruikersnaam}}:",
        "prefs-memberingroups": "{{GENDER:$2|Lid}} van {{PLURAL:$1|groep|groepen}}:",
        "prefs-registration": "Registratiedatum:",
-       "yourrealname": "Uw echte naam:",
+       "yourrealname": "Echte naam:",
        "yourlanguage": "Taal:",
        "yourvariant": "Taalvariant voor inhoud:",
        "prefs-help-variant": "Uw voorkeursvariant of -spelling om de inhoudspagina's van deze wiki in weer te geven.",
        "userrights": "Gebruikersrechtenbeheer",
        "userrights-lookup-user": "Gebruikersgroepen beheren",
        "userrights-user-editname": "Voer een gebruikersnaam in:",
-       "editusergroup": "Gebruikersgroepen wijzigen",
+       "editusergroup": "{{GENDER:$1|Gebruiker}}sgroepen wijzigen",
        "editinguser": "Bezig met wijzigen van de gebruikersrechten van gebruiker '''[[User:$1|$1]]''' $2",
        "userrights-editusergroup": "Gebruikersgroepen wijzigen",
-       "saveusergroups": "Gebruikersgroepen opslaan",
+       "saveusergroups": "{{GENDER:$1|Gebruiker}}sgroepen opslaan",
        "userrights-groupsmember": "Lid van:",
        "userrights-groupsmember-auto": "Impliciet lid van:",
        "userrights-groups-help": "U kunt de groepen wijzigen waar deze gebruiker lid van is.\n* Een aangekruist vakje betekent dat de gebruiker lid is van de groep.\n* Een niet aangekruist vakje betekent dat de gebruiker geen lid is van de groep.\n* Een \"*\" betekent dat u een gebruiker niet uit een groep kunt verwijderen nadat u die hebt toegevoegd of vice versa.",
        "right-createpage": "Pagina's aanmaken",
        "right-createtalk": "Overlegpagina's aanmaken",
        "right-createaccount": "Nieuwe gebruikers aanmaken",
+       "right-autocreateaccount": "Automatisch inloggen met een extern gebruikersaccount",
        "right-minoredit": "Bewerkingen als klein markeren",
        "right-move": "Pagina's hernoemen",
        "right-move-subpages": "Pagina's inclusief subpagina's verplaatsen",
        "right-blockemail": "Een gebruiker het recht ontnemen om e-mail te versturen",
        "right-hideuser": "Een gebruiker voor de overige gebruikers verbergen",
        "right-ipblock-exempt": "IP-blokkades omzeilen",
-       "right-proxyunbannable": "Blokkades voor proxy's gelden niet",
        "right-unblockself": "Eigen gebruiker deblokkeren",
        "right-protect": "Beveiligingsniveaus wijzigen",
        "right-editprotected": "Pagina's bewerken die beveiligd zijn als \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "[[Special:Tags|Labels]] aan de database toevoegen of eruit verwijderen",
        "right-applychangetags": "[[Special:Tags|Labels]] aan bewerkingen toewijzen",
        "right-changetags": "Willekeurige [[Special:Tags|labels]] toevoegen aan en verwijderen van versies en logboekregels",
+       "grant-generic": "Rechtengroep \"$1\"",
+       "grant-group-page-interaction": "Werken met pagina's",
+       "grant-group-file-interaction": "Werken met media",
+       "grant-group-watchlist-interaction": "Werken met uw volglijst",
+       "grant-group-email": "E-mail verzenden",
+       "grant-group-high-volume": "Activiteiten met hoog volume uitvoeren",
+       "grant-group-customization": "Aanpassingen en voorkeuren",
+       "grant-group-administration": "Beheerdershandelingen uitvoeren",
+       "grant-group-other": "Diverse handelingen",
+       "grant-blockusers": "Gebruikers (de)blokkeren",
+       "grant-createaccount": "Gebruikers aanmaken",
+       "grant-createeditmovepage": "Pagina's aanmaken, bewerken en hernoemen",
+       "grant-delete": "Pagina's, wijzigingen en vermeldingen in het logboek verwijderen",
+       "grant-editinterface": "De naamruimte MediaWiki en CSS en JavaScript van gebruikers bewerken",
+       "grant-editmycssjs": "Uw eigen CSS/JavaScript bewerken",
+       "grant-editmyoptions": "Eigen voorkeuren instellen",
+       "grant-editmywatchlist": "Uw eigen volglijst bewerken",
+       "grant-editpage": "Bestaande pagina's bewerken",
+       "grant-editprotected": "Beveiligde pagina's bewerken",
+       "grant-highvolume": "Veel bewerkingen in korte tijd maken",
+       "grant-oversight": "Gebruikers en versies verbergen",
+       "grant-patrol": "Wijzigingen aan pagina's controleren",
+       "grant-protect": "Pagina's beveiligen en beveiliging opheffen",
+       "grant-rollback": "Wijzigingen aan pagina's terugdraaien",
+       "grant-sendemail": "E-mail verzenden aan andere gebruikers",
+       "grant-uploadeditmovefile": "Bestanden uploaden, vervangen en hernoemen",
+       "grant-uploadfile": "Nieuwe bestanden uploaden",
+       "grant-basic": "Basisrechten",
+       "grant-viewdeleted": "Verwijderde bestanden en pagina's bekijken",
+       "grant-viewmywatchlist": "Uw volglijst bekijken",
        "newuserlogpage": "Logboek nieuwe gebruikers",
        "newuserlogpagetext": "Hieronder staan de nieuw ingeschreven gebruikers",
        "rightslog": "Gebruikersrechtenlogboek",
        "action-createpage": "pagina's aan te maken",
        "action-createtalk": "overlegpagina's 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-minoredit": "deze bewerking als klein te markeren",
        "action-move": "deze pagina te hernoemen",
        "upload-form-label-select-file": "Selecteer bestand",
        "upload-form-label-infoform-title": "Details",
        "upload-form-label-infoform-name": "Naam",
+       "upload-form-label-infoform-name-tooltip": "Een korte beschrijvende naam voor het bestand, die als de bestandsnaam wordt gebruikt. U kunt platte tekst met spaties gebruiken. Neem de bestandsextensie niet op.",
        "upload-form-label-infoform-description": "Beschrijving",
        "upload-form-label-usage-title": "Gebruik",
        "upload-form-label-usage-filename": "Bestandsnaam",
        "protectedpages-performer": "Beveiligd door",
        "protectedpages-params": "Beveiligingsopties",
        "protectedpages-reason": "Reden",
+       "protectedpages-submit": "Pagina's weergeven",
        "protectedpages-unknown-timestamp": "Onbekend",
        "protectedpages-unknown-performer": "Onbekende gebruiker",
        "protectedtitles": "Beveiligde paginanamen",
        "protectedtitles-summary": "Deze pagina bevat een lijst met pagina's die niet mogen worden aangemaakt. Zie [[{{#special:ProtectedPages}}|{{int:protectedpages}}]] voor de lijst met beveiligde pagina's.",
        "protectedtitlesempty": "Er zijn geen paginanamen beveiligd die aan deze voorwaarden voldoen.",
+       "protectedtitles-submit": "Paginanamen weergeven",
        "listusers": "Gebruikerslijst",
        "listusers-editsonly": "Alleen gebruikers met bewerkingen weergeven",
        "listusers-creationsort": "Sorteren op registratiedatum",
        "log-title-wildcard": "Pagina's zoeken die met deze tekens beginnen",
        "showhideselectedlogentries": "Geselecteerde logboekregels weergeven of verbergen",
        "log-edit-tags": "Labels van geselecteerde logboekregels bewerken",
+       "checkbox-select": "Selecteer: $1",
+       "checkbox-all": "Alle",
+       "checkbox-none": "Geen",
+       "checkbox-invert": "Omkeren",
        "allpages": "Alle pagina's",
        "nextpage": "Volgende pagina ($1)",
        "prevpage": "Vorige pagina ($1)",
        "listgrouprights-namespaceprotection-header": "Naamruimtebeperkingen",
        "listgrouprights-namespaceprotection-namespace": "Naamruimte",
        "listgrouprights-namespaceprotection-restrictedto": "Recht(en) waardoor gebruiker kan bewerken",
+       "listgrants": "Toestemmingen",
+       "listgrants-summary": "Hieronder staat een lijst met toestemmingen en de bijbehorende gebruikersrechten. Gebruikers kunnen toepassingen machtigen voor toegang tot hun gebruikers, maar met beperkte rechten gebaseerd op de toestemmingen die de gebruiker aan de toepassing heeft gegeven. Een toepassing die namens een gebruiker handelt, kan nooit rechten gebruiken die een gebruiker niet heeft.\nEr zijn mogelijk [[{{MediaWiki:Listgrouprights-helppage}}|aanvullende  gegevens]] over individuele rechten.",
+       "listgrants-grant": "Toestemming",
+       "listgrants-rights": "Rechten",
        "trackingcategories": "Volgcategorieën",
        "trackingcategories-summary": "Op deze pagina worden volgcategorieën weergegeven die automatisch worden gevuld door de MediaWikisoftware. De namen van de categorieën kunnen gewijzigd worden door de bijbehorende systeemberichten in de naamruimte \"{{ns:8}}\" bij te werken.",
        "trackingcategories-msg": "Volgcategorie",
        "wlshowhideanons": "anonieme gebruikers",
        "wlshowhidepatr": "gecontroleerde bewerkingen",
        "wlshowhidemine": "mijn bewerkingen",
+       "wlshowhidecategorization": "categorisatie van pagina's",
        "watchlist-options": "Opties voor volglijst",
        "watching": "Bezig met plaatsen op volglijst…",
        "unwatching": "Bezig met verwijderen van volglijst…",
        "whatlinkshere-hidelinks": "koppelingen $1",
        "whatlinkshere-hideimages": "Bestandskoppelingen $1",
        "whatlinkshere-filters": "Filters",
+       "whatlinkshere-submit": "OK",
        "autoblockid": "Automatische blokkade #$1",
        "block": "Gebruiker blokkeren",
        "unblock": "Gebruiker deblokkeren",
        "blockip": "{{GENDER:$1|Gebruiker}} blokkeren",
        "blockip-legend": "Gebruiker blokkeren",
-       "blockiptext": "Gebruik het onderstaande formulier om schrijftoegang voor een gebruiker of IP-adres in te trekken.\nDoe dit alleen als bescherming tegen vandalisme en in overeenstemming met het [[{{MediaWiki:Policy-url}}|beleid]].\nGeef hieronder een reden op (bijvoorbeeld welke pagina's gevandaliseerd zijn).",
+       "blockiptext": "Gebruik het onderstaande formulier om schrijftoegang voor een gebruiker of IP-adres in te trekken.\nDoe dit alleen als bescherming tegen vandalisme en in overeenstemming met het [[{{MediaWiki:Policy-url}}|beleid]].\nGeef hieronder een reden op (bijvoorbeeld welke pagina's gevandaliseerd zijn).\nU kunt IP-ranges blokkeren door gebruik te maken van de [https://nl.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]-syntax, tot een maximum range van /$1 voor IPv4 en /$2 voor IPv6.",
        "ipaddressorusername": "IP-adres of gebruikersnaam:",
        "ipbexpiry": "Vervalt (maak een keuze):",
        "ipbreason": "Reden:",
        "block-log-flags-hiddenname": "gebruiker verborgen",
        "range_block_disabled": "De mogelijkheid voor beheerders om een groep IP-adressen te blokkeren is uitgeschakeld.",
        "ipb_expiry_invalid": "Ongeldige duur.",
+       "ipb_expiry_old": "Vervaldatum is in het verleden.",
        "ipb_expiry_temp": "Blokkades voor verborgen gebruikers moeten permanent zijn.",
        "ipb_hide_invalid": "Het is niet mogelijk deze gebruiker te verbergen; deze heeft meer dan {{PLURAL:$1|een bewerking|$1 bewerkingen}} uitgevoerd.",
        "ipb_already_blocked": "\"$1\" is al geblokkeerd",
        "ipb-otherblocks-header": "Andere {{PLURAL:$1|blokkade|blokkades}}",
        "unblock-hideuser": "U kunt deze gebruiker niet deblokkeren, omdat de gebruikersnaam is verborgen.",
        "ipb_cant_unblock": "Fout: blokkadenummer $1 is niet gevonden.\nMisschien is de blokkade al opgeheven.",
-       "ipb_blocked_as_range": "Fout: het IP-adres $1 is niet direct geblokkeerd en de blokkade kan niet opgeheven worden.\nDe blokkade is onderdeel van de reeks $2, waarvan de blokkade wel opgeheven kan worden.",
-       "ip_range_invalid": "Ongeldige IP-reeks.",
+       "ipb_blocked_as_range": "Fout: het IP-adres $1 is niet direct geblokkeerd en de blokkade kan niet opgeheven worden.\nDe blokkade is onderdeel van de range $2, waarvan de blokkade wel opgeheven kan worden.",
+       "ip_range_invalid": "Ongeldige IP-range.",
        "ip_range_toolarge": "Reeksblokkades groter dan /$1 zijn niet toegestaan.",
        "proxyblocker": "Proxyblocker",
        "proxyblockreason": "Uw IP-adres is geblokkeerd, omdat u gebruik maakt van een open proxyserver.\nNeem contact op met uw internetprovider of uw helpdesk en stel die op de hoogte van dit ernstige beveiligingsprobleem.",
        "movenosubpage": "Deze pagina heeft geen subpagina's.",
        "movereason": "Reden:",
        "revertmove": "terugdraaien",
-       "delete_and_move_text": "==Verwijdering nodig==\nOnder de naam \"[[:$1]]\" bestaat al een pagina.\nWilt u deze verwijderen om plaats te maken voor de te hernoemen pagina?",
+       "delete_and_move_text": "Onder de naam \"[[:$1]]\" bestaat al een pagina.\nWilt u deze verwijderen om plaats te maken voor de te hernoemen pagina?",
        "delete_and_move_confirm": "Ja, de pagina verwijderen",
        "delete_and_move_reason": "Verwijderd in verband met hernoeming van \"[[$1]]\"",
        "selfmove": "U kunt een pagina niet hernoemen naar dezelfde paginanaam.",
        "move-leave-redirect": "Een doorverwijzing achterlaten",
        "protectedpagemovewarning": "'''Waarschuwing:''' deze pagina kan alleen door beheerders hernoemd worden.\nDe laatste logboekregel staat hieronder:",
        "semiprotectedpagemovewarning": "'''Let op:''' deze pagina kan alleen door geregistreerde gebruikers hernoemd worden.\nDe laatste logboekregel staat hieronder:",
-       "move-over-sharedrepo": "== Het bestand bestaat al ==\n[[:$1]] bestaat al in een gedeelde mediadatabank.\nDoor een bestand te hernoemen naar deze naam, is het bestand uit de gedeelde mediadatabank niet langer te gebruiken.",
+       "move-over-sharedrepo": "[[:$1]] bestaat al in een gedeelde mediadatabank. Door een bestand te hernoemen naar deze naam, is het bestand uit de gedeelde mediadatabank niet langer te gebruiken.",
        "file-exists-sharedrepo": "Deze bestandsnaam bestaat al in een gedeelde mediadatabank.\nKies een andere bestandsnaam.",
        "export": "Exporteren",
        "exporttext": "U kunt de tekst en geschiedenis van een pagina of pagina's exporteren naar XML.\nDit exportbestand is daarna te importeren in een andere MediaWiki via de [[Special:Import|importpagina]].\n\nGeef in het onderstaande veld de namen van de te exporteren pagina's op, één pagina per regel, en geef aan of u alle versies met de bewerkingssamenvatting of alleen de huidige versies met de bewerkingssamenvatting wilt exporteren.\n\nIn het laatste geval kunt u ook een koppeling gebruiken, bijvoorbeeld [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] voor de pagina \"[[{{MediaWiki:Mainpage}}]]\".",
        "export-download": "Als bestand opslaan",
        "export-templates": "Sjablonen toevoegen",
        "export-pagelinks": "Pagina's waarnaar verwezen wordt toevoegen tot een diepte van:",
+       "export-manual": "Pagina's handmatig toevoegen:",
        "allmessages": "Systeemteksten",
        "allmessagesname": "Naam",
        "allmessagesdefault": "Standaardinhoud",
        "javascripttest-pagetext-frameworks": "Kies een van de volgende testframeworks: $1",
        "javascripttest-pagetext-skins": "Kies een vormgeving om de tests mee uit te voeren:",
        "javascripttest-qunit-intro": "Zie de [$1 testdocumentatie] op mediawiki.org.",
-       "tooltip-pt-userpage": "Uw gebruikerspagina",
+       "tooltip-pt-userpage": "{{GENDER:|Uw}} gebruikerspagina",
        "tooltip-pt-anonuserpage": "Gebruikerspagina voor uw IP-adres",
-       "tooltip-pt-mytalk": "Uw overlegpagina",
+       "tooltip-pt-mytalk": "{{GENDER:|Uw}} overlegpagina",
        "tooltip-pt-anontalk": "Overlegpagina van de anonieme gebruiker van dit IP-adres",
-       "tooltip-pt-preferences": "Mijn voorkeuren",
+       "tooltip-pt-preferences": "{{GENDER:|Uw}} voorkeuren",
        "tooltip-pt-watchlist": "Overzicht van pagina's die u volgt",
-       "tooltip-pt-mycontris": "Overzicht van uw bijdragen",
+       "tooltip-pt-mycontris": "Overzicht van {{GENDER:|uw}} bijdragen",
        "tooltip-pt-anoncontribs": "Een lijst van bewerkingen gemaakt door dit IP-adres",
        "tooltip-pt-login": "U wordt van harte uitgenodigd om aan te melden, maar dit is niet verplicht",
        "tooltip-pt-logout": "Afmelden",
        "tooltip-t-recentchangeslinked": "Recente wijzigingen in pagina's waar deze pagina naar verwijst",
        "tooltip-feed-rss": "RSS-feed voor deze pagina",
        "tooltip-feed-atom": "Atom-feed voor deze pagina",
-       "tooltip-t-contributions": "Een lijst met bijdragen van deze gebruiker",
-       "tooltip-t-emailuser": "Een e-mail naar deze gebruiker verzenden",
+       "tooltip-t-contributions": "Een lijst met bijdragen van {{GENDER:$1|deze gebruiker}}",
+       "tooltip-t-emailuser": "Een e-mail naar {{GENDER:$1|deze gebruiker}} verzenden",
        "tooltip-t-info": "Meer informatie over deze pagina",
        "tooltip-t-upload": "Bestanden uploaden",
        "tooltip-t-specialpages": "Lijst met alle speciale pagina's",
        "pageinfo-category-files": "Aantal bestanden",
        "markaspatrolleddiff": "Als gecontroleerd markeren",
        "markaspatrolledtext": "Deze pagina als gecontroleerd markeren",
+       "markaspatrolledtext-file": "Deze bestandsversie als gecontroleerd markeren",
        "markedaspatrolled": "Gemarkeerd als gecontroleerd",
        "markedaspatrolledtext": "De geselecteerde bewerking van [[:$1]] is gemarkeerd als gecontroleerd.",
        "rcpatroldisabled": "De controlemogelijkheid op recente wijzigingen is uitgeschakeld.",
        "newimages-legend": "Bestandsnaam",
        "newimages-label": "Bestandsnaam (of deel daarvan):",
        "newimages-showbots": "Uploads door bots weergeven",
+       "newimages-hidepatrolled": "Gecontroleerde uploads verbergen",
        "noimages": "Er is niets te zien.",
        "ilsubmit": "Zoeken",
        "bydate": "op datum",
        "exif-compression-4": "CCITT Groep 4 faxcodering",
        "exif-copyrighted-true": "Auteursrechtelijk beschermd",
        "exif-copyrighted-false": "Auteursrechtelijke status niet ingesteld",
+       "exif-photometricinterpretation-1": "Zwart-wit (zwart is 0)",
        "exif-unknowndate": "Datum onbekend",
        "exif-orientation-1": "Normaal",
        "exif-orientation-2": "Horizontaal gespiegeld",
        "scarytranscludefailed-httpstatus": "[De sjabloon $1 kon niet opgehaald worden: HTTP $2]",
        "scarytranscludetoolong": "[De URL is te lang]",
        "deletedwhileediting": "'''Let op''': deze pagina is verwijderd terwijl u bezig was met uw bewerking!",
-       "confirmrecreate": "Nadat u begonnen bent met uw wijziging heeft [[User:$1|$1]] ([[User talk:$1|overleg]]) deze pagina verwijderd met opgave van de volgende reden:\n: ''$2''\nBevestig dat u de pagina opnieuw wilt aanmaken.",
-       "confirmrecreate-noreason": "Nadat u begonnen bent met uw wijziging heeft [[User:$1|$1]] ([[User talk:$1|overleg]]) deze pagina verwijderd.\nBevestig dat u de pagina opnieuw wilt aanmaken.",
+       "confirmrecreate": "Nadat u begonnen bent met uw wijziging heeft [[User:$1|$1]] ([[User talk:$1|overleg]]) deze pagina {{GENDER:$1|verwijderd}} met opgave van de volgende reden:\n: <em>$2</em>\nBevestig dat u de pagina opnieuw wilt aanmaken.",
+       "confirmrecreate-noreason": "Nadat u begonnen bent met uw wijziging heeft [[User:$1|$1]] ([[User talk:$1|overleg]]) deze pagina {{GENDER:$1|verwijderd}}. Bevestig dat u de pagina opnieuw wilt aanmaken.",
        "recreate": "Opnieuw aanmaken",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "De cache van deze pagina legen?",
        "iranian-calendar-m11": "Elfde Perzische maand",
        "iranian-calendar-m12": "Twaalfde Perzische maand",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|overleg]])",
+       "timezone-local": "Lokale tijd",
        "duplicate-defaultsort": "'''Waarschuwing:''' de standaardsortering \"$2\" krijgt voorrang voor de sortering \"$1\".",
        "duplicate-displaytitle": "<strong>Waarschuwing:</strong>Titelweergave \"$2\" overschrijft eerdere titelweergave \"$1\".",
        "invalid-indicator-name": "<strong>Fout:</strong> de eigenschap <code>name</code> van de paginastatusindicators mag niet leeg zijn.",
        "tags-deactivate": "deactiveren",
        "tags-hitcount": "$1 {{PLURAL:$1|wijziging|wijzigingen}}",
        "tags-manage-no-permission": "U hebt geen rechten om labels te beheren.",
+       "tags-manage-blocked": "U kunt geen labels beheren wanneer u geblokkeerd bent.",
        "tags-create-heading": "Een nieuw label aanmaken",
        "tags-create-explanation": "Standaard worden nieuw aangemaakte labels beschikbaar gesteld voor gebruik door gebruikers en bots.",
        "tags-create-tag-name": "Labelnaam:",
        "tags-deactivate-not-allowed": "Het is niet mogelijk het label \"$1\" te deactiveren.",
        "tags-deactivate-submit": "Deactiveren",
        "tags-apply-no-permission": "U hebt geen rechten om wijzigingslabels toe te voegen aan uw wijzigingen.",
+       "tags-apply-blocked": "U kunt geen wijzigen aanbrengen aan labels wanneer u geblokkeerd bent.",
        "tags-apply-not-allowed-one": "Het label \"$1\" mag niet handmatig toegevoegd worden.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Het volgende label mag|De volgende labels mogen}} niet handmatig toegevoegd worden: $1",
        "tags-update-no-permission": "U hebt geen rechten om wijzigingslabels toe te voegen aan of te verwijderen van versies of logboekregels.",
+       "tags-update-blocked": "U kunt geen labels toevoegen of verwijderen wanneer u geblokkeerd bent.",
        "tags-update-add-not-allowed-one": "Het label \"$1\" mag niet handmatig toegevoegd worden.",
        "tags-update-add-not-allowed-multi": " {{PLURAL:$2|Het label kan|De labels kunnen}} niet handmatig toegevoegd worden: $1",
        "tags-update-remove-not-allowed-one": "Het label \"$1\" mag niet verwijderd worden.",
        "expand_templates_preview": "Voorvertoning",
        "expand_templates_preview_fail_html": "<em>Omdat voor {{SITENAME}} ruwe HTML is ingeschakeld, en er sessiegegevens verloren zijn gegaan, is de voorvertoning verborgen als voorzorgmaatregel tegen JavaScriptaanvallen.</em>\n\n<strong>Als dit een legitieme poging is voor het weergeven van een voorvertoning, probeer het dan opnieuw.</strong>\nAls het dan nog steeds niet werkt, probeer dan [[Special:UserLogout|af te melden]] en opnieuw aan te melden.",
        "expand_templates_preview_fail_html_anon": "<em>Omdat in {{SITENAME}} ruwe HTML is ingeschakeld en u niet bent aangemeld, is de voorvertoning verborgen als voorzorgsmaatregel tegen de JavaScriptaanvallen.</em>\n\n<strong>Als dit een legitieme poging is voor het maken van een voorvertoning, [[Special:UserLogin|meld u dan aan]] en probeer het opnieuw.</strong>",
+       "expand_templates_input_missing": "U moet wel iets invullen.",
        "pagelanguage": "Paginataal kiezen",
        "pagelang-name": "Pagina",
        "pagelang-language": "Taal",
        "pagelang-use-default": "Standaard taal gebruiken",
        "pagelang-select-lang": "Taal selecteren",
+       "pagelang-submit": "Verzenden",
        "right-pagelang": "Paginataal wijzigen",
        "action-pagelang": "paginataal te wijzigen",
        "log-name-pagelang": "Logboek taalwijzigingen",
        "mediastatistics": "Mediastatistieken",
        "mediastatistics-summary": "Statistieken over geüploade bestandstypen. Dit overzicht bevat alleen de meest recente versie van een bestand. Oude of verwijderde versies worden niet meegeteld.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Totale bestandsgrootte van deze sectie: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Totale bestandsgrootte van alle bestanden: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "MIME-type",
        "mediastatistics-table-extensions": "Mogelijke extensies",
        "mediastatistics-table-count": "Aantal bestanden",
        "mediastatistics-header-text": "Tekstbestanden",
        "mediastatistics-header-executable": "Uitvoerbare bestanden",
        "mediastatistics-header-archive": "Gecomprimeerde bestanden",
+       "mediastatistics-header-total": "Alle bestanden",
        "json-warn-trailing-comma": "Er {{PLURAL:$1|is $1 komma|zijn $1 komma's}} aan het einde van de regel verwijderd uit de JSON",
        "json-error-unknown": "Er is een fout opgetreden met de JSON. Foutmelding: $1",
        "json-error-depth": "De maximale stackdiepte is overschreden",
        "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."
+       "api-error-blacklisted": "Kies een andere, beschrijvende naam.",
+       "sessionprovider-generic": "$1 sessies",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "sessies gebaseerd op cookies",
+       "sessionprovider-nocookies": "Cookies kunnen uitgeschakeld zijn. Zorg ervoor dat u cookies hebt ingeschakeld en probeer het opnieuw.",
+       "randomrootpage": "Willekeurige hoofdpagina"
 }
index a24db3a..3a47652 100644 (file)
        "confirmable-no": "Nei",
        "thisisdeleted": "Sjå eller attopprett $1?",
        "viewdeleted": "Sjå historikk for $1?",
-       "restorelink": "{{PLURAL:$1|Ã\89in sletta versjon|$1 sletta versjonar}}",
+       "restorelink": "{{PLURAL:$1|éin sletta versjon|$1 sletta versjonar}}",
        "feedlinks": "Abonnementskjelde:",
        "feed-invalid": "Ugyldig abonnementstype.",
        "feed-unavailable": "Det er ingen kjelder til abonnement",
        "right-blockemail": "Blokkere brukarar frå å sende e-post",
        "right-hideuser": "Blokkere eit brukarnamn og skjule det frå ålmenta.",
        "right-ipblock-exempt": "Kan gjere endringar frå blokkerte IP-adresser",
-       "right-proxyunbannable": "Kan gjere endringar frå blokkerte proxyar",
        "right-unblockself": "Avblokkera seg sjølve",
        "right-protect": "Endra vernenivå og verna sider",
        "right-editprotected": "Endre verna sider",
        "revdelete-uname-unhid": "brukarnamn gjort synleg",
        "revdelete-restricted": "la til avgrensingar for administratorar",
        "revdelete-unrestricted": "fjerna avgrensingar for administratorar",
+       "logentry-block-block": "$1 {{GENDER:$2|blokkerte}} {{GENDER:$4|$3}} for $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|endra}} blokkeringsinnstillingar for {{GENDER:$4|$3}} med opphøyrstid $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|flytte}} sida $3 til $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|flytte}} sida $3 til $4 utan å lata etter ei omdirigering",
        "special-characters-group-gujarati": "Gujarati",
        "mw-widgets-dateinput-placeholder-day": "ÅÅÅÅ-MM-DD",
        "mw-widgets-dateinput-placeholder-month": "ÅÅÅÅ-MM",
-       "api-error-blacklisted": "Vel eit anna namn som er meir skildrande."
+       "api-error-blacklisted": "Vel eit anna namn som er meir skildrande.",
+       "randomrootpage": "Tilfeldig rotsida"
 }
index 8408227..4e60293 100644 (file)
        "blanknamespace": "(Piä)",
        "contributions": "{{GENDER:$1|Käyttäi}} kirjutukset",
        "mycontris": "Kirjutukset",
+       "anoncontribs": "Sinun panos",
        "month": "Täs kuus (libo aijembi)",
        "year": "Tänävuon (libo aijembi):",
        "whatlinkshere": "Linkit tänne",
index 8f760e4..df9c0d3 100644 (file)
        "passwordreset-emailtext-ip": "କେହିଜଣେ (ବୋଧେ ଆପଣ, $1 IP addressରୁ) {{SITENAME}} ($4)ରେ ପାସୱାର୍ଡ଼ ରି-ସେଟ କରିବା ପାଇଁ ଅନୁରୋଧ କରିଛନ୍ତି । ଉକ୍ତ ଇମେଲ ଠିକଣା ସହିତ ଏହି {{PLURAL:$3|ସଭ୍ୟ ଖାତାଟି|ସଭ୍ୟ ଖାତାମାନ}} ସମ୍ବନ୍ଧିତ:\n\n$2\n\n{{PLURAL:$3|ଏହି ଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼ଟି|ଏହି ଅସ୍ଥାୟୀ ପାସୱାର୍ଡ଼ମାନ}} {{PLURAL:$5|ଦିନକ|$5 ଦିନ}}ରେ ଅଚଳ ହୋଇଯିବ ।\nଆପଣ ଏବେ ଲଗ ଇନ କରି ନୂଆ ପାସୱାର୍ଡ଼ଟିଏ ବାଛନ୍ତୁ । ଯଦି ଆଉ କେହି ଏହି ଅନୁରୋଧ କରିଥାନ୍ତି କିମ୍ବା ନିଜର ପୁରୁଣା ପାସୱାର୍ଡ଼ଟି ମନେପଡ଼ିଲା, ଏବଂ ଆଉ ପାସୱାର୍ଡ଼ଟି ବଦଳାଇବାକୁ ଚାହୁଁନାହାଁନ୍ତି ତାହେଲେ ଏହି ମେଲଟିକୁ ଅଣଦେଖା କରି ନିଜର ପୁରୁଣା ପାସୱାର୍ଡ଼ ବ୍ୟବହାର କରନ୍ତୁ ।",
        "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-emailsent": "ଏକ ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲ ପଠାଇଦିଆଯାଇଅଛି ।",
+       "passwordreset-emailsentemail": "ଏକ ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲ ପଠାଇଦିଆଯାଇଅଛି ।",
        "passwordreset-emailsent-capture": "ତଳେ ଦେଖାଯାଉଥିବା ଭଳି, ପାସୱାର୍ଡ଼ ପୁନଃସ୍ଥାପନ ଇମେଲଟିଏ ପଠାଇଦିଆଯାଇଛି ।",
        "passwordreset-emailerror-capture": "ପାସୱାର୍ଡ଼ ବଦଳାଇବା ସୂଚନା ସହ ଇମେଲଟିଏ ତିଆରି ହୋଇଛି, ଯାହା ତଳେ ଦେଖିପାରିବେ । କିନ୍ତୁ ଏହାକୁ {{GENDER:$2|ସଭ୍ୟ}}ଙ୍କୁ ପଠାଇବାରେ ବିଫଳ ହେଲୁ, କାରଣ: $1",
        "changeemail": "ଇ-ମେଲ ଠିକଣା ବଦଳାଇବେ",
        "prefs-help-prefershttps": "ଏହି ପସନ୍ଦ ଆପଣଙ୍କ ଲଗ୍ଇନ୍ କରିବାପରେ କାର୍ଯ୍ୟକ୍ଷମ ହେବ ।",
        "prefswarning-warning": "ଆପଣ ନିଜ \"ପସନ୍ଦ\"ରେ କରିଥିବା ବଦଳ ଏଯାଏ ସାଇତା ଯାଇନାହିଁ ।\nଯଦି ଆପଣ \"$1\"ରେ କ୍ଲିକ ନ କରି ଏହି ପୃଷ୍ଠା ଛାଡ଼ି ଚାଲିଗଲେ ଆପଣଙ୍କର ପସନ୍ଦ ଅପଡେଟ ହେବ ନାହିଁ ।",
        "prefs-tabs-navigation-hint": "ସୂଚନା: ବାମ ଓ ଡାହାଣ ଆରୋ କି ବ୍ୟବହାର କରି ଆପଣ ଗୋଟେ ଟ୍ୟାବରୁ ଆଉ ଗୋଟେ ଟ୍ୟାବକୁ ଯାଇପାରିବ ।",
-       "email-address-validity-valid": "ଇ-ମେଲ ଠିକଣା ବୈଧ ଭଳି ଲାଗୁଅଛି",
-       "email-address-validity-invalid": "ଏକ ସଠିକ ଇ-ମେଲ ଠିକଣା ଦିଅନ୍ତୁ",
        "userrights": "ସଭ୍ୟ ଅଧିକାର ପରିଚାଳନା",
        "userrights-lookup-user": "ସଭ୍ୟ ଗୋଠ ପରିଚାଳନା କରିବେ",
        "userrights-user-editname": "ଇଉଜର ନାମଟିଏ ଦିଅନ୍ତୁ:",
        "contributions": "{{GENDER:$1|ବ୍ୟବହାରକାରୀ}}ଙ୍କ ଅବଦାନ",
        "contributions-title": "$1 ପାଇଁ ବ୍ୟବହାରକାରୀଙ୍କ ଦାନ",
        "mycontris": "ଅବଦାନ",
+       "anoncontribs": "ଅବଦାନ",
        "contribsub2": "{{GENDER:$3|$1}} ପାଇଁ  ($2)",
        "contributions-userdoesnotexist": "ଇଉଜର ନାମ \"$1\" ତିଆରି କରାଯାଇ ନାହିଁ ।",
        "nocontribs": "ଏହି ନିର୍ଣ୍ଣାୟକବଳୀ ନିମନ୍ତେ କିଛି ବି ବଦଳ ମେଳ ଖାଇଲା ନାହିଁ ।",
index 59f5979..e6af41d 100644 (file)
@@ -9,7 +9,8 @@
                        "아라",
                        "Leeheonjin",
                        "TTO",
-                       "Macofe"
+                       "Macofe",
+                       "Amiel Guanlao"
                ]
        },
        "tog-underline": "Gulisan lang panglalam deng suglung:",
        "unprotectthispage": "Lako ya pangaprotekta ing bulung a ini",
        "newpage": "Bayung bulung",
        "talkpage": "Pisabian ya ining bulung",
-       "talkpagelinktext": "Pisasabian",
+       "talkpagelinktext": "talamitam",
        "specialpage": "Bulung a Makabukud",
        "personaltools": "Sariling kasangkapan",
        "articlepage": "Lawen me ing kalamnan ning bulung",
index 04957af..0ea0a17 100644 (file)
        "october-date": "$1 października",
        "november-date": "$1 listopada",
        "december-date": "$1 grudnia",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategoria|Kategorie}}",
        "category_header": "Strony w kategorii „$1”",
        "subcategories": "Podkategorie",
        "laggedslavemode": "Uwaga! Ta strona może nie zawierać najnowszych aktualizacji.",
        "readonly": "Baza danych jest zablokowana",
        "enterlockreason": "Podaj powód zablokowania bazy oraz szacunkowy termin jej odblokowania",
-       "readonlytext": "Baza danych jest obecnie zablokowana – nie można wprowadzać nowych informacji ani modyfikować istniejących. Powodem są prawdopodobnie czynności administracyjne. Po ich zakończeniu przywrócona zostanie pełna funkcjonalność bazy.\n\nAdministrator, który zablokował bazę, podał następujące wyjaśnienie: $1",
+       "readonlytext": "Baza danych jest obecnie zablokowana – nie można wprowadzać nowych informacji ani modyfikować istniejących. Powodem są prawdopodobnie czynności administracyjne. Po ich zakończeniu przywrócona zostanie pełna funkcjonalność bazy.\n\nAdministrator systemu, który zablokował bazę, podał następujące wyjaśnienie: $1",
        "missing-article": "W bazie danych nie odnaleziono treści strony „$1” $2.\n\nZazwyczaj jest to spowodowane odwołaniem do nieaktualnego linku prowadzącego do różnicy pomiędzy dwoma wersjami strony lub do wersji z historii usuniętej strony.\n\nJeśli tak nie jest, możliwe, że problem został wywołany przez błąd w oprogramowaniu.\nMożna zgłosić ten fakt [[Special:ListUsers/sysop|administratorowi]], podając adres URL.",
        "missingarticle-rev": "(wersja $1)",
        "missingarticle-diff": "(różnica: $1, $2)",
        "virus-scanfailed": "skanowanie nieudane (błąd $1)",
        "virus-unknownscanner": "nieznany antivirus:",
        "logouttext": "'''Nie jesteś już zalogowany.'''\n\nZauważ, że do momentu wyczyszczenia pamięci podręcznej przeglądarki niektóre strony mogą wyglądać tak, jakbyś wciąż był zalogowany.",
+       "cannotlogoutnow-title": "Nie możesz się teraz wylogować",
+       "cannotlogoutnow-text": "Podczas używania $1 wylogowanie nie jest niemożliwe.",
        "welcomeuser": "Witaj, $1!",
        "welcomecreation-msg": "Twoje konto zostało utworzone.\nMożesz zmienić swoje [[Special:Preferences|preferencje]], jeśli chcesz.",
        "yourname": "Nazwa {{GENDER:|użytkownika|użytkowniczki}}:",
        "resetpass_submit": "Ustaw hasło i zaloguj się",
        "changepassword-success": "Twoje hasło zostało pomyślnie zmienione!",
        "changepassword-throttled": "Ostatnio zbyt wiele razy próbowałeś zalogować się na to konto.\nOdczekaj $1, zanim ponowisz próbę.",
+       "botpasswords-label-appid": "Nazwa bota:",
+       "botpasswords-label-create": "Utwórz",
+       "botpasswords-label-update": "Aktualizuj",
+       "botpasswords-label-cancel": "Anuluj",
+       "botpasswords-label-delete": "Usuń",
+       "botpasswords-label-resetpassword": "Zresetuj hasło",
        "resetpass_forbidden": "Hasła nie mogą zostać zmienione",
        "resetpass-no-info": "Musisz być zalogowany, by uzyskać bezpośredni dostęp do tej strony.",
        "resetpass-submit-loggedin": "Zmień hasło",
        "confirmedittext": "Edytowanie jest możliwe dopiero po zweryfikowaniu adresu e‐mail.\nPodaj adres e‐mail i potwierdź go w swoich [[Special:Preferences|ustawieniach użytkownika]].",
        "nosuchsectiontitle": "Nie można znaleźć sekcji",
        "nosuchsectiontext": "{{GENDER:|Próbowałeś|Próbowałaś}} edytować sekcję, która nie istnieje.\nMogła zostać przeniesiona lub usunięta podczas przeglądania tej strony.",
-       "loginreqtitle": "musisz się zalogować",
+       "loginreqtitle": "Musisz się zalogować",
        "loginreqlink": "zalogować się",
        "loginreqpagetext": "Musisz $1, żeby móc przeglądać inne strony.",
        "accmailtitle": "Hasło zostało wysłane.",
        "recentchangesdays-max": "(maksymalnie $1 {{PLURAL:$1|dzień|dni}})",
        "recentchangescount": "Domyślna liczba wyświetlanych edycji:",
        "prefs-help-recentchangescount": "Uwzględnia ostatnie zmiany, historię stron i rejestry.",
-       "prefs-help-watchlist-token2": "To jest tajny klucz umożliwiający dostęp do kanału internetowego zmian w obserwowanych przez Ciebie stronach.\nKażdy, kto go zna, będzie mógł je zobaczyć, więc zachowaj go dla siebie.\n[[Special:ResetTokens|Kliknij tu, jeśli musisz go zresetować]].",
+       "prefs-help-watchlist-token2": "To jest tajny klucz umożliwiający dostęp do kanału internetowego zmian w obserwowanych przez ciebie stronach.\nKażdy, kto go zna, będzie mógł je zobaczyć, więc zachowaj go dla siebie.\n[[Special:ResetTokens|Kliknij tu, jeśli chcesz go zresetować]].",
        "savedprefs": "Twoje preferencje zostały zapisane.",
        "savedrights": "Zapisano uprawnienia {{GENDER:$1|użytkownika $1|użytkowniczki $1}}.",
        "timezonelegend": "Strefa czasowa:",
        "userrights": "Zarządzanie uprawnieniami użytkowników",
        "userrights-lookup-user": "Zarządzaj grupami użytkownika",
        "userrights-user-editname": "Wprowadź nazwę użytkownika:",
-       "editusergroup": "Edytuj grupy użytkownika",
+       "editusergroup": "Edytuj grupy {{GENDER:$1|użytkownika|użytkowniczki}}",
        "editinguser": "Zmiana uprawnień {{GENDER:$1|użytkownika|użytkowniczki}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Edytuj grupy użytkownika",
-       "saveusergroups": "Zapisz",
+       "saveusergroups": "Zapisz grupy {{GENDER:$1|użytkownika|użytkowniczki}}",
        "userrights-groupsmember": "Należy do:",
        "userrights-groupsmember-auto": "Na stałe należy do:",
        "userrights-groups-help": "Możesz zmienić przynależność tego użytkownika do podanych grup:\n* Zaznaczone pole oznacza przynależność użytkownika do danej grupy.\n* Niezaznaczone pole oznacza, że użytkownik nie należy do danej grupy.\n* Gwiazdka * informuje, że nie możesz usunąć użytkownika z grupy po dodaniu do niej lub dodać po usunięciu.",
        "right-blockemail": "Blokowanie użytkownikom możliwości wysyłania wiadomości",
        "right-hideuser": "Blokowanie użytkownika, niewidoczne publicznie",
        "right-ipblock-exempt": "Obejście blokad, automatycznych blokad i blokad zakresów adresów IP",
-       "right-proxyunbannable": "Obejście automatycznych blokad proxy",
        "right-unblockself": "Odblokowanie samego siebie",
        "right-protect": "Zmiana poziomu zabezpieczenia i edycja stron zabezpieczonych kaskadowo",
        "right-editprotected": "Edycja stron zabezpieczonych na poziomie „{{int:protect-level-sysop}}”",
        "right-managechangetags": "Tworzenie i usuwanie [[Special:Tags|znaczników]] z bazy danych",
        "right-applychangetags": "Wprowadzanie [[Special:Tags|znaczników]] wraz z własnymi zmianami",
        "right-changetags": "Dodawanie i usuwanie dowolnych [[Special:Tags|znaczników]] z poszczególnych wersji i wpisów w rejestrze",
+       "grant-group-page-interaction": "Interakcja ze stronami",
+       "grant-group-file-interaction": "Interakcja z plikami multimedialnymi",
+       "grant-group-watchlist-interaction": "Interakcja z listą obserwowanych",
+       "grant-group-email": "Wyślij e‐mail",
+       "grant-group-high-volume": "Czynności na dużą skalę",
+       "grant-group-customization": "Dostosowywanie i preferencje",
+       "grant-group-administration": "Czynności administracyjne",
+       "grant-group-other": "Różne czynności",
+       "grant-blockusers": "Blokowanie i odblokowywanie użytkowników",
+       "grant-createaccount": "Tworzenie kont",
+       "grant-createeditmovepage": "Tworzenie, edycja i przenoszenie stron",
+       "grant-delete": "Usuwanie stron, wersji stron i wpisów rejestru",
+       "grant-editinterface": "Edycja przestrzeni nazw MediaWiki oraz CSS/JavaScript użytkownika",
+       "grant-editmycssjs": "Edycja swoich plików CSS/JavaScript",
+       "grant-editmyoptions": "Edycja swoich preferencji",
+       "grant-editmywatchlist": "Edycja listy obserwowanych",
+       "grant-editpage": "Edycja istniejących stron",
+       "grant-editprotected": "Edycja stron zabezpieczonych",
+       "grant-highvolume": "Masowe edytowanie",
+       "grant-oversight": "Ukrywanie użytkowników i wersji stron",
+       "grant-protect": "Zabezpieczanie i odbezpieczanie stron",
+       "grant-rollback": "Wycofywanie zmian na stronach",
+       "grant-sendemail": "Wysyłanie e‐maili do innych użytkowników",
+       "grant-uploadeditmovefile": "Przesyłanie, zamiana i przenoszenie plików",
+       "grant-uploadfile": "Przesyłanie nowych plików",
+       "grant-basic": "Podstawowe uprawnienia",
+       "grant-viewdeleted": "Wyświetlanie usuniętych plików i stron",
+       "grant-viewmywatchlist": "Zobacz listę obserwowanych",
        "newuserlogpage": "Nowi użytkownicy",
        "newuserlogpagetext": "To jest rejestr ostatnio utworzonych kont użytkowników",
        "rightslog": "Uprawnienia",
        "upload-form-label-select-file": "Wybierz plik",
        "upload-form-label-infoform-title": "Szczegóły",
        "upload-form-label-infoform-name": "Nazwa",
+       "upload-form-label-infoform-name-tooltip": "Podaj krótką, opisującą i unikalną nazwę, która będzie służyła jako nazwa pliku. Możesz używać prostego języka i spacji. Nie dodawaj rozszerzenia pliku.",
        "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-infoform-description-tooltip": "Krótko opisz wszystko istotne, co dotyczy tej pracy.\nW przypadku zdjęcia wymień najważniejsze ujęte obiekty, sytuację lub miejsce.",
        "upload-form-label-usage-title": "Korzystanie",
        "upload-form-label-usage-filename": "Nazwa pliku",
        "foreign-structured-upload-form-label-own-work": "To moja własna praca",
        "log-title-wildcard": "Szukaj tytułów zaczynających się od tego tekstu",
        "showhideselectedlogentries": "Zmień widoczność wybranych wpisów rejestru",
        "log-edit-tags": "Edytuj znaczniki wybranych wpisów rejestru",
+       "checkbox-select": "Zaznacz: $1",
+       "checkbox-all": "Wszystko",
+       "checkbox-none": "Nic",
+       "checkbox-invert": "Odwróć",
        "allpages": "Wszystkie strony",
        "nextpage": "Następna strona ($1)",
        "prevpage": "Poprzednia strona ($1)",
        "listgrouprights-namespaceprotection-header": "Ograniczenia przestrzeni nazw",
        "listgrouprights-namespaceprotection-namespace": "Przestrzeń nazw",
        "listgrouprights-namespaceprotection-restrictedto": "Uprawnienia pozwalające użytkownikom na edytowanie",
+       "listgrants-rights": "Uprawnienie",
        "trackingcategories": "Śledzenie kategorii",
        "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",
        "wlshowhideanons": "anonimowych",
        "wlshowhidepatr": "sprawdzone edycje",
        "wlshowhidemine": "moje edycje",
+       "wlshowhidecategorization": "kategoryzację stron",
        "watchlist-options": "Opcje obserwowanych",
        "watching": "Dodaję do obserwowanych...",
        "unwatching": "Przestaję obserwować...",
        "unblock": "Odblokuj użytkownika",
        "blockip": "Zablokuj {{GENDER:$1|użytkownika|użytkowniczkę}}",
        "blockip-legend": "Zablokuj użytkownika",
-       "blockiptext": "Użyj poniższego formularza do zablokowania możliwości edycji spod określonego adresu IP lub konkretnemu użytkownikowi.\nBlokować należy jedynie po to, by zapobiec wandalizmom, zgodnie z [[{{MediaWiki:Policy-url}}|przyjętymi zasadami]].\nPodaj powód (np. umieszczając nazwy stron, na których dopuszczono się wandalizmu).",
+       "blockiptext": "Użyj poniższego formularza do zablokowania możliwości edycji spod określonego adresu IP lub konkretnemu użytkownikowi.\nBlokować należy jedynie po to, by zapobiec wandalizmom, zgodnie z [[{{MediaWiki:Policy-url}}|przyjętymi zasadami]].\nPodaj powód (np. umieszczając nazwy stron, na których dopuszczono się wandalizmu).\nMożesz zablokować zakres adresów stosując składnię [https://pl.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; najszerszym dozwolonym zakresem jest /$1 dla IPv4 i /$2 dla IPv6.",
        "ipaddressorusername": "Adres IP lub nazwa użytkownika:",
        "ipbexpiry": "Czas trwania:",
        "ipbreason": "Powód:",
        "block-log-flags-hiddenname": "nazwa użytkownika jest ukryta",
        "range_block_disabled": "Możliwość blokowania zakresu adresów IP została wyłączona.",
        "ipb_expiry_invalid": "Błędny czas wygaśnięcia blokady.",
+       "ipb_expiry_old": "Czas wygaśnięcia blokady już minął.",
        "ipb_expiry_temp": "Ukryte blokowanie nazwy użytkownika należy wykonać trwale.",
        "ipb_hide_invalid": "Ukrycie konta tego użytkownika nie jest możliwe, wykonał on więcej niż {{PLURAL:$1|jedną edycję|$1 edycje|$1 edycji}}.",
        "ipb_already_blocked": "„$1” jest już zablokowany",
        "javascripttest-pagetext-frameworks": "Wybierz jeden z następujących frameworków testowania: $1",
        "javascripttest-pagetext-skins": "Wybierz skórkę, na której chcesz uruchomić testy:",
        "javascripttest-qunit-intro": "Zobacz [$1 dokumentację testów] na mediawiki.org.",
-       "tooltip-pt-userpage": "Moja osobista strona",
+       "tooltip-pt-userpage": "{{GENDER:|Moja}} strona osobista",
        "tooltip-pt-anonuserpage": "Strona użytkownika dla adresu IP, spod którego edytujesz",
-       "tooltip-pt-mytalk": "Moja strona dyskusji",
+       "tooltip-pt-mytalk": "{{GENDER:|Moja}} strona dyskusji",
        "tooltip-pt-anontalk": "Dyskusja użytkownika dla tego adresu IP",
-       "tooltip-pt-preferences": "Moje preferencje",
-       "tooltip-pt-watchlist": "Lista stron obserwowanych przez Ciebie",
-       "tooltip-pt-mycontris": "Lista moich edycji",
+       "tooltip-pt-preferences": "{{GENDER:|Moje}} preferencje",
+       "tooltip-pt-watchlist": "Lista stron obserwowanych przeze mnie",
+       "tooltip-pt-mycontris": "Lista {{GENDER:|moich}} edycji",
        "tooltip-pt-anoncontribs": "Lista edycji wykonanych z tego adresu IP",
        "tooltip-pt-login": "Zachęcamy do zalogowania się, choć nie jest to obowiązkowe.",
        "tooltip-pt-logout": "Wyloguj",
        "tooltip-t-recentchangeslinked": "Ostatnie zmiany w stronach, do których ta strona linkuje",
        "tooltip-feed-rss": "Kanał RSS dla tej strony",
        "tooltip-feed-atom": "Kanał Atom dla tej strony",
-       "tooltip-t-contributions": "Pokaż listę edycji tego użytkownika",
-       "tooltip-t-emailuser": "Wyślij e‐mail do tego użytkownika",
+       "tooltip-t-contributions": "Pokaż listę edycji {{GENDER:$1|tego użytkownika|tej użytkowniczki}}",
+       "tooltip-t-emailuser": "Wyślij e‐mail do {{GENDER:$1|tego użytkownika|tej użytkowniczki}}",
        "tooltip-t-info": "Więcej informacji na temat tej strony",
        "tooltip-t-upload": "Prześlij pliki",
        "tooltip-t-specialpages": "Lista wszystkich specjalnych stron",
        "scarytranscludefailed-httpstatus": "[Pobranie szablonu dla $1 nie powiodło się: HTTP $2]",
        "scarytranscludetoolong": "[zbyt długi adres URL]",
        "deletedwhileediting": "'''Uwaga!''' Ta strona została usunięta po tym, jak rozpoczął{{GENDER:|eś|aś|eś(‐aś)}} jej edycję!",
-       "confirmrecreate": "[[User:$1|$1]] ([[User talk:$1|dyskusja]]) usun{{GENDER:$1|ął|ęła|ął(‐ęła)}} tę stronę po tym, jak rozpoczął{{GENDER:|eś|aś|eś(‐aś)}} jego edycję, podając jako powód usunięcia:\n: ''$2''\nCzy na pewno chcesz ją ponownie utworzyć?",
-       "confirmrecreate-noreason": "Użytkownik [[User:$1|$1]] ([[User talk:$1|dyskusja]]) usunął tę stronę po rozpoczęciu przez Ciebie edycji. Potwierdź, czy naprawdę chcesz, ponownie utworzyć tę stronę.",
+       "confirmrecreate": "{{GENDER:$1|Użytkownik|Użytkowniczka}} [[User:$1|$1]] ([[User talk:$1|dyskusja]]) {{GENDER:$1|usunął|usunęła}} tę stronę po tym, jak rozpocząłeś/rozpoczęłaś jego edycję, podając jako powód usunięcia:\n: <em>$2</em>\nPotwierdź, że na pewno chcesz odtworzyć tę stronę.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Użytkownik|Użytkowniczka}} [[User:$1|$1]] ([[User talk:$1|dyskusja]]) {{GENDER:$1|usunął|usunęła}} tę stronę po tym, jak rozpocząłeś/rozpoczęłaś jego edycję. Potwierdź, że naprawdę chcesz odtworzyć tę stronę.",
        "recreate": "Utwórz powtórnie",
        "confirm_purge_button": "Wyczyść",
        "confirm-purge-top": "Wyczyścić pamięć podręczną dla tej strony?",
        "hebrew-calendar-m9-gen": "Siwan",
        "hebrew-calendar-m11-gen": "Aw",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|dyskusja]])",
+       "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”.",
        "invalid-indicator-name": "<strong>Błąd:</strong> Atrybut stanu strony <code>name</code> nie może być pusty.",
        "pagelang-language": "Język",
        "pagelang-use-default": "Użyj domyślnego języka",
        "pagelang-select-lang": "Wybierz język",
+       "pagelang-submit": "Wyślij",
        "right-pagelang": "Zmiana języka strony",
        "action-pagelang": "zmiany języka strony",
        "log-name-pagelang": "Rejestr zmian języka",
        "log-description-pagelang": "Rejestr zmian języków przypisanych do poszczególnych stron",
        "logentry-pagelang-pagelang": "$1 {{GENDER:$2|zmienił|zmieniła}} język strony $3 z „$4” na „$5”.",
+       "default-skin-not-found": "Ups! Domyślna skórka dla Twojej wiki, zdefiniowana jako <code dir=\"ltr\">$wgDefaultSkin</code> jako <code>$1</code>, nie jest dostępna.\n\nTwoja instalacja, jak się wydaje, zawiera {{PLURAL:$4|następującą skórkę|następujące skórki}}. Zobacz [https://www.mediawiki.org/wiki/Manual:Skin_configuration/pl Podręcznik:Konfiguracja skórki] z informacjami o tym, jak {{PLURAL:$4|ją włączyć|je włączyć i wybrać domyślną}}.\n\n$2\n\n; Jeśli zainstalowałeś właśnie MediaWiki:\n: Prawdopodobnie zrobiłeś to z Git lub bezpośrednio z kodu źródłowego z wykorzystaniem innej metody. Wtedy jest to możliwe. Spróbuj zainstalować niektóre skórki z [https://www.mediawiki.org/wiki/Category:All_skins/pl folderu skórek serwisu mediawiki.org]:\n:* pobierając [https://www.mediawiki.org/wiki/Download/pl archiwum plików instalacyjnych], zawierające kilka skórek i rozszerzeń. Możesz skopiować i wkleić z niego folder <code>skins/</code>;\n:* pobierając archiwa poszczególnych skórek z [https://www.mediawiki.org/wiki/Special:SkinDistributor mediawiki.org];\n:* [https://www.mediawiki.org/wiki/Download_from_Git/pl#Korzystanie_z_Git_do_pobrania_rozszerzeń_MediaWiki Używając Git do pobrania skórek].\n: Jeśli jesteś programistą MediaWiki, nie powinno to zaszkodzić twojemu repozytorium Git.\n\n\n; Jeśli tylko aktualizowałeś MediaWiki:\n: MediaWiki w wersji 1.24 i nowszej nie zawiera automatycznie zainstalowanych skórek (zobacz [https://www.mediawiki.org/wiki/Manual:Skin_autodiscovery Manual:Skin autodiscovery]).\nMożna wstawić {{PLURAL:$5|następujący linię|następujące linie}} do <code>LocalSettings.php</code>, aby włączyć {{PLURAL:$5|zainstalowaną skórkę|wszystkie zainstalowane skórki}}: \n\n<pre dir=\"ltr\">$3</pre>\n\n; Jeśli właśnie zmodyfikowałeś <code>LocalSettings.php</code>:\n: Dokładnie sprawdź nazwy skórek pod kątem literówek.",
        "default-skin-not-found-row-enabled": "* <code>$1</code> / $2 (włączone)",
        "default-skin-not-found-row-disabled": "* <code>$1</code> / $2 ('''wyłączone''')",
        "mediastatistics": "Statystyki mediów",
        "mediastatistics-summary": "Statystyki dotyczące przesłanych typów plików. Dotyczą one tylko najnowszej wersji pliku. Starsze lub usunięte wersje plików nie są uwzględniane.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtów}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Całkowity rozmiar pliku dla tej sekcji: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtów}} ($2; $3%).",
+       "mediastatistics-allbytes": "Całkowity rozmiar pliku dla wszystkich plików: {{PLURAL:$1|$1 bajt|$1 bajty|$1 bajtów}} ($2).",
        "mediastatistics-table-mimetype": "Typ MIME",
        "mediastatistics-table-extensions": "Możliwe rozszerzenia",
        "mediastatistics-table-count": "Liczba plików",
        "mediastatistics-header-text": "Tekstowe",
        "mediastatistics-header-executable": "Pliki wykonywalne",
        "mediastatistics-header-archive": "Formaty skompresowane",
+       "mediastatistics-header-total": "Wszystkie pliki",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|końcowy przecinek został usunięty|końcowe przecinki zostały usunięte}} z JSON",
        "json-error-unknown": "Wystąpił problem z JSON. Błąd: $1",
        "json-error-depth": "Została przekroczona maksymalna głębokość stosu",
        "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ł."
+       "api-error-blacklisted": "Wybierz inny, opisowy tytuł.",
+       "randomrootpage": "Losowa strona tytułowa"
 }
index c20186c..078cc60 100644 (file)
        "resetpass_submit": "پټنوم مو وټاکۍ او بيا غونډال ته ورننوځۍ",
        "changepassword-success": "ستاسې پټنوم په برياليتوب سره بدل شو!",
        "changepassword-throttled": "تاسې څو واره هڅه کړې چې غونډال ته ورننوځۍ.\nلطفاً د بيا هڅې نه مخکې $1 شېبې تم شۍ.",
+       "botpasswords": "روباټ پټنومونه",
+       "botpasswords-label-appid": "روباټ نوم:",
+       "botpasswords-label-create": "جوړول",
+       "botpasswords-label-update": "اوسمهالول",
+       "botpasswords-label-cancel": "ناگارل",
+       "botpasswords-label-delete": "ړنگول",
        "resetpass_forbidden": "پټنومونه مو نه شي بدلېدلای",
        "resetpass-no-info": "دې مخ ته د لاسرسي لپاره بايد غونډال کې ورننوځۍ.",
        "resetpass-submit-loggedin": "پټنوم بدلول",
        "userrights": "د کارن رښتو سمبالښت",
        "userrights-lookup-user": "کارن ډلې سمبالول",
        "userrights-user-editname": "يو کارن نوم ورکړئ:",
-       "editusergroup": "کارن ډلې سمول",
-       "editinguser": "د <strong>[[User:$1|$1]]</strong> کارن رښتې بدلول $2",
+       "editusergroup": "{{GENDER:$1|کارن}} ډلې سمول",
+       "editinguser": "د <strong>[[User:$1|$1]]</strong> {{GENDER:$1|کارن}} رښتې بدلول $2",
        "userrights-editusergroup": "کارن ډلې سمول",
-       "saveusergroups": "کارن ډلې خوندي کول",
+       "saveusergroups": "{{GENDER:$1|کارن}} ډلې خوندي کول",
        "userrights-groupsmember": "غړی د:",
        "userrights-groupsmember-auto": "ضمني غړی د:",
        "userrights-groupsmember-type": "$1",
        "right-siteadmin": "توکبنسټ کولپول او پرانيستل",
        "right-sendemail": "نورو کارنانو ته برېښليک لېږل",
        "right-passwordreset": "د پټنوم بياپرځايولو برېښليکونه کتل",
+       "grant-group-email": "برېښليک لېږل",
+       "grant-group-other": "بېلابېل فعاليتونه",
+       "grant-blockusers": "په کارنانو بنديز لگول او بنديز ليرې کول",
+       "grant-createaccount": "گڼونونه جوړول",
+       "grant-createeditmovepage": "مخونه جوړول، سمول او لېږدول",
+       "grant-delete": "مخونه، بياکتنې، او يادښتليکونه ړنگول",
+       "grant-editmycssjs": "خپل کارن CSS/JavaScript سمول",
+       "grant-editmyoptions": "خپل کارن غوره توبونه سمول",
+       "grant-editmywatchlist": "خپل کتنلړ سمول",
+       "grant-editpage": "شته مخونه سمول",
+       "grant-editprotected": "ژغورلي مخونه سمول",
+       "grant-sendemail": "نورو کارنانو ته برېښليک لېږل",
+       "grant-uploadfile": "نوې دوتنې پورته کول",
+       "grant-basic": "بنسټيزې رښتې",
+       "grant-viewmywatchlist": "خپل کتنلړ کتل",
        "newuserlogpage": "د کارن-نوم د جوړېدو يادښت",
        "newuserlogpagetext": "دا د کارن-نوم د جوړېدو يادښت دی",
        "rightslog": "د کارن رښتو يادښت",
        "listduplicatedfiles": "د دوه گونو دوتنو لړليک",
        "listduplicatedfiles-entry": "[[:File:$1|$1]] [[$3|{{PLURAL:$2|يوه غبرگه دوتنه لري|$2 غبرگې دوتنې لري}}]].",
        "unusedtemplates": "ناکارېدلې کينډۍ",
+       "unusedtemplatestext": "دا مخ هغه ټولې {{ns:template}} د لړليک په توگه اوډي چې په کوم بل مخ کې نه دي کارېدلي.\nتر  دې مخکې چې کومه کينډۍ ړنگه کړئ نو د نورو مخونو سره تړنې يې وگورئ.",
        "unusedtemplateswlh": "نور تړنونه",
        "randompage": "ناټاکلی مخ",
        "randompage-nopages": "په لانديني {{PLURAL:$2|نوم-تشيال|نوم-تشيالونو}} کې هېڅ کوم مخ نشته: $1.",
        "protectedpages-unknown-performer": "ناڅرگنده کارن",
        "protectedtitles": "ژغورلي سرليکونه",
        "protectedtitles-summary": "په دې مخ کې هغه سرليکونه د لړليک په توگه راغلي چې دم مهال د جوړېدلو څخه ژغورل شوي. د ژغورلو مخونو د يو لړليک لپاره [[{{#special:ProtectedPages}}|{{int:protectedpages}}]] وگورئ.",
+       "protectedtitlesempty": "دم مهال د همدغو پاراميټرونو له مخې هېڅ کومه ليکنه نه ده ژغورل شوې.",
        "protectedtitles-submit": "ښکارېدونکي سرليکونه",
        "listusers": "کارن لړليک",
        "listusers-editsonly": "يوازې هغه کارنان چې سمونونه يې کړي ښکاره کول",
        "move": "لېږدول",
        "movethispage": "دا مخ لېږدول",
        "unusedimagestext": "دا لاندينۍ دوتنې په هېڅ کوم مخ کې نه دي ټومبېدلي. لطفاً په پام کې وساتۍ چې نور وېبځايونه به د دغو دوتنو له يو دوتنې سره يو راسن يو آر ال (URL) ولري او لا تر اوسه به دوتنه د فعالې کارېدنې سره سره دلته پرته وي.",
+       "unusedcategoriestext": "د لاندينيو مخونو په نومونو وېشنيزې شته، خو هېڅ کومه ليکنه يا وېشنيزه يې نه کاروي.",
        "notargettitle": "بې موخې",
        "nopagetitle": "داسې کوم مخ نشته",
        "nopagetext": "کوم مخ مو چې وښوده هغه نشته.",
        "logempty": "په يادښت کې ورته څه نشته.",
        "log-title-wildcard": "هغه سرليکونه پلټل چې په دې متن پيلېږي",
        "showhideselectedlogentries": "د ټاکلو يادښتونو ښکارېدنه بدلول",
+       "checkbox-select": "ټاکل: $1",
+       "checkbox-all": "ټول",
+       "checkbox-none": "هېڅ",
+       "checkbox-invert": "سرچپه کول",
        "allpages": "ټول مخونه",
        "nextpage": "بل مخ ($1)",
        "prevpage": "تېر مخ ($1)",
        "listgrouprights-namespaceprotection-header": "د نومتشيال محدوديتونه",
        "listgrouprights-namespaceprotection-namespace": "نوم-تشيال",
        "listgrouprights-namespaceprotection-restrictedto": "د کارن سمون ترسره کولو رښته(رښتې)",
+       "listgrants-rights": "رښتې",
        "trackingcategories": "موندونکې وېشنيزې",
+       "trackingcategories-summary": "په دې مخ کې هغه موندونکې وېشنيزې چې په اتوماتيک ډول د مېډياويکي ساوترې لخوا ډکېږي، د لړليک په توگه راغلي. د وېشنيزو نومونه د اړونده غونډال پيغامونو په بدلون سره چې د {{ns:8}} په نومتشيال کې دي، د بدلېدلو وړتيا لري.",
        "trackingcategories-msg": "وېشنيزه موندنه",
        "trackingcategories-name": "پيغام نوم",
        "trackingcategories-desc": "د وېشنيزې ورگډونې معيارونه",
        "databasenotlocked": "توکبنسټ نه دی تړل شوی.",
        "move-page": "$1 لېږدول",
        "move-page-legend": "مخ لېږدول",
-       "movepagetext": "د لاندينۍ فورمې په کارولو سره تاسې د يوه مخ نوم بدلولی شی، چې په همدې توگه به د يوه مخ ټول پېښليک د هغه د نوي نوم سرليک ته ولېږدېږي.\nد يوه مخ، پخوانی نوم به د نوي نوم ورگرځونکی مخ وگرځي او نوي سرليک ته به وگرځولی شي.\nهغه تړنې چې په زاړه مخ کې دي په هغو کې به هېڅ کوم بدلون را نه شي;\n[[Special:BrokenRedirects|د ماتو مخ گرځونو]] يا [[Special:DoubleRedirects|دوه ځلي مخ گرځونو]] د ستونزو د پېښېدو په خاطر ځان ډاډه کړی چې ستاسې مخ گرځونې ماتې يا دوه ځله نه وي.\nدا ستاسې پازه ده چې ځان په دې هم ډاډمن کړی چې آيا هغه تړنې کوم چې د يو مخ سره پکار دي چې وي، همداسې په پرله پسې توگه پېيلي او خپل موخن ځايونو سره اړونده دي.\n\nپه ياد مو اوسه چې يو مخ به '''هېڅکله''' و نه لېږدېږي که چېرته د پخوا نه په هماغه نوم يو مخ شتون ولري، خو که چېرته يو مخ تش وه او يا هم يوه مخ گرځونه چې پېښليک کې يې بدلون نه وي راغلی. نو دا په دې مانا ده چې تاسې کولای شی چې د يو مخ نوم بېرته هماغه پخواني نوم ته بدل کړی چې د پخوا نه يې درلوده، که چېرته تاسې تېرووځۍ نو په داسې حال کې تاسې نه شی کولای چې د يوه مخ پر سر يو څه وليکۍ.\n\n'''گواښنه!'''\nيوه نوي نوم ته د مخونو د نوم بدلون کېدای شي چې په نامتو مخونو کې بنسټيزه او نه اټکل کېدونکی بدلونونه رامېنځ ته کړي;\nمخکې له دې نه چې پرمخ ولاړ شی، لطفاُ لومړی خپل ځان په دې ډاډه کړی چې تاسې ددغې کړنې په پايلو ښه پوهېږۍ.",
-       "movepagetext-noredirectfixer": "د لاندينۍ فورمې په کارولو سره تاسې د يوه مخ نوم بدلولی شی، چې په همدې توگه به د يوه مخ ټول پېښليک د هغه د نوي نوم سرليک ته ولېږدېږي.\nد يوه مخ، پخوانی نوم به د نوي نوم ورگرځونکی مخ وگرځي او نوي سرليک ته به وگرځولی شي.\n\n[[Special:BrokenRedirects|د ماتو مخ گرځونو]] يا [[Special:DoubleRedirects|دوه ځلي مخ گرځونو]] د ستونزو د پېښېدو په خاطر ځان ډاډه کړی چې ستاسې مخ گرځونې ماتې يا دوه ځله نه وي.\nدا ستاسې پازه ده چې ځان په دې هم ډاډمن کړی چې آيا هغه تړنې کوم چې د يو مخ سره پکار دي چې وي، همداسې په پرله پسې توگه پېيلي او خپل د موخې ځايونو سره اړونده دي که نه.\n\nپه ياد مو اوسه چې يو مخ به '''هېڅکله''' و نه لېږدېږي که چېرته د پخوا نه په هماغه نوم يو بل مخ شتون ولري، خو که چېرته يو مخ تش وه او يا هم يوه مخ گرځونه چې پېښليک کې يې بدلون نه وي راغلی. نو دا په دې مانا ده چې تاسې کولای شی چې د يو مخ نوم بېرته هماغه پخواني نوم ته بدل کړی چې د پخوا نه يې درلوده، که چېرته تاسې تېرووځۍ نو په داسې حال کې تاسې نه شی کولای چې د يوه مخ پر سر يو څه وليکۍ.\n\n'''گواښنه!'''\nيوه نوي نوم ته د مخونو د نوم بدلون کېدای شي چې په نامتو مخونو کې بنسټيزه او نه اټکل کېدونکي بدلونونه رامېنځ ته کړي; مخکې له دې نه چې پرمخ ولاړ شی، لطفاُ لومړی خپل ځان په دې ډاډه کړی چې تاسې ددغې کړنې په پايلو ښه پوهېږۍ.",
+       "movepagetext": "د لاندينۍ فورمې په کارولو سره تاسې د يوه مخ نوم بدلولی شی، چې په همدې توگه به د يوه مخ ټول پېښليک د هغه د نوي نوم سرليک ته ولېږدېږي.\nد يوه مخ، پخوانی نوم به د نوي نوم ورگرځونکی مخ وگرځي او نوي سرليک ته به وگرځولی شي.\nهغه تړنې چې په زاړه مخ کې دي په هغو کې به هېڅ کوم بدلون را نه شي;\n[[Special:BrokenRedirects|د ماتو مخ گرځونو]] يا [[Special:DoubleRedirects|دوه ځلي مخ گرځونو]] د ستونزو د پېښېدو په خاطر ځان ډاډه کړی چې ستاسې مخ گرځونې ماتې يا دوه ځله نه وي.\nدا ستاسې پازه ده چې ځان په دې هم ډاډمن کړی چې آيا هغه تړنې کوم چې د يو مخ سره پکار دي چې وي، همداسې په پرله پسې توگه پېيلي او خپل موخن ځايونو سره اړونده دي.\n\nپه ياد مو اوسه چې يو مخ به <strong>هېڅکله</strong> و نه لېږدېږي که چېرته د پخوا نه په هماغه نوم يو مخ شتون ولري، خو که چېرته يو مخ تش وه او يا هم يوه مخ گرځونه چې پېښليک کې يې بدلون نه وي راغلی. نو دا په دې مانا ده چې تاسې کولای شی چې د يو مخ نوم بېرته هماغه پخواني نوم ته بدل کړی چې د پخوا نه يې درلوده، که چېرته تاسې تېرووځۍ نو په داسې حال کې تاسې نه شی کولای چې د يوه مخ پر سر يو څه وليکۍ.\n\n<strong>گواښنه:</strong>\nيوه نوي نوم ته د مخونو د نوم بدلون کېدای شي چې په نامتو مخونو کې بنسټيزه او نه اټکل کېدونکی بدلونونه رامېنځ ته کړي;\nمخکې له دې نه چې پرمخ ولاړ شی، لطفاُ لومړی خپل ځان په دې ډاډه کړی چې تاسې ددغې کړنې په پايلو ښه پوهېږۍ.",
+       "movepagetext-noredirectfixer": "د لاندينۍ فورمې په کارولو سره تاسې د يوه مخ نوم بدلولی شی، چې په همدې توگه به د يوه مخ ټول پېښليک د هغه د نوي نوم سرليک ته ولېږدېږي.\nد يوه مخ، پخوانی نوم به د نوي نوم ورگرځونکی مخ وگرځي او نوي سرليک ته به وگرځولی شي.\n\n[[Special:BrokenRedirects|د ماتو مخ گرځونو]] يا [[Special:DoubleRedirects|دوه ځلي مخ گرځونو]] د ستونزو د پېښېدو په خاطر ځان ډاډه کړی چې ستاسې مخ گرځونې ماتې يا دوه ځله نه وي.\nدا ستاسې پازه ده چې ځان په دې هم ډاډمن کړی چې آيا هغه تړنې کوم چې د يو مخ سره پکار دي چې وي، همداسې په پرله پسې توگه پېيلي او خپل د موخې ځايونو سره اړونده دي که نه.\n\nپه ياد مو اوسه چې يو مخ به <strong>هېڅکله</strong> و نه لېږدېږي که چېرته د پخوا نه په هماغه نوم يو بل مخ شتون ولري، خو که چېرته يو مخ تش وه او يا هم يوه مخ گرځونه چې پېښليک کې يې بدلون نه وي راغلی. نو دا په دې مانا ده چې تاسې کولای شی چې د يو مخ نوم بېرته هماغه پخواني نوم ته بدل کړی چې د پخوا نه يې درلوده، که چېرته تاسې تېرووځۍ نو په داسې حال کې تاسې نه شی کولای چې د يوه مخ پر سر يو څه وليکۍ.\n\n<strong>پاملرنه:</strong>\nيوه نوي نوم ته د مخونو د نوم بدلون کېدای شي چې په نامتو مخونو کې بنسټيزه او نه اټکل کېدونکي بدلونونه رامېنځ ته کړي; مخکې له دې نه چې پرمخ ولاړ شی، لطفاُ لومړی خپل ځان په دې ډاډه کړی چې تاسې ددغې کړنې په پايلو ښه پوهېږۍ.",
        "movepagetalktext": "همدې مخ ته اړونده د خبرواترو مخ هم په اتوماتيک ډول لېږدول کېږي '''خو که چېرته:'''\n*په نوي نوم د پخوا نه د خبرواترو يو مخ شتون ولري، او يا هم\n*تاسې ته لاندې ورکړ شوی څلورڅنډی په نښه شوی وي.\n\nنو په هغه وخت کې پکار ده چې د خبرواترو د مخ لېږدونه او د نوي مخ سره د يوځای کولو کړنه په لاسي توگه ترسره کړی.",
        "moveuserpage-warning": "'''گواښنه:''' تاسې د يو کارن مخ د لېږدولو په حال کې ياست. لطفاً دا مه هېروۍ چې يوازې همدا مخ به ولېږدول شي او د کارن نوم به ''نه'' بدلېږي.",
        "movenologintext": "ددې لپاره چې يو مخ ولېږدوی، نو تاسې بايد يو ثبت شوی کارن او غونډال کې [[Special:UserLogin|ننوتي]] اوسۍ.",
        "movenosubpage": "دا مخ کوم څېرمه مخونه نه لري.",
        "movereason": "سبب:",
        "revertmove": "په څټ گرځول",
-       "delete_and_move_text": "== د ړنگولو اړتيا ==\nد \"[[:$1]]\" په نوم مخ له پخوا څخه شته.\nآيا د دې په ړنگولو سره لېږدولو ته لاره هوارول غواړۍ؟",
+       "delete_and_move_text": "د \"[[:$1]]\" په نوم مخ له پخوا څخه شته.\nآيا د دې په ړنگولو سره لېږدولو ته لاره هوارول غواړئ؟",
        "delete_and_move_confirm": "هو, دا مخ ړنگ کړه",
        "immobile-source-page": "دا مخ نه لېږدېدنونکی دی",
        "imageinvalidfilename": "د موخنې دوتنې نوم سم نه دی",
        "move-leave-redirect": "يو ورگرځونکی مخ پر ځای پرېښودل",
-       "move-over-sharedrepo": "== دوتنه شته ==\nد [[:$1]] دوتنه په يو گډ زېرمتون کې شته. دې نوم ته د يوې دوتنې لېږدون به د گډې دوتنې د باطلېدلو سبب شي.",
+       "move-over-sharedrepo": "د [[:$1]] دوتنه په يو گډ زېرمتون کې شته. نوي نوم ته د يوې دوتنې لېږدون به د شريکې دوتنې د باطلېدلو سبب وگرځي.",
        "export": "مخونه صادرول",
        "exportall": "ټول مخونه صادرول",
        "export-submit": "صادرول",
        "javascripttest": "د جاوا سکرېپټ آزمېښت",
        "javascripttest-pagetext-unknownaction": "ناڅرگنده کړنه \"$1\".",
        "javascripttest-pagetext-skins": "د آزمېښتونو د پلي کولو لپاره يوه پوښۍ وټاکئ:",
-       "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-t-recentchangeslinked": "له دې مخ سره د تړل شويو مخونو وروستي بدلونونه",
        "tooltip-feed-rss": "د همدې مخ د آر اس اس کتنه",
        "tooltip-feed-atom": "د دې مخ د اټوم کتنې",
-       "tooltip-t-contributions": "د دې کارن د ونډو لړليک کتل",
-       "tooltip-t-emailuser": "دې کارن ته يو برېښليک لېږل",
+       "tooltip-t-contributions": "{{GENDER:$1|د دې کارن}} د ونډو يو لړليک",
+       "tooltip-t-emailuser": "{{GENDER:$1|دې کارن}} ته يو برېښليک لېږل",
        "tooltip-t-info": "د دې مخ اړونده نور مالومات",
        "tooltip-t-upload": "دوتنې پورته کول",
        "tooltip-t-specialpages": "د ټولو ځانگړو پاڼو لړليک",
        "exif-giffilecomment": "د GIF دوتنې تبصره",
        "exif-copyrighted-true": "په رښتو سمبال",
        "exif-copyrighted-false": "د خپراوي د رښتو دريځ نه دی ټاکل شوی",
+       "exif-photometricinterpretation-1": "تور او سپين (تور 0 دی)",
        "exif-unknowndate": "ناڅرگنده نېټه",
        "exif-orientation-1": "نورمال",
        "exif-componentsconfiguration-0": "نشته دی",
        "hebrew-calendar-m6": "آدار",
        "hebrew-calendar-m10-gen": "تموز",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|خبرې اترې]])",
+       "timezone-local": "سيمه ايز",
        "duplicate-defaultsort": "'''گواښنه:'''د \"$2\" تلواليزه اوډون تڼۍ تر دې پخوا ټاکلې تلواليزه اوډون تڼۍ \"$1\" پر ځای چارنه کېږي.",
        "version": "بڼه",
        "version-extensions": "لگېدلي شاتاړي",
        "pagelang-language": "ژبه",
        "pagelang-use-default": "تلواليزه ژبه کارول",
        "pagelang-select-lang": "ژبه ټاکل",
+       "pagelang-submit": "سپارل",
        "right-pagelang": "د مخ ژبه بدلول",
        "action-pagelang": "د مخ ژبه بدلول",
        "log-name-pagelang": "ژبيادښت بدلول",
        "mediastatistics-header-office": "دفتر",
        "mediastatistics-header-text": "متني",
        "mediastatistics-header-executable": "اجرايي",
+       "mediastatistics-header-total": "ټولې دوتنې",
        "headline-anchor-title": "دې برخې ته تړنه",
        "special-characters-group-latin": "لاتين",
        "special-characters-group-latinextended": "غځېدلی لاتين",
        "special-characters-group-thai": "تايلنډي",
        "special-characters-group-lao": "لاوي",
        "special-characters-group-khmer": "خمري",
+       "mw-widgets-dateinput-no-date": "کومه نېټه نه ده ټاکل شوې",
        "mw-widgets-dateinput-placeholder-day": "کککک-م م-و و",
        "mw-widgets-dateinput-placeholder-month": "کککک-م م",
        "mw-widgets-titleinput-description-new-page": "تر اوسه پورې دا مخ نشته",
-       "mw-widgets-titleinput-description-redirect": "$1 ته ورگرځېدنه"
+       "mw-widgets-titleinput-description-redirect": "$1 ته ورگرځېدنه",
+       "randomrootpage": "د ناټاکلې ريښې مخ"
 }
index 5ab5f7c..a9e22e1 100644 (file)
@@ -86,7 +86,9 @@
                        "Walesson",
                        "Rhcastilhos",
                        "Claudio Emanuel Weiler",
-                       "Almondega"
+                       "Almondega",
+                       "Eduardo Addad de Oliveira",
+                       "Raphaelras"
                ]
        },
        "tog-underline": "Sublinhar links:",
        "categoryviewer-pagedlinks": "($1) ($2)",
        "about": "Sobre",
        "article": "Página de conteúdo",
-       "newwindow": "(abre em uma nova janela)",
+       "newwindow": "(abre numa nova janela)",
        "cancel": "Cancelar",
        "moredotdotdot": "Mais...",
        "morenotlisted": "Esta lista não está completa.",
        "mypage": "Página",
        "mytalk": "Discussão",
-       "anontalk": "Discussão para este endereço IP",
+       "anontalk": "Discussão",
        "navigation": "Navegação",
        "and": "&#32;e",
        "qbfind": "Procurar",
        "virus-scanfailed": "a verificação falhou (código $1)",
        "virus-unknownscanner": "antivírus desconhecido:",
        "logouttext": "'''Agora você encontra-se desautenticado.'''\n\nNote que algumas páginas podem continuar sendo exibidas como se você ainda estivesse autenticado até que você limpe a ''cache'' do seu navegador.",
+       "cannotlogoutnow-title": "Não é possível encerrar a sessão agora",
        "welcomeuser": "Bem-vindo, $1!",
        "welcomecreation-msg": "A sua conta foi criada.\nNão se esqueça de personalizar as suas [[Special:Preferences|preferências no wiki {{SITENAME}}]].",
        "yourname": "Nome de usuário:",
        "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",
        "yourdomainname": "Seu domínio:",
        "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.",
        "resetpass_submit": "Definir senha e entrar",
        "changepassword-success": "Sua senha foi alterada com sucesso!",
        "changepassword-throttled": "Você realizou demasiadas tentativas de se registrar.\nPor favor, aguarde $1 antes de tentar novamente.",
+       "botpasswords-label-appid": "Nome do robô:",
+       "botpasswords-label-create": "Criar",
+       "botpasswords-label-update": "Atualizar",
+       "botpasswords-label-cancel": "Cancelar",
+       "botpasswords-label-delete": "Apagar",
+       "botpasswords-label-resetpassword": "Redefinir a sua senha",
+       "botpasswords-label-grants": "Permissões aplicáveis",
+       "botpasswords-label-restrictions": "Restrições de uso:",
+       "botpasswords-label-grants-column": "Concedido",
        "resetpass_forbidden": "As senhas não podem ser alteradas",
        "resetpass-no-info": "Você precisa estar autenticado para acessar esta página diretamente.",
        "resetpass-submit-loggedin": "Alterar senha",
        "passwordreset-emailtext-user": "O usuário $1 da {{SITENAME}} pediu a recuperação dos detalhes da sua conta na {{SITENAME}} ($4). {{PLURAL:$3|A seguinte conta está associada|As seguintes contas estão associadas}} a este e-mail:\n\n$2\n\n{{PLURAL:$3|Esta senha temporária irá|Estas senhas temporárias irão}} expirar dentro de {{PLURAL:$5|um dia|$5 dias}}. Deve autenticar-se e escolher uma senha nova agora. Se este pedido não foi feito por si, ou se entretanto se recordou da sua senha original e já não deseja alterá-la, pode ignorar esta mensagem e continuar a usar a senha antiga.",
        "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",
        "changeemail": "Alterar ou remover endereço de email",
        "extlink_tip": "Link externo (lembre-se do prefixo http://)",
        "headline_sample": "Conteúdo do cabeçalho",
        "headline_tip": "Seção de nível 2",
-       "nowiki_sample": "Inserir texto não-formatado aqui",
+       "nowiki_sample": "Inserir texto não formatado aqui",
        "nowiki_tip": "Ignorar a formatação wiki",
        "image_sample": "Exemplo.jpg",
        "image_tip": "Arquivo embutido",
        "media_tip": "Link para o arquivo",
        "sig_tip": "Sua assinatura, com hora e data",
        "hr_tip": "Linha horizontal (use de forma moderada)",
-       "summary": "Sumário:",
+       "summary": "Resumo da edição:",
        "subject": "Assunto:",
        "minoredit": "Marcar como edição menor",
        "watchthis": "Vigiar esta página",
        "selfredirect": "<strong>Aviso:</strong> Você esta redirecionando esta pagina para ela mesmo. Você pode ter especificado o caminho errado para redirecionar, ou você pode estar editando a pagina errada. Se você clicar \"{{int:savearticle}}\" novamente, o redirecionamento será criado de qualquer modo.",
        "missingcommenttext": "Por favor, introduzida um comentário abaixo.",
        "missingcommentheader": "'''Lembrete:''' Você não introduziu um assunto/título para este comentário.\nSe você clicar novamente \"{{int:savearticle}}\", a sua edição será salva sem um assunto/título.",
-       "summary-preview": "Previsão de sumário:",
+       "summary-preview": "Previsão do resumo:",
        "subject-preview": "Previsão do assunto/título:",
        "previewerrortext": "Ocorreu um erro ao tentar pré-visualizar suas alterações.",
        "blockedtitle": "O usuário está bloqueado",
        "templatesusedpreview": "{{PLURAL:$1|Predefinição usada|Predefinições usadas}} nesta previsão:",
        "templatesusedsection": "{{PLURAL:$1|Predefinição utilizada|Predefinições utilizadas}} nesta seção:",
        "template-protected": "(protegida)",
-       "template-semiprotected": "(semi-protegida)",
+       "template-semiprotected": "(semiprotegida)",
        "hiddencategories": "Esta página pertence a {{PLURAL:$1|uma categoria oculta|$1 categorias ocultas}}:",
        "edittools": "<!-- O texto aqui disponibilizado será exibido abaixo dos formulários de edição e de envio de arquivos. -->",
        "nocreatetext": "{{SITENAME}} tem restringida a habilidade de criar novas páginas.\nVolte à tela anterior e edite uma página já existente, ou [[Special:UserLogin|autentique-se ou crie uma conta]].",
        "history-feed-empty": "A página requisitada não existe.\nPoderá ter sido eliminada do wiki ou renomeada.\nTente [[Special:Search|pesquisar no wiki]] por páginas relevantes.",
        "history-edit-tags": "Editar etiquetas das revisões selecionadas",
        "rev-deleted-comment": "(resumo da edição suprimido)",
-       "rev-deleted-user": "(nome de usuário removido)",
+       "rev-deleted-user": "(nome de usuário(a) removido)",
        "rev-deleted-event": "(registros de detalhes eliminados)",
-       "rev-deleted-user-contribs": "[nome de usuário ou endereço de IP eliminado - edição ocultada das contribuições]",
+       "rev-deleted-user-contribs": "[nome de usuário(a) ou endereço de IP eliminado – edição ocultada das contribuições]",
        "rev-deleted-text-permission": "Esta revisão desta página foi '''eliminada'''.\nPodem existir mais detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminações].",
        "rev-suppressed-text-permission": "A revisão desta página foi '''eliminada'''.\nVocê pode visualizá-la; podem existir mais detalhes no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registro de eliminação].",
        "rev-deleted-text-unhide": "Esta revisão desta página foi '''removida'''.\nPoderá haver detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registro de eliminação].\nVocê ainda pode [$1 ver esta revisão] se deseja prosseguir.",
        "revdelete-hide-text": "Texto de revisão",
        "revdelete-hide-image": "Ocultar conteúdos do arquivo",
        "revdelete-hide-name": "Ocultar destino e parâmetros",
-       "revdelete-hide-comment": "Sumário de edição",
+       "revdelete-hide-comment": "Resumo da edição",
        "revdelete-hide-user": "Nome de usuário/endereço IP",
        "revdelete-hide-restricted": "Suprimir dados de administradores assim como de outros",
        "revdelete-radio-same": "(não alterar)",
        "showingresultsinrange": "Apresenta-se abaixo {{PLURAL:$1|<strong>1</strong> resultado|até <strong>$1</strong> resultados}} no intervalo #<strong>$2</strong> a #<strong>$3</strong>.",
        "search-showingresults": "{{PLURAL:$4|Resultado <strong>$1</strong> de <strong>$3</strong>|Resultados <strong>$1 - $2</strong> de <strong>$3</strong>}}",
        "search-nonefound": "Não há resultados que correspondam à consulta.",
+       "search-nonefound-thiswiki": "Não há resultados que correspondam à consulta.",
        "powersearch-legend": "Pesquisa avançada",
        "powersearch-ns": "Pesquisar nos espaços nominais:",
        "powersearch-togglelabel": "Selecionar:",
        "prefs-edits": "Número de edições:",
        "prefsnologintext2": "Por favor clique $1 para alterar suas preferências",
        "prefs-skin": "Tema",
-       "skin-preview": "Pré-visualização",
+       "skin-preview": "prever",
        "datedefault": "Sem preferência",
        "prefs-labs": "Características de laboratório",
        "prefs-user-pages": "Páginas de usuário",
        "prefs-files": "Arquivos",
        "prefs-custom-css": "CSS personalizada",
        "prefs-custom-js": "JS personalizado",
-       "prefs-common-css-js": "CSS/JS partilhado por todos os temas:",
+       "prefs-common-css-js": "CSS/JS compartilhado por todos os temas:",
        "prefs-reset-intro": "Você pode usar esta página para restaurar as suas preferências para os valores predefinidos do sítio.\nEsta ação não pode ser desfeita.",
        "prefs-emailconfirm-label": "Confirmação do e-mail:",
        "youremail": "Seu e-mail:",
-       "username": "Nome de {{GENDER:$1|usuário|usuária}}:",
+       "username": "Nome de {{GENDER:$1|usuário|usuária|usuário(a)}}:",
        "prefs-memberingroups": "{{GENDER:$2|Membro}} {{PLURAL:$1|do grupo|dos grupos}}:",
        "prefs-registration": "Hora de registro:",
        "yourrealname": "Nome verdadeiro:",
        "prefs-help-prefershttps": "Esta preferência terá efeito no seu próximo início de sessão.",
        "prefswarning-warning": "Você fez alterações em suas preferências, que não foram salvas ainda. \nSe você sair desta página sem clicar em \"$1\" suas preferências não serão atualizado.",
        "prefs-tabs-navigation-hint": "Dica: Você pode usar as teclas de seta esquerda e direita para navegar entre as abas da lista de abas.",
-       "userrights": "Gestão de privilégios de usuários",
+       "userrights": "Gestão de privilégios {{GENDER:{{BASEPAGENAME}}|do usuário|da usuária|de usuário(a)}}",
        "userrights-lookup-user": "Administrar grupos de usuários",
        "userrights-user-editname": "Forneça um nome de usuário:",
-       "editusergroup": "Editar grupos de usuários",
+       "editusergroup": "Editar {{GENDER:$1|usuário}} grupos",
        "editinguser": "Modificando privilégios d{{GENDER:$1|o usuário|a usuária|o(a) usuário(a)}} <strong>[[User:$1|$1]]</Strong> $2",
        "userrights-editusergroup": "Editar grupos do usuário",
-       "saveusergroups": "Salvar grupos do usuário",
+       "saveusergroups": "Salvar grupos de{{GENDER:$1|usuário}}",
        "userrights-groupsmember": "Membro de:",
        "userrights-groupsmember-auto": "Membro implícito de:",
-       "userrights-groups-help": "É possível alterar os grupos em que este usuário se encontra:\n* Uma caixa de seleção selecionada significa que o usuário se encontra no grupo.\n* Uma caixa de seleção desselecionada significa que o usuário não se encontra no grupo.\n* Um * indica que não pode remover o grupo depois de o adicionar, ou vice-versa.",
+       "userrights-groups-help": "É possível alterar os grupos em que {{GENDER:$1|este usuário|esta usuária|este(a) usuário(a)}} se encontra:\n* Uma caixa de seleção selecionada significa que {{GENDER:$1|o usuário|a usuária|o(a) usuário(a)}} encontra-se no grupo.\n* Uma caixa de seleção não selecionada significa que {{GENDER:$1|o usuário|a usuária|o(a) usuário(a)}} não se encontra no grupo.\n* Um * indica que não pode remover o grupo depois de o adicionar, ou vice-versa.",
        "userrights-reason": "Motivo:",
        "userrights-no-interwiki": "Você não tem permissão para alterar privilégios de usuários em outros wikis.",
        "userrights-nodatabase": "O banco de dados $1 não existe ou não é um banco de dados local.",
        "group-bot-member": "robô",
        "group-sysop-member": "{{GENDER:$1|administrador|administradora|administrador(a)}}",
        "group-bureaucrat-member": "burocrata",
-       "group-suppress-member": "{{GENDER:$1|supressor|supressora}}",
+       "group-suppress-member": "{{GENDER:$1|supressor|supressora|supressor(a)}}",
        "grouppage-user": "{{ns:project}}:Usuários",
        "grouppage-autoconfirmed": "{{ns:project}}:Auto-confirmados",
        "grouppage-bot": "{{ns:project}}:Robôs",
        "right-createpage": "Criar páginas (que não sejam páginas de discussão)",
        "right-createtalk": "Criar páginas de discussão",
        "right-createaccount": "Criar novas contas de usuário",
+       "right-autocreateaccount": "Iniciar a sessão automaticamente com uma conta de usuário externa",
        "right-minoredit": "Marcar edições como menores",
        "right-move": "Mover páginas",
        "right-move-subpages": "Mover páginas com as suas subpáginas",
        "right-blockemail": "Impedir um usuário de enviar email",
        "right-hideuser": "Bloquear um nome de usuário, escondendo-o do público",
        "right-ipblock-exempt": "Contornar bloqueios de IP, automáticos e de intervalo",
-       "right-proxyunbannable": "Contornar bloqueios automáticos de proxies",
        "right-unblockself": "Desbloquear a si mesmo",
        "right-protect": "Mudar níveis de proteção e editar páginas protegidas em cascata",
        "right-editprotected": "Editar páginas protegidas como \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Criar e apagar [[Special:Tags|tags]] na base de dados",
        "right-applychangetags": "Aplicar [[Special:Tags|etiquetas]] juntamente com as alterações de alguém",
        "right-changetags": "Adicionar e remover [[Special:Tags|etiquetas]] arbitrárias em revisões e ''logs'' individuais",
+       "grant-group-page-interaction": "Interagir com páginas",
+       "grant-group-watchlist-interaction": "Interagir com sua lista de páginas vigiadas",
+       "grant-group-email": "Enviar e-mail",
+       "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-other": "Atividade diversa",
+       "grant-blockusers": "Bloquear e desbloquear usuários",
+       "grant-createaccount": "Criar contas",
+       "grant-createeditmovepage": "Criar, editar e mover páginas",
+       "grant-delete": "Excluir páginas, revisões e entradas de registro",
+       "grant-editmyoptions": "Editar suas preferências de usuário",
+       "grant-editmywatchlist": "Editar sua lista de páginas vigiadas",
+       "grant-editpage": "Editar páginas existentes",
+       "grant-editprotected": "Editar páginas protegidas",
+       "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-protect": "Proteger e desproteger páginas",
+       "grant-rollback": "Reverter alterações nas páginas",
+       "grant-sendemail": "Enviar e-mail a outros usuários",
+       "grant-uploadeditmovefile": "Enviar, substituir e mover arquivos",
+       "grant-uploadfile": "Enviar novos arquivos",
+       "grant-basic": "Direitos básicos",
+       "grant-viewdeleted": "Ver páginas e arquivos excluídos",
+       "grant-viewmywatchlist": "Veja sua lista de páginas vigiadas",
        "newuserlogpage": "Registro de criação de usuários",
        "newuserlogpagetext": "Este é um registro de novas contas de usuário",
-       "rightslog": "Registro de privilégios de usuário",
+       "rightslog": "Registro de privilégios de 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-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-minoredit": "marcar esta edição como uma edição menor",
        "action-move": "mover esta página",
        "recentchanges-legend-heading": "'''Legenda''':",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (veja também a [[Special:NewPages|lista de páginas novas]])",
        "recentchanges-legend-plusminus": "(''±123'')",
+       "recentchanges-submit": "Exibir",
        "rcnotefrom": "Abaixo {{PLURAL:$5|é a mudança|são as mudanças}} desde <strong>$3, $4</strong> (up to <strong>$1</strong> shown).",
        "rclistfrom": "Mostrar as novas alterações a partir das $2 de $3",
        "rcshowhideminor": "$1 edições menores",
        "rcshowhidemine": "$1 minhas edições",
        "rcshowhidemine-show": "Exibir",
        "rcshowhidemine-hide": "Ocultar",
+       "rcshowhidecategorization-show": "Exibir",
+       "rcshowhidecategorization-hide": "Esconder",
        "rclinks": "Exibir as $1 alterações recentes feitas nos últimos $2 dias<br />$3",
        "diff": "dif",
        "hist": "his",
        "recentchangeslinked-title": "Alterações relacionadas com \"$1\"",
        "recentchangeslinked-summary": "Esta página lista alterações feitas recentemente em páginas com links a uma em específico (ou de membros de uma categoria especificada).\nPáginas de sua [[Special:Watchlist|lista de páginas vigiadas]] são exibidas em '''negrito'''.",
        "recentchangeslinked-page": "Nome da página:",
-       "recentchangeslinked-to": "Visualizar as alterações nas páginas vinculadas à página especificada ao invés disso",
+       "recentchangeslinked-to": "Inversamente, mostrar mudanças nas páginas que contêm ligações para esta",
        "recentchanges-page-added-to-category": "[[:$1]]adicionada à categoria",
+       "autochange-username": "Alteração automática do MediaWiki",
        "upload": "Enviar arquivo",
        "uploadbtn": "Enviar arquivo",
        "reuploaddesc": "Cancelar o envio e retornar ao formulário de upload",
        "upload-form-label-infoform-description": "Descrição",
        "upload-form-label-usage-title": "Uso",
        "upload-form-label-usage-filename": "Nome do arquivo",
+       "foreign-structured-upload-form-label-own-work": "Isto é o meu próprio trabalho",
        "foreign-structured-upload-form-label-infoform-categories": "Categorias",
        "foreign-structured-upload-form-label-infoform-date": "Data",
+       "foreign-structured-upload-form-label-not-own-work-local-local": "Você pode também querer tentar [[Special:Upload|the default upload page]]",
+       "foreign-structured-upload-form-2-label-useful": "Deve ser <strong>educativo e útil</strong> para ensinar a outros.",
+       "foreign-structured-upload-form-3-label-yes": "Sim",
+       "foreign-structured-upload-form-3-label-no": "Não",
        "backend-fail-stream": "Não foi possível transmitir o arquivo  $1.",
        "backend-fail-backup": "Não foi possível fazer backup do arquivo  $1 .",
        "backend-fail-notexists": "O arquivo $1 não existe.",
        "listfiles_search_for": "Pesquisar por nome de mídia:",
        "listfiles-userdoesnotexist": "A conta de usuário \"$1\" não está registrada.",
        "imgfile": "arquivo",
-       "listfiles": "Lista de arquivo",
+       "listfiles": "Lista de arquivos",
        "listfiles_thumb": "Miniatura",
        "listfiles_date": "Data",
        "listfiles_name": "Nome",
        "filehist-deleteone": "eliminar",
        "filehist-revert": "restaurar",
        "filehist-current": "atual",
-       "filehist-datetime": "Data/Horário",
+       "filehist-datetime": "Data e horário",
        "filehist-thumb": "Miniatura",
        "filehist-thumbtext": "Miniatura da versão das $1",
        "filehist-nothumb": "Miniatura indisponível",
        "mostrevisions": "Páginas de conteúdo com mais revisões",
        "prefixindex": "Todas as páginas com prefixo",
        "prefixindex-namespace": "Todas as páginas com prefixo (espaço nominal $1)",
+       "prefixindex-submit": "Exibir",
        "prefixindex-strip": "Remover prefixo",
        "shortpages": "Páginas curtas",
        "longpages": "Páginas longas",
        "protectedpages-performer": "Usuário que protegeu",
        "protectedpages-params": "Parâmetros de proteção.",
        "protectedpages-reason": "Motivo",
+       "protectedpages-submit": "Exibir páginas",
        "protectedpages-unknown-timestamp": "Desconhecido",
        "protectedpages-unknown-performer": "Usuário desconhecido",
        "protectedtitles": "Títulos protegidos",
        "protectedtitles-summary": "Esta página lista títulos cuja criação está impossibilitada. Para ver uma lista de páginas protegidas, consulte [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Neste momento, nenhum dos títulos está protegido com estes parâmetros.",
+       "protectedtitles-submit": "Mostrar títulos",
        "listusers": "Lista de usuários",
        "listusers-editsonly": "Mostrar apenas usuários com edições",
        "listusers-creationsort": "Ordenar por data de criação",
        "listusers-desc": "Listar em ordem decrescente",
        "usereditcount": "$1 {{PLURAL:$1|edição|edições}}",
-       "usercreated": "{{GENDER:$3|Criado|Criada}} em $1 às $2",
+       "usercreated": "{{GENDER:$3|criado|criada|criado(a)}} em $1 às $2",
        "newpages": "Páginas novas",
+       "newpages-submit": "Exibir",
        "newpages-username": "Nome de usuário:",
        "ancientpages": "Páginas mais antigas",
        "move": "Mover",
        "specialloguserlabel": "Executor:",
        "speciallogtitlelabel": "Alvo (título da página ou {{ns:user}}:'nomedeusuário' para usuários):",
        "log": "Registros",
+       "logeventslist-submit": "Exibir",
        "all-logs-page": "Todos os registros públicos",
        "alllogstext": "Exibição combinada de todos registros disponíveis para o {{SITENAME}}.\nVocê pode diminuir a lista escolhendo um tipo de registro, um nome de usuário (sensível a maiúsculas e minúsculas), ou uma página afetada (também sensível a maiúsculas e minúsculas).",
        "logempty": "Nenhum item correspondente no registro.",
        "log-title-wildcard": "Procurar por títulos que sejam iniciados com o seguinte texto",
        "showhideselectedlogentries": "Exibir/ocultar os itens de registros selecionados",
        "log-edit-tags": "Editar etiquetas das entradas selecionadas",
+       "checkbox-select": "Selecionar: $1",
+       "checkbox-all": "Tudo",
+       "checkbox-none": "Nenhum",
+       "checkbox-invert": "Inverter",
        "allpages": "Todas as páginas",
        "nextpage": "Próxima página ($1)",
        "prevpage": "Página anterior ($1)",
        "cachedspecial-viewing-cached-ts": "Você está visualizando uma versão de cache desta página que pode estar desatualizada.",
        "cachedspecial-refresh-now": "Ver a mais recente.",
        "categories": "Categorias",
+       "categories-submit": "Exibir",
        "categoriespagetext": "{{PLURAL:$1|A seguinte categoria contém|As seguintes contém}} páginas ou mídia.\n[[Special:UnusedCategories|Categorias não utilizadas]] não são mostradas aqui.\nVeja também [[Special:WantedCategories|categorias pedidas]].",
        "categoriesfrom": "Listar categorias começando por:",
        "special-categories-sort-count": "ordenar por contagem",
        "special-categories-sort-abc": "ordenar alfabeticamente",
        "deletedcontributions": "Edições eliminadas",
-       "deletedcontributions-title": "Contribuições de usuário eliminadas",
+       "deletedcontributions-title": "Contribuições eliminadas",
        "sp-deletedcontributions-contribs": "contribuições",
        "linksearch": "Pesquisa de links externos",
        "linksearch-pat": "Procurar padrão:",
        "listusersfrom": "Mostrar usuários começando em:",
        "listusers-submit": "Exibir",
        "listusers-noresult": "Não foram encontrados usuários para a forma pesquisada.",
-       "listusers-blocked": "({{GENDER:$1|bloqueado|bloqueada}})",
+       "listusers-blocked": "({{GENDER:$1|bloqueado|bloqueada|bloqueado(a)}})",
        "activeusers": "Lista de usuários ativos",
        "activeusers-intro": "Esta é uma lista de usuários com algum tipo de atividade nos últimos $1 {{PLURAL:$1|dia|dias}}.",
        "activeusers-count": "$1 {{PLURAL:$1|ação|ações}} {{PLURAL:$3|no último dia|nos últimos $3 dias}}",
        "activeusers-hidebots": "Esconder robôs",
        "activeusers-hidesysops": "Esconder administradores",
        "activeusers-noresult": "Nenhum usuário encontrado.",
-       "listgrouprights": "Privilégios de grupo de usuários",
+       "activeusers-submit": "Mostrar usuários ativos",
+       "listgrouprights": "Privilégios de grupos de usuários",
        "listgrouprights-summary": "O que segue é uma lista dos grupos de usuários definidos neste wiki, com os seus privilégios de acessos associados.\nPode haver [[{{MediaWiki:Listgrouprights-helppage}}|informações adicionais]] sobre privilégios individuais.",
        "listgrouprights-key": "Legenda:\n* <span class=\"listgrouprights-granted\">Privilégio concedido</span>\n* <span class=\"listgrouprights-revoked\">Privilégio revogado</span>",
        "listgrouprights-group": "Grupo",
        "listgrouprights-namespaceprotection-header": "Restrições de namespace",
        "listgrouprights-namespaceprotection-namespace": "Namespace",
        "listgrouprights-namespaceprotection-restrictedto": "Direito(s) permitindo edições do usuário",
+       "listgrants": "Permissões",
+       "listgrants-grant": "Permissão",
+       "listgrants-rights": "Direitos",
        "trackingcategories": "Categorias de rastreamento",
        "trackingcategories-summary": "Esta página lista categorias de monitoramento que são preenchidas automaticamente pelo software MediaWiki. Seus nomes podem ser alterados através da alteração das mensagens de sistema relevantes no namespace {{ns: 8}}.",
        "trackingcategories-msg": "Categoria de monitoramento",
        "mailnologin": "Nenhum endereço de envio",
        "mailnologintext": "Necessita de estar [[Special:UserLogin|autenticado]] e de possuir um endereço de e-mail válido nas suas [[Special:Preferences|preferências]] para poder enviar um e-mail a outros usuários.",
        "emailuser": "Enviar-lhe um e-mail",
-       "emailuser-title-target": "Enviar e-mail para {{GENDER:$1|este usuário|esta usuária}}",
+       "emailuser-title-target": "Enviar e-mail para {{GENDER:$1|este usuário|esta usuária|este(a) usuário(a)}}",
        "emailuser-title-notarget": "Enviar e-mail",
        "emailpagetext": "Você pode usar o formulário a seguir para enviar um e-mail para {{GENDER:$1|este usuário|esta usuária}}.\nO endereço de e-mail que você inseriu em [[Special:Preferences|suas preferências de usuário]] irá aparecer como o endereço do remetente da mensagem, com o destinatário podendo responder diretamente para você.",
-       "defemailsubject": "E-mail do usuário \"$1\" da {{SITENAME}}",
+       "defemailsubject": "E-mail {{GENDER:$1|do usuário|da usuária|do(a) usuário(a)}} \"$1\" da {{SITENAME}}",
        "usermaildisabled": "O e-mail do usuário foi desativado",
        "usermaildisabledtext": "Você não tem como enviar e-mails a outros usuários deste wiki.",
        "noemailtitle": "Sem endereço de e-mail",
        "noemailtext": "Este usuário não especificou um endereço de e-mail válido.",
        "nowikiemailtext": "Este usuário optou por não receber e-mail de outros usuários.",
        "emailnotarget": "O nome do destinatário não existe ou é inválido.",
-       "emailtarget": "Insira o nome de usuário do destinatário",
-       "emailusername": "Nome de usuário:",
+       "emailtarget": "Insira o nome do(a) destinatário(a)",
+       "emailusername": "Nome de usuário(a):",
        "emailusernamesubmit": "Enviar",
        "email-legend": "Enviar uma mensagem eletrônica para outro usuário da {{SITENAME}}",
        "emailfrom": "De:",
        "wlheader-showupdated": "Páginas modificadas desde a sua última visita são mostradas em '''negrito'''",
        "wlnote": "A seguir {{PLURAL:$1|está a última alteração ocorrida|estão as últimas <strong>$1</strong> alterações ocorridas}} {{PLURAL:$2|na última hora|nas últimas <strong>$2</strong> horas}} até $3, $4.",
        "wlshowlast": "Ver últimas $1 horas $2 dias",
-       "watchlistall2": "todas",
+       "watchlistall2": "tudo",
        "watchlist-hide": "Ocultar",
+       "watchlist-submit": "Exibir",
+       "wlshowtime": "Período de tempo a mostrar:",
        "wlshowhideminor": "edições menores",
        "wlshowhidebots": "robôs",
        "wlshowhideliu": "usuários registrados",
        "wlshowhideanons": "usuários anônimos",
        "wlshowhidepatr": "edições patrulhadas",
        "wlshowhidemine": "minhas edições",
+       "wlshowhidecategorization": "categorização de páginas",
        "watchlist-options": "Opções da lista de páginas vigiadas",
        "watching": "Vigiando...",
        "unwatching": "Deixando de vigiar...",
        "watcherrortext": "Ocorreu um erro ao alterar a configuração da sua lista de páginas vigiadas para \"$1\".",
-       "enotif_reset": "Marcar todas páginas como visitadas",
+       "enotif_reset": "Marcar todas as páginas como visitadas",
        "enotif_impersonal_salutation": "Usuário do projeto \"{{SITENAME}}\"",
        "enotif_subject_deleted": "A página $1 da {{SITENAME}} foi eliminada por {{gender:$2|$2}}",
        "enotif_subject_created": "A página $1 da {{SITENAME}} foi criada por {{gender:$2|$2}}",
        "delete-confirm": "Eliminar \"$1\"",
        "delete-legend": "Eliminar",
        "historywarning": "<strong>Aviso:</strong> A página que está prestes a eliminar tem um histórico com aproximadamente $1 {{PLURAL:$1|revisão|revisões}}:",
+       "historyaction-submit": "Exibir",
        "confirmdeletetext": "Encontra-se prestes a eliminar uma página juntamente com todo o seu histórico.\nPor favor, confirme que possui a intenção de fazer isto, que compreende as consequências e que encontra-se a fazer isto de acordo com as [[{{MediaWiki:Policy-url}}|políticas]] do projeto.",
        "actioncomplete": "Ação concluída",
        "actionfailed": "Falha na ação",
        "changecontentmodel": "Alterar o modelo de conteúdo de uma página",
        "changecontentmodel-legend": "Alterar o modelo de conteúdo",
        "changecontentmodel-title-label": "Título da página",
+       "changecontentmodel-model-label": "Modelo de conteúdo novo",
        "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.",
        "restriction-create": "Criar",
        "restriction-upload": "Enviar",
        "restriction-level-sysop": "totalmente protegida",
-       "restriction-level-autoconfirmed": "semi-protegida",
+       "restriction-level-autoconfirmed": "semiprotegida",
        "restriction-level-all": "qualquer nível",
        "undelete": "Ver páginas eliminadas",
        "undeletepage": "Ver e restaurar páginas eliminadas",
        "namespace_association": "Espaço nominal associado",
        "tooltip-namespace_association": "Marque esta caixa para incluir também o espaço nominal de conteúdo ou de discussão associado à sua seleção",
        "blanknamespace": "(Principal)",
-       "contributions": "Contribuições {{GENDER:$1|do usuário|da usuária}}",
-       "contributions-title": "Contribuições {{GENDER:$1|do usuário|da usuária}} $1",
+       "contributions": "Contribuições {{GENDER:$1|do usuário|da usuária|do(a) usuário(a)}}",
+       "contributions-title": "Contribuições {{GENDER:$1|do usuário|da usuária|do(a) usuário(a)}} $1",
        "mycontris": "Contribuições",
+       "anoncontribs": "Contribuições",
        "contribsub2": "Para {{GENDER:$3|$1}} ($2)",
        "contributions-userdoesnotexist": "A conta de usuário \"$1\" não está registrada.",
        "nocontribs": "Não foram encontradas mudanças com este critério.",
        "sp-contributions-blocked-notice": "Este usuário atualmente está bloqueado. O registro de bloqueio mais recente é fornecido abaixo para referência:",
        "sp-contributions-blocked-notice-anon": "Este endereço IP encontra-se bloqueado.\nSegue, para referência, a entrada mais recente no registro de bloqueios:",
        "sp-contributions-search": "Navegar pelas contribuições",
-       "sp-contributions-username": "Endereço de IP ou usuário:",
+       "sp-contributions-username": "Endereço de IP ou usuário(a):",
        "sp-contributions-toponly": "Mostrar somente as edições que sejam a última alteração",
        "sp-contributions-newonly": "Mostrar somente as criações de páginas",
        "sp-contributions-submit": "Pesquisar",
        "whatlinkshere-hidelinks": "$1 links",
        "whatlinkshere-hideimages": "$1 links para arquivos",
        "whatlinkshere-filters": "Filtros",
+       "whatlinkshere-submit": "Avançar",
        "autoblockid": "Autobloqueio #$1",
-       "block": "Bloquear usuário",
+       "block": "Bloquear usuário(a)",
        "unblock": "Desbloquear usuário",
-       "blockip": "Bloquear {{GENDER:$1|usuário|usuária}}",
-       "blockip-legend": "Bloquear usuário",
-       "blockiptext": "Utilize o formulário abaixo para bloquear o acesso à escrita de um endereço específico de IP ou nome de usuário.\nIsto só deve ser feito para prevenir vandalismo, e de acordo com a [[{{MediaWiki:Policy-url}}|política]]. Preencha com um motivo específico a seguir (por exemplo, citando páginas que sofreram vandalismo).",
-       "ipaddressorusername": "Endereço de IP ou nome de usuário:",
+       "blockip": "Bloquear {{GENDER:$1|usuário|usuária|usuário(a)}}",
+       "blockip-legend": "Bloquear usuário(a)",
+       "blockiptext": "Utilize o formulário abaixo para bloquear o acesso à escrita de um endereço específico de IP ou nome de usuário(a).\nIsto só deve ser feito para prevenir vandalismo, e de acordo com a [[{{MediaWiki:Policy-url}}|política]]. Preencha com um motivo específico a seguir (por exemplo, citando páginas que sofreram vandalismo).",
+       "ipaddressorusername": "Endereço de IP ou nome de usuário(a):",
        "ipbexpiry": "Expiração:",
        "ipbreason": "Motivo:",
        "ipbreason-dropdown": "*Razões comuns para um bloqueio\n** Inserindo informações falsas\n** Removendo o conteúdo de páginas\n** Fazendo \"spam\" de sítios externos\n** Inserindo conteúdo sem sentido/incompreensível nas páginas\n** Comportamento intimidador/inoportuno\n** Uso abusivo de contas múltiplas\n** Nome de usuário inaceitável",
        "ipb-hardblock": "Impedir que usuários autenticados editem a partir deste endereço IP",
-       "ipbcreateaccount": "Prevenir criação de conta de usuário",
-       "ipbemailban": "Impedir usuário de enviar e-mail",
-       "ipbenableautoblock": "Bloquear automaticamente o endereço de IP mais recente usado por este usuário e todos os IPs subseqüentes dos quais ele tentar editar",
-       "ipbsubmit": "Bloquear este usuário",
+       "ipbcreateaccount": "Prevenir a criação de contas",
+       "ipbemailban": "Impedir usuário(a) de enviar e-mail",
+       "ipbenableautoblock": "Bloquear automaticamente o endereço de IP mais recente usado por este(a) usuário(a) e todos os IPs subsequentes dos quais ele(a) tentar editar",
+       "ipbsubmit": "Bloquear este(a) usuário(a)",
        "ipbother": "Outro período:",
        "ipboptions": "2 horas:2 hours,1 dia:1 day,3 dias:3 days,1 semana:1 week,2 semanas:2 weeks,1 mês:1 month,3 meses:3 months,6 meses:6 months,1 ano:1 year,indefinido:infinite",
        "ipbhidename": "Ocultar nome de usuário em edições e listas",
-       "ipbwatchuser": "Vigiar a página de usuário e a página de discussão deste usuário",
-       "ipb-disableusertalk": "Impedir que este usuário edite a sua página de discussão enquanto estiver bloqueado",
+       "ipbwatchuser": "Vigiar a página de usuário(a) e a página de discussão deste(a) usuário(a)",
+       "ipb-disableusertalk": "Impedir que este(a) usuário(a) edite a sua página de discussão enquanto estiver bloqueado(a)",
        "ipb-change-block": "Bloquear o usuário novamente com estes parâmetros",
        "ipb-confirm": "Confirmar bloqueio",
        "badipaddress": "Endereço de IP inválido",
        "blockipsuccesssub": "Bloqueio bem sucedido",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]] foi {{GENDER:$1|bloqueado|bloqueada}}.<br />\nConsulte a [[Special:BlockList|lista de bloqueios]].",
-       "ipb-blockingself": "Você está prestes a bloquear-se a si próprio. Você tem a certeza de que pretende fazê-lo?",
+       "ipb-blockingself": "Você está prestes a se bloquear! Tem certeza de que pretende fazer isso?",
        "ipb-confirmhideuser": "Você está prestes a bloquear um usuário com \"Ocultar nome de usuário/IP\" ativado. Isto irá suprimir o nome do usuário de todas as listas e entradas dos registos. Tem a certeza de que pretende fazê-lo?",
        "ipb-confirmaction": "Se você tem certeza que realmente quer fazer isto, por favor verifique o campo \"{{int:ipb-confirm}}\" no final.",
        "ipb-edit-dropdown": "Editar motivos de bloqueio",
        "export-download": "Oferecer para salvar como um arquivo",
        "export-templates": "Incluir predefinições",
        "export-pagelinks": "Incluir páginas ligadas até uma profundidade de:",
+       "export-manual": "Adicionar páginas manualmente:",
        "allmessages": "Todas as mensagens de sistema",
        "allmessagesname": "Nome",
        "allmessagesdefault": "Texto padrão",
        "javascripttest-pagetext-frameworks": "Escolha uma das seguintes estruturas de teste: $1",
        "javascripttest-pagetext-skins": "Escolha o tema para executar os testes:",
        "javascripttest-qunit-intro": "Veja a [$1 documentação de testes] no mediawiki.org.",
-       "tooltip-pt-userpage": "Sua página de usuário",
+       "tooltip-pt-userpage": "Sua página de {{GENDER:|usuário|usuária|usuário(a)}}",
        "tooltip-pt-anonuserpage": "A página de usuário para o ip com o qual você está editando",
-       "tooltip-pt-mytalk": "Sua página de discussão",
+       "tooltip-pt-mytalk": "{{GENDER:|Sua}} página de discussão",
        "tooltip-pt-anontalk": "Discussão sobre edições deste endereço de ip",
-       "tooltip-pt-preferences": "Suas configurações",
+       "tooltip-pt-preferences": "{{GENDER:|Suas}} configurações",
        "tooltip-pt-watchlist": "Lista de alterações nas páginas que você está monitorando",
-       "tooltip-pt-mycontris": "Listagem de suas contribuições",
+       "tooltip-pt-mycontris": "Listagem das {{GENDER:|suas}} contribuições",
+       "tooltip-pt-anoncontribs": "Uma lista de edições feitas a partir deste endereço de IP",
        "tooltip-pt-login": "Você é encorajado a autenticar-se; no entanto, não é obrigatório",
        "tooltip-pt-logout": "Sair",
        "tooltip-pt-createaccount": "É recomendado que você crie uma conta e inicie uma seção; todavia, isto não é obrigatório",
        "tooltip-t-recentchangeslinked": "Mudanças recentes nas páginas para as quais esta possui links",
        "tooltip-feed-rss": "Feed RSS desta página",
        "tooltip-feed-atom": "Feed Atom desta página",
-       "tooltip-t-contributions": "Ver as contribuições deste usuário",
-       "tooltip-t-emailuser": "Enviar um e-mail a este usuário",
+       "tooltip-t-contributions": "Ver as contribuições {{GENDER:$1{{BASEPAGENAME}}|deste usuário|desta usuária|deste(a) usuário(a)}}",
+       "tooltip-t-emailuser": "Enviar um e-mail a {{GENDER:{{BASEPAGENAME}}|este usuário|esta usuária|este(a) usuário(a)}}",
        "tooltip-t-info": "Mais informações sobre esta página",
        "tooltip-t-upload": "Enviar arquivos",
        "tooltip-t-specialpages": "Lista de páginas especiais",
        "pageinfo-category-files": "Número de arquivos",
        "markaspatrolleddiff": "Marcar como patrulhada",
        "markaspatrolledtext": "Marcar esta página como patrulhada",
+       "markaspatrolledtext-file": "Marcar esta versão de artigo como patrulhada",
        "markedaspatrolled": "Marcado como verificado",
        "markedaspatrolledtext": "A revisão selecionada de [[:$1]] foi marcada como patrulhada.",
        "rcpatroldisabled": "Edições verificadas nas Mudanças Recentes desativadas",
        "newimages-legend": "Filtrar",
        "newimages-label": "Nome de arquivo (ou parte dele):",
        "newimages-showbots": "Mostrar uploads realizados por bots",
+       "newimages-hidepatrolled": "Ocultar os carregamentos patrulhados.",
        "noimages": "Nada para ver.",
        "ilsubmit": "Pesquisar",
        "bydate": "por data",
        "exif-subjectdistancerange": "Distância de alcance do sujeito",
        "exif-imageuniqueid": "Identificação única da imagem",
        "exif-gpsversionid": "Versão de GPS",
-       "exif-gpslatituderef": "Latitude Norte ou Sul",
+       "exif-gpslatituderef": "Latitude norte ou sul",
        "exif-gpslatitude": "Latitude",
-       "exif-gpslongituderef": "Longitude Leste ou Oeste",
+       "exif-gpslongituderef": "Longitude leste ou oeste",
        "exif-gpslongitude": "Longitude",
        "exif-gpsaltituderef": "Referência de altitude",
        "exif-gpsaltitude": "Altitude",
        "exif-compression-4": "CCITT Grupo 4 codificação de fax",
        "exif-copyrighted-true": "Direitos autorais reservados",
        "exif-copyrighted-false": "Situação de direitos autorais não definido",
+       "exif-photometricinterpretation-1": "Preto e branco (Preto é 0)",
        "exif-unknowndate": "Data desconhecida",
        "exif-orientation-1": "Normal",
        "exif-orientation-2": "Espelhamento horizontal",
        "exif-subjectdistancerange-1": "Macro",
        "exif-subjectdistancerange-2": "Vista próxima",
        "exif-subjectdistancerange-3": "Vista distante",
-       "exif-gpslatitude-n": "Latitude Norte",
-       "exif-gpslatitude-s": "Latitude Sul",
-       "exif-gpslongitude-e": "Longitude Leste",
-       "exif-gpslongitude-w": "Longitude Oeste",
+       "exif-gpslatitude-n": "Latitude norte",
+       "exif-gpslatitude-s": "Latitude sul",
+       "exif-gpslongitude-e": "Longitude leste",
+       "exif-gpslongitude-w": "Longitude oeste",
        "exif-gpsaltitude-above-sealevel": "$1 {{PLURAL:$1|metro|metros}} acima do nível do mar",
        "exif-gpsaltitude-below-sealevel": "$1 {{PLURAL:$1|metro|metros}} abaixo do nível do mar",
        "exif-gpsstatus-a": "Medição em progresso",
        "exif-gpsstatus-v": "Interoperabilidade de medição",
        "exif-gpsmeasuremode-2": "Medição bidimensional",
        "exif-gpsmeasuremode-3": "Medição tridimensional",
-       "exif-gpsspeed-k": "Quilómetros por hora",
+       "exif-gpsspeed-k": "Quilômetros por hora",
        "exif-gpsspeed-m": "Milhas por hora",
        "exif-gpsspeed-n": "Nós",
        "exif-gpsdestdistance-k": "Quilômetros",
        "autoredircomment": "Redirecionando para [[$1]]",
        "autosumm-new": "Criou página com '$1'",
        "autosumm-newblank": "Criar página em branco",
+       "size-kilobytes": "$1 kB",
+       "bitrate-kilobits": "$1&nbsp;kb/s",
        "lag-warn-normal": "É possível que as alterações que sejam mais recentes do que $1 {{PLURAL:$1|segundo|segundos}} não sejam exibidas nesta lista.",
        "lag-warn-high": "Devido a sérios problemas de latência no servidor do banco de dados, as alterações mais recentes que $1 {{PLURAL:$1|segundo|segundos}} poderão não ser exibidas nesta lista.",
        "watchlistedit-normal-title": "Editar lista de páginas vigiadas",
        "watchlisttools-edit": "Ver e editar a lista de páginas vigiadas",
        "watchlisttools-raw": "Edição crua da lista de páginas vigiadas",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussão]])",
+       "timezone-local": "Localização",
        "duplicate-defaultsort": "Aviso: A chave de ordenação padrão \"$2\" sobrepõe-se à anterior chave de ordenação padrão \"$1\".",
        "duplicate-displaytitle": "<strong>Aviso:</strong> O título exibido \"$2\" substituí o título anterior \"$1\".",
        "invalid-indicator-name": "<strong>Erro:</strong> O atributo indicador do status da página <code>name</code> não deve estar vazio.",
        "version-libraries-license": "Licença",
        "version-libraries-description": "Descrição",
        "version-libraries-authors": "Autores",
-       "redirect": "Redirecionar por arquivo, usuário ou ID de revisão",
+       "redirect": "Redirecionar por arquivo, usuário, página, revisão ou registro de identificação.",
        "redirect-legend": "Redirecionar para um arquivo ou página",
        "redirect-summary": "Esta página especial redireciona a um arquivo (dado o nome do arquivo), a uma página (dado um ID de revisão ou ID da página) ou a uma página de usuário (dado o ID do usuário). Uso: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], or [[{{#Special:Redirect}}/user/101]].",
        "redirect-submit": "Ir",
        "tags-deactivate": "Desativar",
        "tags-hitcount": "$1 {{PLURAL:$1|modificação|modificações}}",
        "tags-manage-no-permission": "Você não possui permissão para gerenciar alterações de etiquetas",
+       "tags-manage-blocked": "Você não pode fazer mudanças nas etiquetas enquanto bloqueado.",
        "tags-create-heading": "Criar uma nova etiqueta",
        "tags-create-explanation": "Por padrão, etiquetas recém-criadas serão disponibilizadas para usuários e robôs",
        "tags-create-tag-name": "Nome de etiqueta",
        "tags-deactivate-not-allowed": "Não é possível desativar a etiqueta \"$1\".",
        "tags-deactivate-submit": "Desativar",
        "tags-apply-no-permission": "Você não tem permissão para aplicar mudanças de etiquetas, juntamente com suas alterações.",
+       "tags-apply-blocked": "Você não pode aplicar as mudanças nas etiquetas enquanto está bloqueado.",
        "tags-apply-not-allowed-one": "A etiqueta \"$1\" não pode ser aplicada manualmente.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser aplicada|As seguintes etiquetas não podem ser aplicadas}} manualmente: $1",
        "tags-update-no-permission": "Você não tem permissão para adicionar ou remover mudanças de etiquetas de revisões individuais ou entradas de registro.",
+       "tags-update-blocked": "Você não pode adicionar ou remover mudanças nas etiquetas enquanto bloqueado.",
        "tags-update-add-not-allowed-one": "A etiqueta \"$1\" não pode ser adicionada manualmente.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|A seguinte etiqueta não pode ser adicionada|As seguintes etiquetas não podem ser adicionadas}} manualmente: $1",
        "tags-update-remove-not-allowed-one": "A remoção da etiqueta \"$1\" não é permitida.",
        "logentry-suppress-event-legacy": "$1 {{GENDER:$2|alterou}} secretamente a visibilidade de entradas de registo em $3",
        "logentry-suppress-revision-legacy": "$1 {{GENDER:$2|alterou}} secretamente a visibilidade de revisões da página $3",
        "revdelete-content-hid": "conteúdo oculto",
-       "revdelete-summary-hid": "sumário de edição oculto",
+       "revdelete-summary-hid": "resumo da edição oculto",
        "revdelete-uname-hid": "nome de usuário oculto",
        "revdelete-content-unhid": "conteúdo desocultado",
        "revdelete-summary-unhid": "sumário de edição desocultado",
        "logentry-merge-merge": "$1 {{GENDER:$2|fundiu}} $3 com $4 (edições até $5)",
        "logentry-move-move": "$1 moveu a página $3 para $4",
        "logentry-move-move-noredirect": "$1 moveu a página $3 para $4 sem deixar um redirecionamento",
-       "logentry-move-move_redir": "$1 {{GENDER:$2|moveu}} a página $3 para $4 através de um redirecionamento",
-       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|moveu}} a página $3 para $4 sobre um redirecionamento sem deixar um redirecionamento",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|moveu}} a página $3 para seu redirecionamento $4",
+       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|moveu}} a página $3 para seu redirecionamento $4 suprimindo o primeiro",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|marcou}} a revisão $4 da página $3 como patrulhada",
        "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|marcou}} automaticamente a revisão $4 da página $3 como patrulhada",
        "logentry-newusers-newusers": "A conta de usuário $1 foi {{GENDER:$2|criada}}",
        "pagelang-language": "Idioma",
        "pagelang-use-default": "Idioma padrão de uso",
        "pagelang-select-lang": "Selecionar idioma",
+       "pagelang-submit": "Enviar",
        "right-pagelang": "Mudar idioma da página",
        "action-pagelang": "mudar idioma da página",
        "log-name-pagelang": "Mudar idioma do log",
        "mediastatistics-header-text": "Textuais",
        "mediastatistics-header-executable": "Executáveis",
        "mediastatistics-header-archive": "Formatos compactados",
+       "mediastatistics-header-total": "Todos os arquivos",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|vírgula desnecessária foi removida|vírgulas desnecessárias foram removidas}} do código JSON",
        "json-error-unknown": "Houve um problema com o JSON. Erro: $1",
        "json-error-depth": "A profundidade máxima da pilha foi excedida",
        "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."
+       "api-error-blacklisted": "Por favor, escolha  um título descritivo diferente.",
+       "randomrootpage": "Página raiz aleatória"
 }
index 9b96a54..05a7293 100644 (file)
        "virus-scanfailed": "a verificação falhou (código $1)",
        "virus-unknownscanner": "antivírus desconhecido:",
        "logouttext": "<strong>Já não está autenticado.</strong>\n\nTenha em atenção que algumas páginas poderão continuar a ser apresentadas como se ainda estivesse autenticado até limpar a ''cache'' do seu navegador.",
+       "cannotlogoutnow-title": "Não é possível encerrar a sessão agora",
        "welcomeuser": "Bem-vindo, $1!",
        "welcomecreation-msg": "A sua conta foi criada.\nNão se esqueça de personalizar as suas [[Special:Preferences|preferências]].",
        "yourname": "Nome de utilizador(a):",
        "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",
        "yourdomainname": "O seu domínio:",
        "password-change-forbidden": "Não pode alterar palavras-passe nesta wiki.",
        "externaldberror": "Ocorreu um erro externo à base de dados durante a autenticação ou não lhe é permitido atualizar a sua conta externa.",
        "resetpass_submit": "Definir palavra-passe e entrar",
        "changepassword-success": "A sua palavra-passe foi alterada!",
        "changepassword-throttled": "Realizou demasiadas tentativas de início de sessão com esta conta.\nAguarde $1 antes de tentar novamente, por favor.",
+       "botpasswords": "Palavras-passe de robô",
+       "botpasswords-createnew": "Criar uma nova palavra-passe para robô",
+       "botpasswords-label-appid": "Nome do robô:",
+       "botpasswords-label-create": "Criar",
+       "botpasswords-label-update": "Atualizar",
+       "botpasswords-label-cancel": "Cancelar",
+       "botpasswords-label-delete": "Eliminar",
+       "botpasswords-label-resetpassword": "Redefinir palavra-passe",
+       "botpasswords-label-restrictions": "Restrições de uso:",
+       "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": "A palavra-passe para o robô \"$1\" foi criada com sucesso.",
+       "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>",
        "resetpass_forbidden": "Não é possível alterar palavras-passe",
        "resetpass-no-info": "Precisa de iniciar sessão para aceder diretamente a esta página.",
        "resetpass-submit-loggedin": "Alterar palavra-passe",
        "passwordreset-emailtext-ip": "Alguém (provavelmente você, a partir do endereço IP $1) pediu a recuperação da palavra-passe no projeto {{SITENAME}} ($4). {{PLURAL:$3|A seguinte conta de utilizador está associada|As seguintes contas de utilizador estão associadas}} a este correio eletrónico:\n\n$2\n\n{{PLURAL:$3|Esta palavra-passe temporária irá|Estas palavras-passes temporárias irão}} expirar dentro de {{PLURAL:$5|um dia|$5 dias}}.\nDeve autenticar-se e escolher uma palavra-passe nova agora. Se outra pessoa fez este pedido, ou se entretanto se recordou da sua palavra-passe original e já não deseja alterá-la, pode ignorar esta mensagem e continuar a usar a palavra-passe antiga.",
        "passwordreset-emailtext-user": "O utilizador $1 do projeto {{SITENAME}} pediu a recuperação da sua palavra-passe no projeto {{SITENAME}} ($4). {{PLURAL:$3|A seguinte conta de utilizador está associada|As seguintes contas de utilizador estão associadas}} a este endereço de correio eletrónico:\n\n$2\n\n{{PLURAL:$3|Esta palavra-passe temporária irá|Estas palavras-passes temporárias irão}} expirar dentro de {{PLURAL:$5|um dia|$5 dias}}.\nDeve autenticar-se e escolher uma palavra-passe nova agora. Se outra pessoa fez este pedido, ou se entretanto se recordou da sua palavra-passe original e já não deseja alterá-la, pode ignorar esta mensagem e continuar a usar a palavra-passe antiga.",
        "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 registado para a sua conta, então ser-lhe-à enviada uma palavra-passe de reposição.",
+       "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",
        "changeemail": "Alterar ou remover o endereço de correio eletrónico",
        "history-feed-empty": "A página solicitada não existe.\nPode ter sido eliminada da wiki ou o nome sido alterado.\nTente [[Special:Search|pesquisar na wiki]] novas páginas relevantes.",
        "history-edit-tags": "Editar etiquetas das revisões selecionadas",
        "rev-deleted-comment": "(resumo da edição suprimido)",
-       "rev-deleted-user": "(nome de utilizador removido)",
+       "rev-deleted-user": "(nome de utilizador(a) removido)",
        "rev-deleted-event": "(registos de detalhes eliminados)",
-       "rev-deleted-user-contribs": "[nome de utilizador ou IP removido - edição ocultada das contribuições]",
+       "rev-deleted-user-contribs": "[nome de utilizador(a) ou IP removido – edição ocultada das contribuições]",
        "rev-deleted-text-permission": "Esta revisão de página foi <strong>eliminada</strong>.\nEncontrará detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminações].",
        "rev-suppressed-text-permission": "Esta revisão de página foi <strong>suprimida</strong>.\nPode consultar os detalhes no [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} registo de supressões].",
        "rev-deleted-text-unhide": "Esta revisão de página foi <strong>eliminada</strong>.\nEncontrará detalhes no [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} registo de eliminações].\nPode mesmo assim [$1 ver esta revisão] se deseja prosseguir.",
        "prefs-edits": "Número de edições:",
        "prefsnologintext2": "Por favor, inicie sessão para alterar as suas preferências.",
        "prefs-skin": "Tema",
-       "skin-preview": "Antever tema",
+       "skin-preview": "antever",
        "datedefault": "Sem preferência",
        "prefs-labs": "Funcionalidades dos laboratórios",
        "prefs-user-pages": "Páginas de utilizador",
        "prefs-reset-intro": "Pode usar esta página para repor as configurações padrão das preferências.\nAs suas preferências serão modificadas para os valores predefinidos do site.\nEsta operação não pode ser desfeita.",
        "prefs-emailconfirm-label": "Confirmação do endereço eletrónico:",
        "youremail": "Correio eletrónico:",
-       "username": "Nome de {{GENDER:$1|utilizador|utilizadora}}:",
+       "username": "Nome de {{GENDER:$1|utilizador|utilizadora|utilizador(a)}}:",
        "prefs-memberingroups": "{{GENDER:$2|Membro}} {{PLURAL:$1|do grupo|dos grupos}}:",
        "prefs-registration": "Hora de registo:",
        "yourrealname": "Nome verdadeiro:",
        "prefs-help-prefershttps": "Esta preferência terá efeito no seu próximo início de sessão.",
        "prefswarning-warning": "Fez alterações às suas preferências que não foram gravadas ainda.\nSe abandonar esta página sem clicar em \"$1\", as suas preferências não serão atualizadas.",
        "prefs-tabs-navigation-hint": "Dica: Pode usar as setas direita e esquerda do teclado para navegar entre os separadores.",
-       "userrights": "Gestão de privilégios do utilizador",
+       "userrights": "Gestão de privilégios {{GENDER:{{BASEPAGENAME}}|do utilizador|da utilizadora|de utilizador(a)}}",
        "userrights-lookup-user": "Gerir grupos de utilizadores",
        "userrights-user-editname": "Introduza um nome de utilizador:",
-       "editusergroup": "Editar grupos do utilizador",
+       "editusergroup": "Editar grupos {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}",
        "editinguser": "A modificar os privilégios {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}  <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Editar grupos do utilizador",
-       "saveusergroups": "Gravar grupos do utilizador",
+       "saveusergroups": "Gravar grupos {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}",
        "userrights-groupsmember": "Membro de:",
        "userrights-groupsmember-auto": "Membro implícito de:",
-       "userrights-groups-help": "É possível alterar os grupos a que este utilizador pertence:\n* Uma caixa de seleção marcada significa que o utilizador se encontra no grupo.\n* Uma caixa de seleção desmarcada significa que o utilizador não se encontra no grupo.\n* Um asterisco (*) indica que não pode remover o grupo depois de o adicionar, ou vice-versa.",
+       "userrights-groups-help": "É possível alterar os grupos a que {{GENDER:$1|este utilizador|esta utilizadora|este(a) utilizador(a)}} pertence:\n* Uma caixa de seleção marcada significa que {{GENDER:$1|o utilizador|a utilizadora|o(a) utilizador(a)}} se encontra no grupo.\n* Uma caixa de seleção desmarcada significa que {{GENDER:$1|o utilizador|a utilizadora|o(a) utilizador(a)}} não se encontra no grupo.\n* Um asterisco (*) indica que não pode remover o grupo depois de o adicionar, ou vice-versa.",
        "userrights-reason": "Motivo:",
        "userrights-no-interwiki": "Não tem permissões para alterar os privilégios de utilizadores noutras wikis.",
        "userrights-nodatabase": "A base de dados $1 não existe ou não é uma base de dados local.",
        "group-user-member": "{{GENDER:$1|utilizador|utilizadora}}",
        "group-autoconfirmed-member": "{{GENDER:$1|utilizador autoconfirmado|utilizadora autoconfirmada|utilizador(a) autoconfirmado(a)}}",
        "group-bot-member": "{{GENDER:$1|robô}}",
-       "group-sysop-member": "{{GENDER:$1|administrador|administradora}}",
+       "group-sysop-member": "{{GENDER:$1|administrador|administradora|administrador(a)}}",
        "group-bureaucrat-member": "{{GENDER:$1|burocrata}}",
-       "group-suppress-member": "{{GENDER:$1|supressor|supressora}}",
+       "group-suppress-member": "{{GENDER:$1|supressor|supressora|supressor(a)}}",
        "grouppage-user": "{{ns:project}}:Utilizadores",
        "grouppage-autoconfirmed": "{{ns:project}}:Autoconfirmados",
        "grouppage-bot": "{{ns:project}}:Robôs",
        "right-blockemail": "Impedir um utilizador de enviar correio eletrónico",
        "right-hideuser": "Bloquear um nome de utilizador, escondendo-o do público",
        "right-ipblock-exempt": "Contornar bloqueios de IP, bloqueios automáticos e bloqueios de gamas de IP",
-       "right-proxyunbannable": "Contornar bloqueios automáticos de proxies",
        "right-unblockself": "Desbloquearem-se a si próprios",
        "right-protect": "Mudar níveis de proteção e editar páginas protegidas em cascata",
        "right-editprotected": "Editar páginas protegidas como \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Criar e eliminar [[Special:Tags|etiquetas]] da base de dados",
        "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",
+       "grant-generic": "Pacote de direitos \"$1\"",
+       "grant-group-page-interaction": "Interagir com páginas",
+       "grant-group-file-interaction": "Interagir com multimédia",
+       "grant-group-watchlist-interaction": "Interagir com a sua lista de vigiados",
+       "grant-group-email": "Enviar correio electrónico",
+       "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-other": "Actividade diversa",
+       "grant-blockusers": "Bloquear e desbloquear utilizadores",
+       "grant-createaccount": "Criar contas",
+       "grant-createeditmovepage": "Criar, editar e mover páginas",
+       "grant-delete": "Eliminar páginas, revisões e entradas de registo",
+       "grant-editinterface": "Editar o domínio MediaWiki e o CSS/JavaScript do utilizador",
+       "grant-editmycssjs": "Editar o seu CSS/JavaScript personalizado",
+       "grant-editmyoptions": "Editar as suas preferências de utilizador",
+       "grant-editmywatchlist": "Editar a sua lista de vigiados",
+       "grant-editpage": "Editar páginas existentes",
+       "grant-editprotected": "Editar páginas protegidas",
+       "grant-highvolume": "Alta quantidade de edições",
+       "grant-oversight": "Ocultar utilizadores e edições suprimidas",
+       "grant-patrol": "Patrulhar alterações a páginas",
+       "grant-protect": "Proteger e desproteger páginas",
+       "grant-rollback": "Reverter alterações a páginas",
+       "grant-sendemail": "Enviar correio electrónico a outros utilizadores",
+       "grant-uploadeditmovefile": "Carregar, substituir e mover ficheiros",
+       "grant-uploadfile": "Carregar novos ficheiros",
+       "grant-basic": "Direitos básicos",
+       "grant-viewdeleted": "Ver páginas e ficheiros eliminados",
+       "grant-viewmywatchlist": "Ver a sua lista de vigiados",
        "newuserlogpage": "Registo de criação de utilizadores",
        "newuserlogpagetext": "Este é um registo de novas contas de utilizador",
-       "rightslog": "Registo de privilégios de utilizador",
+       "rightslog": "Registo de privilégios de utilizadores",
        "rightslogtext": "Este é um registo de mudanças nos privilégios dos utilizadores.",
        "action-read": "ler esta página",
        "action-edit": "editar esta página",
        "foreign-structured-upload-form-label-infoform-date": "Data",
        "foreign-structured-upload-form-label-own-work-message-local": "Confirmo que estou a carregar este ficheiro segundo as condições de serviço e política de licenças de {{SITENAME}}.",
        "foreign-structured-upload-form-label-not-own-work-local-local": "Poderá querer experimentar [[Special:Upload|a página padrão de carregamento]].",
+       "foreign-structured-upload-form-2-label-intro": "Obrigado por doar uma imagem para utilização em {{SITENAME}}. Deverá continuar apenas se cumprir algumas condições:",
        "foreign-structured-upload-form-3-label-yes": "Sim",
        "foreign-structured-upload-form-3-label-no": "Não",
+       "foreign-structured-upload-form-4-label-bad": "Não pode carregar imagens encontradas num motor de busca ou descarregadas de outros sítios.",
        "backend-fail-stream": "Não foi possível transmitir o ficheiro \"$1\".",
        "backend-fail-backup": "Não foi possível fazer cópia de segurança do ficheiro \"$1\".",
        "backend-fail-notexists": "O ficheiro $1 não existe.",
        "filehist-deleteone": "eliminar",
        "filehist-revert": "restaurar",
        "filehist-current": "atual",
-       "filehist-datetime": "Data/Hora",
+       "filehist-datetime": "Data e hora",
        "filehist-thumb": "Miniatura",
        "filehist-thumbtext": "Miniatura da versão das $1",
        "filehist-nothumb": "Miniatura indisponível",
        "listusers-creationsort": "Ordenar por data de criação",
        "listusers-desc": "Ordenar de forma decrescente",
        "usereditcount": "$1 {{PLURAL:$1|edição|edições}}",
-       "usercreated": "{{GENDER:$3|Criado|Criada}} em $1 às $2",
+       "usercreated": "{{GENDER:$3|criado|criada|criado(a)}} a $1 às $2",
        "newpages": "Páginas recentes",
        "newpages-submit": "Mostrar",
        "newpages-username": "Nome de utilizador(a):",
        "log-title-wildcard": "Procurar títulos iniciados por este texto",
        "showhideselectedlogentries": "Mostrar ou ocultar as entradas selecionadas",
        "log-edit-tags": "Editar etiquetas das entradas de registo selecionadas",
+       "checkbox-select": "Selecionar: $1",
+       "checkbox-invert": "Inverter",
        "allpages": "Todas as páginas",
        "nextpage": "Página seguinte ($1)",
        "prevpage": "Página anterior ($1)",
        "listusersfrom": "Mostrar utilizadores começados por:",
        "listusers-submit": "Mostrar",
        "listusers-noresult": "Não foram encontrados utilizadores.",
-       "listusers-blocked": "(bloqueado)",
+       "listusers-blocked": "({{GENDER:$1|bloqueado|bloqueada|bloqueado(a)}})",
        "activeusers": "Lista de utilizadores ativos",
        "activeusers-intro": "Esta é uma lista dos utilizadores com qualquer tipo de atividade {{PLURAL:$1|no último dia|nos últimos $1 dias}}.",
        "activeusers-count": "$1 {{PLURAL:$1|ação|ações}} {{PLURAL:$3|no último dia|nos últimos $3 dias}}",
        "listgrouprights-namespaceprotection-header": "Restrições do domínio",
        "listgrouprights-namespaceprotection-namespace": "Domínio",
        "listgrouprights-namespaceprotection-restrictedto": "Direito(s) do utilizador para editar",
+       "listgrants": "Privilégios",
+       "listgrants-grant": "Privilégio",
+       "listgrants-rights": "Direitos",
        "trackingcategories": "Categorias de monitorização",
        "trackingcategories-summary": "Esta página lista as categorias monitoradas que foram geradas automaticamente pelo software MediaWiki. Os seus nomes podem ser alterados ao editar sua mensagem correspondente no domínio {{ns:8}}.",
        "trackingcategories-msg": "Categoria monitorada",
        "trackingcategories-disabled": "A categoria está desativada.",
        "mailnologin": "Não existe endereço de envio",
        "mailnologintext": "Precisa de estar [[Special:UserLogin|autenticado]] e ter um endereço de correio válido nas suas [[Special:Preferences|preferências]], para poder enviar correio eletrónico a outros utilizadores.",
-       "emailuser": "Enviar correio eletrónico a este utilizador",
-       "emailuser-title-target": "Enviar correio eletrónico a {{GENDER:$1|este utilizador|esta utilizadora}}",
+       "emailuser": "Enviar correio eletrónico a {{GENDER:{{BASEPAGENAME}}|este utilizador|esta utilizadora|este(a) utilizador(a)}}",
+       "emailuser-title-target": "Enviar correio eletrónico a {{GENDER:$1|este utilizador|esta utilizadora|este(a) utilizador(a)}}",
        "emailuser-title-notarget": "Enviar correio eletrónico ao utilizador",
        "emailpagetext": "Pode usar o formulário abaixo para enviar uma mensagem por correio eletrónico para {{GENDER:$1|este utilizador|esta utilizadora}}.\nO endereço de correio que introduziu nas [[Special:Preferences|suas preferências]] irá aparecer no campo do remetente da mensagem \"De:\", para que o destinatário lhe possa responder diretamente.",
-       "defemailsubject": "Correio eletrónico da {{SITENAME}}, do utilizador \"$1\"",
+       "defemailsubject": "Correio eletrónico da {{SITENAME}}, {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}} \"$1\"",
        "usermaildisabled": "O correio eletrónico do utilizador foi desativado",
        "usermaildisabledtext": "Não pode enviar correio eletrónico a outros utilizadores desta wiki",
        "noemailtitle": "Sem endereço de correio eletrónico",
        "noemailtext": "Este utilizador não especificou um endereço de correio eletrónico válido.",
        "nowikiemailtext": "Este utilizador optou por não receber correio eletrónico de outros utilizadores.",
        "emailnotarget": "O nome do destinatário não existe ou é inválido.",
-       "emailtarget": "Introduza o nome de utilizador do destinatário.",
-       "emailusername": "Utilizador:",
+       "emailtarget": "Introduza o nome do(a) destinatário(a)",
+       "emailusername": "Utilizador(a):",
        "emailusernamesubmit": "Enviar",
        "email-legend": "Enviar uma mensagem a outro utilizador da {{SITENAME}}",
        "emailfrom": "De:",
        "wlheader-showupdated": "As páginas modificadas desde a última vez que as visitou aparecem destacadas a '''negrito'''.",
        "wlnote": "A seguir {{PLURAL:$1|está a última alteração ocorrida|estão as últimas <strong>$1</strong> alterações ocorridas}} {{PLURAL:$2|na última hora|nas últimas <strong>$2</strong> horas}} até $3, $4.",
        "wlshowlast": "Ver últimas $1 horas $2 dias",
-       "watchlistall2": "todas",
+       "watchlistall2": "desde sempre",
        "watchlist-hide": "Ocultar",
        "watchlist-submit": "Mostrar",
        "wlshowtime": "Período de tempo a mostrar:",
        "wlshowhideanons": "usuários anônimos",
        "wlshowhidepatr": "edições patrulhadas",
        "wlshowhidemine": "minhas edições",
+       "wlshowhidecategorization": "categorização de páginas",
        "watchlist-options": "Opções da lista de páginas vigiadas",
        "watching": "A vigiar...",
        "unwatching": "A deixar de vigiar...",
        "namespace_association": "Domínio associado",
        "tooltip-namespace_association": "Marque esta caixa para incluir também o domínio de conteúdo ou de discussão associado à sua seleção",
        "blanknamespace": "(Principal)",
-       "contributions": "Contribuições {{GENDER:$1|do utilizador|da utilizadora}}",
-       "contributions-title": "Contribuições {{GENDER:$1|do utilizador|da utilizadora}} $1",
+       "contributions": "Contribuições {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}}",
+       "contributions-title": "Contribuições {{GENDER:$1|do utilizador|da utilizadora|do(a) utilizador(a)}} $1",
        "mycontris": "Contribuições",
        "anoncontribs": "Contribuições",
        "contribsub2": "Para {{GENDER:$3|$1}} ($2)",
        "whatlinkshere-filters": "Filtros",
        "whatlinkshere-submit": "Ir",
        "autoblockid": "Bloqueio automático nº$1",
-       "block": "Bloquear utilizador",
+       "block": "Bloquear utilizador(a)",
        "unblock": "Desbloquear utilizador",
-       "blockip": "Bloquear {{GENDER:$1|utilizador|utilizadora}}",
-       "blockip-legend": "Bloquear utilizador",
-       "blockiptext": "Utilize o formulário abaixo para bloquear o acesso de escrita a um endereço IP específico ou a um nome de utilizador.\nIsto só deve ser feito para prevenir vandalismo e de acordo com a [[{{MediaWiki:Policy-url}}|política]]. Indique a seguir um motivo de bloqueio específico (por exemplo, indicando as páginas que foram alvo de vandalismo).",
+       "blockip": "Bloquear {{GENDER:$1|utilizador|utilizadora|utilizador(a)}}",
+       "blockip-legend": "Bloquear utilizador(a)",
+       "blockiptext": "Utilize o formulário abaixo para bloquear o acesso de escrita a um endereço IP específico ou a um nome de utilizador(a).\nIsto só deve ser feito para prevenir vandalismo e de acordo com a [[{{MediaWiki:Policy-url}}|política]]. Indique a seguir um motivo de bloqueio específico (por exemplo, indicando as páginas que foram alvo de vandalismo).",
        "ipaddressorusername": "Endereço de IP ou utilizador(a):",
        "ipbexpiry": "Expiração:",
        "ipbreason": "Motivo:",
        "ipbreason-dropdown": "*Razões comuns para um bloqueio\n** Inserção de informações falsas\n** Remoção de conteúdos de páginas\n** Inserção de \"spam\" para sítios externos\n** Inserção de conteúdo sem sentido/incompreensível nas páginas\n** Comportamento intimidador/inoportuno\n** Uso abusivo de contas múltiplas\n** Nome de utilizador inaceitável",
        "ipb-hardblock": "Impedir que utilizadores autenticados editem a partir deste endereço IP",
-       "ipbcreateaccount": "Impedir criação de contas de utilizador",
-       "ipbemailban": "Impedir utilizador de enviar correio eletrónico",
-       "ipbenableautoblock": "Bloquear automaticamente o endereço IP mais recente deste utilizador e todos os endereços IP subsequentes a partir dos quais ele tente editar",
-       "ipbsubmit": "Bloquear este utilizador",
+       "ipbcreateaccount": "Impedir a criação de contas",
+       "ipbemailban": "Impedir utilizador(a) de enviar correio eletrónico",
+       "ipbenableautoblock": "Bloquear automaticamente o endereço IP mais recente deste(a) utilizador(a) e todos os endereços IP subsequentes a partir dos quais ele(a) tente editar",
+       "ipbsubmit": "Bloquear este(a) utilizador(a)",
        "ipbother": "Outro período:",
        "ipboptions": "2 horas:2 hours,1 dia:1 day,3 dias:3 days,1 semana:1 week,2 semanas:2 weeks,1 mês:1 month,3 meses:3 months,6 meses:6 months,1 ano:1 year,indefinido:infinite",
        "ipbhidename": "Ocultar nome de utilizador nas edições e listas",
-       "ipbwatchuser": "Vigiar as páginas de utilizador e de discussão deste utilizador",
-       "ipb-disableusertalk": "Impedir que este utilizador edite a sua página de discussão enquanto estiver bloqueado",
+       "ipbwatchuser": "Vigiar as páginas de utilizador(a) e de discussão deste(a) utilizador(a)",
+       "ipb-disableusertalk": "Impedir que este(a) utilizador(a) edite a sua página de discussão enquanto estiver bloqueado(a)",
        "ipb-change-block": "Voltar a bloquear o utilizador com estes parâmetros",
        "ipb-confirm": "Confirmar o bloqueio",
        "badipaddress": "Endereço IP inválido",
        "lockedbyandtime": "(por {{GENDER:$1|$1}} em $2 às $3)",
        "move-page": "Mover $1",
        "move-page-legend": "Mover página",
-       "movepagetext": "Usando o formulário abaixo pode mover esta página e todo o seu histórico de edições para uma página nova com outro nome.\nA página original será transformada num redirecionamento para a página nova.\nPode corrigir de forma automática os redirecionamentos existentes que apontam para a página original.\nCaso escolha não o fazer, após a operação certifique-se de que dela não resultaram  [[Special:DoubleRedirects|redirecionamentos duplos]] ou [[Special:BrokenRedirects|quebrados]].\nÉ da sua responsabilidade verificar que as ligações continuam a apontar para onde é suposto que apontem.\n\nNote que a página '''não''' será movida se já existir uma página com o novo título, a menos que esta última seja um redirecionamento sem qualquer histórico de edições.\nIsto significa que pode mover uma página de volta para o seu nome original se a tiver movido por engano e que não pode mover uma página para cima de outra já existente.\n\n'''CUIDADO!'''\nNuma página popular esta operação pode representar uma mudança drástica e inesperada;\ncertifique-se de que compreende as consequências da mudança antes de prosseguir, por favor.",
-       "movepagetext-noredirectfixer": "Usando o formulário abaixo, pode alterar o nome de uma página e mover todo o histórico desta para o nome novo.\nA página antiga é transformada numa página de redirecionamento para a nova.\nVerifique a existência de [[Special:DoubleRedirects|redirecionamentos duplos]] ou [[Special:BrokenRedirects|quebrados]].\nÉ da sua responsabilidade certificar-se de que as ligações continuam a apontar para onde é suposto.\n\nNote que a página '''não''' será movida se já existir uma página com o nome novo, a menos que esta página já existente esteja vazia ou seja uma página de redirecionamento e não tenha um histórico de edições.\nIsto também significa que, se se tiver enganado, pode alterar o nome da página movida de volta para o seu nome original; e que não pode sobrescrever o conteúdo de uma página existente.\n\n'''Aviso!'''\nPara páginas populares, esta operação pode representar uma mudança drástica e inesperada;\ncertifique-se de que compreende as consequências da operação antes de continuar.",
+       "movepagetext": "Usando o formulário abaixo pode mover esta página e todo o seu histórico de edições para uma página nova com outro nome.\nA página original será transformada num redirecionamento para a página nova.\nPode corrigir de forma automática os redirecionamentos existentes que apontam para a página original.\nCaso escolha não o fazer, após a operação certifique-se de que dela não resultaram  [[Special:DoubleRedirects|redirecionamentos duplos]] ou [[Special:BrokenRedirects|quebrados]].\nÉ da sua responsabilidade verificar que as ligações continuam a apontar para onde é suposto que apontem.\n\nNote que a página '''não''' será movida se já existir uma página com o novo título, a menos que esta última seja um redirecionamento sem qualquer histórico de edições.\nIsto significa que pode mover uma página de volta para o seu nome original se a tiver movido por engano e que não pode mover uma página para cima de outra já existente.\n\n'''Nota:'''\nNuma página popular esta operação pode representar uma mudança drástica e inesperada;\ncertifique-se de que compreende as consequências da mudança antes de prosseguir, por favor.",
+       "movepagetext-noredirectfixer": "Usando o formulário abaixo, pode alterar o nome de uma página e mover todo o histórico desta para o nome novo.\nA página antiga é transformada numa página de redirecionamento para a nova.\nVerifique a existência de [[Special:DoubleRedirects|redirecionamentos duplos]] ou [[Special:BrokenRedirects|quebrados]].\nÉ da sua responsabilidade certificar-se de que as ligações continuam a apontar para onde é suposto.\n\nNote que a página '''não''' será movida se já existir uma página com o nome novo, a menos que esta página já existente esteja vazia ou seja uma página de redirecionamento e não tenha um histórico de edições.\nIsto também significa que, se se tiver enganado, pode alterar o nome da página movida de volta para o seu nome original; e que não pode sobrescrever o conteúdo de uma página existente.\n\n'''Nota:'''\nPara páginas populares, esta operação pode representar uma mudança drástica e inesperada;\ncertifique-se de que compreende as consequências da operação antes de continuar.",
        "movepagetalktext": "Se assinalar esta opção, e existir uma página de discussão associada, ela será automaticamente movida, a não ser que já exista uma página de discussão com o novo título que não esteja vazia.\n\nNeste caso, terá de mover a página de discussão manualmente, ou fundi-la com a existente, se assim desejar.",
        "moveuserpage-warning": "'''Aviso:''' Está prestes a mover uma página de utilizador. Note que a página será apenas movida, ''sem'' alterar o nome do utilizador.",
        "movecategorypage-warning": "<strong>Aviso:</strong> Está prestes a mover uma categoria. Por favor, note que apenas moverá a página da categoria e quaisquer páginas que estejam listadas <em>não</em> serão recategorizadas na nova.",
        "movenosubpage": "Esta página não tem subpáginas.",
        "movereason": "Motivo:",
        "revertmove": "reverter",
-       "delete_and_move_text": "==Eliminação necessária==\nA página de destino (\"[[:$1]]\") já existe. Deseja eliminá-la de modo a poder mover?",
+       "delete_and_move_text": "A página de destino (\"[[:$1]]\") já existe. \nDeseja eliminá-la de modo a poder mover?",
        "delete_and_move_confirm": "Sim, eliminar a página",
        "delete_and_move_reason": "Eliminada para poder mover \"[[$1]]\" para este título",
        "selfmove": "Os títulos de origem e destino são iguais;\nnão é possível mover uma página para ela mesma.",
        "move-leave-redirect": "Criar um redirecionamento",
        "protectedpagemovewarning": "<strong>Aviso:</strong> Esta página foi protegida de modo que apenas os utilizadores com privilégios de administrador possam movê-la.\nA última entrada é fornecido abaixo como referência:",
        "semiprotectedpagemovewarning": "<strong>Note:</strong> Esta página foi protegida de modo que apenas utilizadores registados a possam mover.\nA última entrada é fornecida abaixo como referência:",
-       "move-over-sharedrepo": "== O ficheiro existe ==\n[[:$1]] já existe num repositório partilhado. Mover um ficheiro para o título [[:$1]] irá sobrepô-lo ao ficheiro partilhado.",
+       "move-over-sharedrepo": "[[:$1]] já existe num repositório partilhado. Mover um ficheiro para o título [[:$1]] irá substituir o ficheiro partilhado.",
        "file-exists-sharedrepo": "O nome de ficheiro que escolheu já é utilizado num repositório partilhado.\nEscolha outro nome, por favor.",
        "export": "Exportar páginas",
        "exporttext": "Pode exportar o texto e o histórico de edições de uma página em particular para um ficheiro XML. Poderá então importar esse conteúdo noutra wiki que utilize o programa MediaWiki, através da [[Special:Import|página de importações]].\n\nPara exportar páginas, introduza os títulos na caixa de texto abaixo (um título por linha) e selecione se deseja todas as versões, com as linhas de histórico de edições, ou apenas a edição atual e informações sobre a mais recente das edições.\n\nSe desejar, pode utilizar um link (por exemplo, [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] para a [[{{MediaWiki:Mainpage}}]]).",
        "export-download": "Gravar em ficheiro",
        "export-templates": "Incluir predefinições",
        "export-pagelinks": "Incluir páginas ligadas, até uma profundidade de:",
+       "export-manual": "Adicionar páginas manualmente:",
        "allmessages": "Mensagens de sistema",
        "allmessagesname": "Nome",
        "allmessagesdefault": "Texto padrão",
        "javascripttest-pagetext-frameworks": "Escolha, por favor, uma das seguintes estruturas de teste: $1",
        "javascripttest-pagetext-skins": "Escolher um tema para executar os testes com:",
        "javascripttest-qunit-intro": "Consulte a [ $1 documentação de testes] no mediawiki.org.",
-       "tooltip-pt-userpage": "A sua página de utilizador",
+       "tooltip-pt-userpage": "A sua página de {{GENDER:|utilizador|utilizadora|utilizador(a)}}",
        "tooltip-pt-anonuserpage": "A página de utilizador para o endereço IP que está a usar",
-       "tooltip-pt-mytalk": "A sua página de discussão",
+       "tooltip-pt-mytalk": "A {{GENDER:|sua}} página de discussão",
        "tooltip-pt-anontalk": "Discussão sobre edições feitas a partir deste endereço IP",
-       "tooltip-pt-preferences": "Configuração dos comportamentos que prefere da wiki",
+       "tooltip-pt-preferences": "As {{GENDER:|suas}} preferências",
        "tooltip-pt-watchlist": "Lista de mudanças nas páginas que está a vigiar",
-       "tooltip-pt-mycontris": "Lista das suas contribuições",
+       "tooltip-pt-mycontris": "Lista das {{GENDER:|suas}} contribuições",
        "tooltip-pt-anoncontribs": "Uma lista de edições feitas a partir deste endereço de IP",
        "tooltip-pt-login": "É encorajado que inicie sessão, apesar de não ser obrigatório.",
        "tooltip-pt-logout": "Sair da conta",
        "tooltip-t-recentchangeslinked": "Mudanças recentes nas páginas para as quais esta contém ligação",
        "tooltip-feed-rss": "''Feed'' RSS desta página",
        "tooltip-feed-atom": "''Feed'' Atom desta página",
-       "tooltip-t-contributions": "Ver as contribuições deste utilizador",
-       "tooltip-t-emailuser": "Enviar uma mensagem de correio a este utilizador",
+       "tooltip-t-contributions": "Ver as contribuições {{GENDER:$1|deste utilizador|desta utilizadora|deste(a) utilizador(a)}}",
+       "tooltip-t-emailuser": "Enviar uma mensagem de correio a {{GENDER:$1|este utilizador|esta utilizadora|este(a) utilizador(a)}}",
        "tooltip-t-info": "Mais informações sobre esta página",
        "tooltip-t-upload": "Carregar ficheiros",
        "tooltip-t-specialpages": "Lista de páginas especiais",
        "pageinfo-category-files": "Número de ficheiros",
        "markaspatrolleddiff": "Marcar como patrulhada",
        "markaspatrolledtext": "Marcar esta página como patrulhada",
+       "markaspatrolledtext-file": "Marcar esta versão do ficheiro como patrulhada",
        "markedaspatrolled": "Marcada como patrulhada",
        "markedaspatrolledtext": "A edição selecionada de [[:$1]] foi marcada como patrulhada.",
        "rcpatroldisabled": "Edições patrulhadas nas Mudanças Recentes desativadas",
        "newimages-legend": "Filtrar",
        "newimages-label": "Nome de ficheiro (ou parte dele):",
        "newimages-showbots": "Mostrar carregamentos feitos por robôs",
+       "newimages-hidepatrolled": "Ocultar carregamentos patrulhados",
        "noimages": "Nada para ver.",
        "ilsubmit": "Pesquisar",
        "bydate": "por data",
        "scarytranscludefailed-httpstatus": "[Não foi possível obter a predefinição a partir de $1: HTTP $2]",
        "scarytranscludetoolong": "[URL longa demais]",
        "deletedwhileediting": "<strong>AVISO:</strong> Esta página foi eliminada depois de ter começado a editá-la!",
-       "confirmrecreate": "Depois de ter começado a editar esta página, o utilizador [[User:$1|$1]] ([[User talk:$1|conversar]]) eliminou-a pelo seguinte motivo:\n: <em>$2</em>\nPor favor, confirme que quer realmente recriar esta página.",
-       "confirmrecreate-noreason": "O utilizador [[User:$1|$1]] ([[User talk:$1|discussão]]) eliminou esta página depois de ter começado a editá-la. Confirme que deseja recriar a página, por favor.",
+       "confirmrecreate": "Depois de ter começado a editar esta página, {{GENDER:$2|o utilizador|a utilizadora|o(a) utilizador(a)}} [[User:$1|$1]] ([[User talk:$1|discussão]]) eliminou-a pelo seguinte motivo:\n: <em>$2</em>\nPor favor, confirme que quer realmente recriar esta página.",
+       "confirmrecreate-noreason": "{{GENDER:$1|O utilizador|A utilizadora|O(a) utilizador(a)}} [[User:$1|$1]] ([[User talk:$1|discussão]]) eliminou esta página depois de ter começado a editá-la. Confirme que deseja recriar a página, por favor.",
        "recreate": "Recriar",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Limpar a memória cache desta página?",
        "watchlisttools-edit": "Ver e editar a lista de páginas vigiadas",
        "watchlisttools-raw": "Editar a lista de páginas vigiadas em forma de texto",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discussão]])",
+       "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\".",
        "invalid-indicator-name": "<strong>Erro:</strong> O atributo <code>name</code>, da página de estados, não deve estar em branco.",
        "logentry-suppress-event-legacy": "$1 {{GENDER:$2|alterou}} secretamente a visibilidade de entradas de registo em $3",
        "logentry-suppress-revision-legacy": "$1 {{GENDER:$2|alterou}} secretamente a visibilidade de revisões da página $3",
        "revdelete-content-hid": "conteúdo oculto",
-       "revdelete-summary-hid": "sumário de edição oculto",
+       "revdelete-summary-hid": "resumo da edição oculto",
        "revdelete-uname-hid": "utilizador oculto",
        "revdelete-content-unhid": "conteúdo desocultado",
        "revdelete-summary-unhid": "sumário de edição desocultado",
        "logentry-merge-merge": "$1 {{GENDER:$2|fundiu}} $3 com $4 (edições até $5)",
        "logentry-move-move": "$1 moveu a página $3 para $4",
        "logentry-move-move-noredirect": "$1 moveu a página $3 para $4 sem deixar um redirecionamento",
-       "logentry-move-move_redir": "$1 {{GENDER:$2|moveu}} a página $3 para $4 sobre um redirecionamento",
-       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|moveu}} a página $3 para $4 sobre um redirecionamento sem deixar um redirecionamento",
+       "logentry-move-move_redir": "$1 {{GENDER:$2|moveu}} a página $3 para o seu redirecionamento $4",
+       "logentry-move-move_redir-noredirect": "$1 {{GENDER:$2|moveu}} a página $3 para o seu redirecionamento $4, suprimindo o primeiro",
        "logentry-patrol-patrol": "$1 {{GENDER:$2|marcou}} a revisão $4 da página $3 como patrulhada",
        "logentry-patrol-patrol-auto": "$1 {{GENDER:$2|marcou}} automaticamente a revisão $4 da página $3 como patrulhada",
        "logentry-newusers-newusers": "A conta de utilizador $1 foi {{GENDER:$2|criada}}",
        "pagelang-language": "Idioma",
        "pagelang-use-default": "Usar idioma pré-definido",
        "pagelang-select-lang": "Escolher o idioma",
+       "pagelang-submit": "Submeter",
        "right-pagelang": "Alterar o idioma da página",
        "action-pagelang": "alterar o idioma da página",
        "log-name-pagelang": "Alterar registo de idioma",
        "mediastatistics": "Estatísticas multimédia",
        "mediastatistics-summary": "Estatísticas sobre os tipos de ficheiros carregados. Inclui apenas a versão mais recente do ficheiro. Versões antigas ou eliminadas são excluídas.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Tamanho total de ficheiro para este secção: {{PLURAL:$1|$1 byte|$1 bytes}} ($2; $3%).",
+       "mediastatistics-allbytes": "Tamanho total de ficheiro para todos os ficheiros: {{PLURAL:$1|$1 byte|$1 bytes}} ($2).",
        "mediastatistics-table-mimetype": "Tipo MIME",
        "mediastatistics-table-extensions": "Extensões possíveis",
        "mediastatistics-table-count": "Número de ficheiros",
        "mediastatistics-header-text": "Textuais",
        "mediastatistics-header-executable": "Executáveis",
        "mediastatistics-header-archive": "Formatos compactados",
+       "mediastatistics-header-total": "Todos os ficheiros",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|vírgula desnecessária foi removida|vírgulas desnecessárias foram removidas}} do código JSON",
        "json-error-unknown": "Houve um problema com o JSON. Erro: $1",
        "json-error-depth": "A profundidade máxima da pilha foi excedida",
        "headline-anchor-title": "Ligação para esta secção",
        "special-characters-group-latin": "Latim",
        "special-characters-group-latinextended": "Latim expandido",
-       "special-characters-group-ipa": "IPA",
+       "special-characters-group-ipa": "AFI (IPA)",
        "special-characters-group-symbols": "Símbolos",
        "special-characters-group-greek": "Grego",
        "special-characters-group-cyrillic": "Cirílico",
        "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."
+       "api-error-blacklisted": "Escolha um título diferente e descritivo, por favor.",
+       "randomrootpage": "Página raiz aleatória"
 }
index dab2adc..128e243 100644 (file)
        "october-date": "A date in the Gregorian month of October. $1 is the numerical date, for example \"23\".",
        "november-date": "A date in the Gregorian month of November. $1 is the numerical date, for example \"23\".\n{{Identical|November}}",
        "december-date": "A date in the Gregorian month of December. $1 is the numerical date, for example \"23\".",
+       "period-am": "Text indicating the first period of the day when using a 12-hour calendar.",
+       "period-pm": "Text indicating the second period of the day when using a 12-hour calendar.",
        "pagecategories": "Used in the categories section of pages.\n\nFollowed by a colon and a list of categories.\n\nParameters:\n* $1 - number of categories\n{{Identical|Category}}",
        "pagecategorieslink": "{{notranslate}}",
        "category_header": "In category description page. Parameters:\n* $1 - category name\nSee also:\n* {{msg-mw|Category-media-header}}",
        "virus-scanfailed": "Used as error message. \"scan\" stands for \"virus scan\". Parameters:\n* $1 - exit code of virus scanner",
        "virus-unknownscanner": "Used as error message. This message is followed by the virus scanner name.",
        "logouttext": "Log out message. Parameters:\n* $1 - (Unused) an URL to [[Special:Userlogin]] containing <code>returnto</code> and <code>returntoquery</code> parameters",
+       "cannotlogoutnow-title": "Error page title shown when logging out is not possible.",
+       "cannotlogoutnow-text": "Error page text shown when logging out is not possible. Parameters:\n* $1 - Session type in use that makes it not possible to log out, from a message like {{msg-mw|sessionprovider-mediawiki-session-cookiesessionprovider}}.",
        "welcomeuser": "Text for a welcome heading that users see after registering a user account.\n\nParameters:\n* $1 - the username of the new user. See [[phab:T44215]]",
        "welcomecreation-msg": "A welcome message users see after registering a user account, following a welcomeuser heading.\n\nParameters:\n* $1 - (Unused) the username of the new user.\n\nReplaces [[MediaWiki:welcomecreation|welcomecreation]] in 1.21wmf5, see [[phab:T44215]]",
        "yourname": "Since 1.22 no longer used in core, but used by some extensions.\n{{Identical|Username}}",
        "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}}.",
        "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_submit": "Submit button on [[Special:ChangePassword]]",
        "changepassword-success": "Used in [[Special:ChangePassword]].",
        "changepassword-throttled": "Error message shown at [[Special:ChangePassword]] after the user has tried to login with incorrect password too many times.\n\nThe user has to wait a certain time before trying to log in again.\n\nParameters:\n* $1 - the time to wait before the next login attempt. Automatically formatted using the following duration messages:\n** {{msg-mw|Duration-millennia}}\n** {{msg-mw|Duration-centuries}}\n** {{msg-mw|Duration-decades}}\n** {{msg-mw|Duration-years}}\n** {{msg-mw|Duration-weeks}}\n** {{msg-mw|Duration-days}}\n** {{msg-mw|Duration-hours}}\n** {{msg-mw|Duration-minutes}}\n** {{msg-mw|Duration-seconds}}\n\nThis is a protection against robots trying to find the password by trying lots of them.\nThe number of attempts and waiting time are configured via [[mw:Manual:$wgPasswordAttemptThrottle|$wgPasswordAttemptThrottle]].\nThis message is used in html.\n\nSee also:\n* {{msg-mw|Changeemail-throttled}}",
+       "botpasswords": "The name of the special page [[Special:BotPasswords]].",
+       "botpasswords-summary": "Explanatory text shown at the top of [[Special:BotPasswords]].",
+       "botpasswords-disabled": "Error message displayed when bot passwords are not enabled (<code>$wgEnableBotPasswords = false</code>).",
+       "botpasswords-no-central-id": "Error message displayed when the current user does not have a central ID (e.g. they're not logged in or not attached in something like CentralAuth).",
+       "botpasswords-existing": "Form section label for the part of the form listing the user's existing bot passwords.",
+       "botpasswords-createnew": "Form section label for the part of the form related to creating a new bot password.",
+       "botpasswords-editexisting": "Form section label for the part of the form related to editing an existing bot password.",
+       "botpasswords-label-appid": "Form field label for the \"bot name\", internally known as the \"application ID\".",
+       "botpasswords-label-create": "Button label for the button to create a new bot password.\n{{Identical|Create}}",
+       "botpasswords-label-update": "Button label for the button to save changes to a bot password.\n{{Identical|Update}}",
+       "botpasswords-label-cancel": "Button label for a button to cancel the creation or edit of a bot password.\n{{Identical|Cancel}}",
+       "botpasswords-label-delete": "Button label for the button to delete a bot password.\n{{Identical|Delete}}",
+       "botpasswords-label-resetpassword": "Label for the checkbox to reset the actual password for the current bot password.",
+       "botpasswords-label-grants": "Label for the checkmatrix for selecting grants allowed when the bot password is used.",
+       "botpasswords-help-grants": "Help text for the grant selection checkmatrix.",
+       "botpasswords-label-restrictions": "Label for the textarea field in which JSON defining access restrictions (e.g. which IP address ranges are allowed) is entered.",
+       "botpasswords-label-grants-column": "Label for the checkbox column on the checkmatrix for selecting grants allowed when the bot password is used.",
+       "botpasswords-bad-appid": "Used as an error message when an invalid \"bot name\" is supplied on [[Special:BotPasswords]]. Parameters:\n* $1 - The rejected bot name.",
+       "botpasswords-insert-failed": "Error message when saving a new bot password failed. It's likely that the failure was because the user resubmitted the form after a previous successful save. Parameters:\n* $1 - Bot name",
+       "botpasswords-update-failed": "Error message when saving changes to an existing bot password failed. It's likely that the failure was because the user deleted the bot password in another browser window. Parameters:\n* $1 - Bot name",
+       "botpasswords-created-title": "Title of the success page when a new bot password is created.",
+       "botpasswords-created-body": "Success message when a new bot password is created. Parameters:\n* $1 - Bot name",
+       "botpasswords-updated-title": "Title of the success page when a bot password is updated.",
+       "botpasswords-updated-body": "Success message when a bot password is updated. Parameters:\n* $1 - Bot name",
+       "botpasswords-deleted-title": "Title of the success page when a bot password is deleted.",
+       "botpasswords-deleted-body": "Success message when a bot password is deleted. Parameters:\n* $1 - Bot name",
+       "botpasswords-newpassword": "Success message to display the new password when a bot password is created or updated. Parameters:\n* $1 - User name to be used for login.\n* $2 - Password to be used for login.",
+       "botpasswords-no-provider": "Error message when login is attempted but the BotPasswordsSessionProvider is not included in <code>$wgSessionProviders</code>.",
+       "botpasswords-restriction-failed": "Error message when login is rejected because the configured restrictions were not satisfied.",
+       "botpasswords-invalid-name": "Error message when a username lacking the separator character is passed to BotPassword. Parameters:\n* $1 - The separator character.",
+       "botpasswords-not-exist": "Error message when a username exists but does not a bot password for the given \"bot name\". Parameters:\n* $1 - username\n* $2 - bot name",
        "resetpass_forbidden": "Used as error message in changing password. Maybe the external auth plugin won't allow local password changes.",
        "resetpass-no-info": "Error message for [[Special:ChangePassword]].\n\nParameters:\n* $1 (unused) - a link to [[Special:UserLogin]] with {{msg-mw|loginreqlink}} as link description",
        "resetpass-submit-loggedin": "Button on [[Special:ResetPass]] to submit new password.\n\n{{Identical|Change password}}",
        "userrights-summary": "{{doc-specialpagesummary|userrights}}",
        "userrights-lookup-user": "Label text when managing user rights ([[Special:UserRights]])",
        "userrights-user-editname": "Displayed on [[Special:UserRights]].",
-       "editusergroup": "Button name, in page [[Special:Userrights]] (only available to administrators), in the section named {{MediaWiki:userrights-lookup-user}}.\n\n{{Identical|Edit user groups}}",
+       "editusergroup": "Button name, in page [[Special:Userrights]] (only available to administrators), in the section named {{MediaWiki:userrights-lookup-user}}.\n\n{{Identical|Edit user groups}}.\nParameters:\n* $1 - username, for GENDER support",
        "editinguser": "Appears on [[Special:UserRights]]. Parameters:\n* $1 - a plaintext username\n* $2 - user tool links. e.g. \"(Talk | contribs | block | send email)\"",
        "userrights-editusergroup": "Parameter:\n* $1 - (Optional) a username, can be used for GENDER\n{{Identical|Edit user groups}}",
-       "saveusergroups": "Button text when editing user groups",
+       "saveusergroups": "Button text when editing user groups.\nParameters:\n* $1 - username, for GENDER support",
        "userrights-groupsmember": "Used when editing user groups in [[Special:Userrights]].\n\nThe message is followed by a list of group names.\n\nParameters:\n* $1 - (Optional) the number of items in the list following the message, for PLURAL\n* $2 - (Optional) the user name, for GENDER",
        "userrights-groupsmember-auto": "Used when editing user groups in [[Special:Userrights]]. The message is followed by a list of group names.\n\n\"Implicit\" is for groups that the user was automatically added to (such as \"autoconfirmed\"); cf. {{msg-mw|userrights-groupsmember}}\n\nParameters:\n* $1 - (Optional) the number of items in the list following the message, for PLURAL\n* $2 - (Optional) the user name, for GENDER",
        "userrights-groupsmember-type": "{{optional}}\nParameters:\n* $1 - list of group names\n* $2 - list of group member names. Used with labels {{msg-mw|Userrights-groupsmember}} and {{msg-mw|Userrights-groupsmember-auto}}",
        "right-createpage": "{{doc-right|createpage}}\nBasic right to create pages. The right to edit discussion/talk pages is {{msg-mw|right-createtalk}}.",
        "right-createtalk": "{{doc-right|createtalk}}\nBasic right to create discussion/talk pages. The right to edit other pages is {{msg-mw|right-createpage}}.",
        "right-createaccount": "{{doc-right|createaccount}}\nThe right to [[Special:CreateAccount|create a user account]].",
+       "right-autocreateaccount": "{{doc-right|autocreateaccount}}\nThe right to automatically create an account from an external source (e.g. CentralAuth).",
        "right-minoredit": "{{doc-right|minoredit}}\nThe right to use the \"This is a minor edit\" checkbox. See {{msg-mw|minoredit}} for the message used for that checkbox.",
        "right-move": "{{doc-right|move}}\nThe right to move any page that is not protected from moving.\n{{Identical|Move page}}",
        "right-move-subpages": "{{doc-right|move-subpages}}",
        "right-blockemail": "{{doc-right|blockemail}}",
        "right-hideuser": "{{doc-right|hideuser}}\nThis user right is part of the [[mw:RevisionDelete|RevisionDelete]] feature.\nIt can be given to the group {{msg-mw|group-suppress}}, although that group is disabled by default.\n\nSee also\n* {{msg-mw|right-suppressionlog}}\n* {{msg-mw|right-suppressrevision}}\n* {{msg-mw|right-deletelogentry}}\n* {{msg-mw|right-deleterevision}}",
        "right-ipblock-exempt": "{{doc-right|ipblock-exempt}}\nThis user automatically bypasses IP blocks, auto-blocks and range blocks - so I presume - but I am uncertain",
-       "right-proxyunbannable": "{{doc-right|proxyunbannable}}",
        "right-unblockself": "{{doc-right|unblockself}}",
        "right-protect": "{{doc-right|protect}}",
        "right-editprotected": "{{doc-right|editprotected}}\nRefers to {{msg-mw|Protect-level-sysop}}.\n\nSee also:\n* {{msg-mw|Right-editsemiprotected}}",
        "right-managechangetags": "{{doc-right|managechangetags}}",
        "right-applychangetags": "{{doc-right|applychangetags}}",
        "right-changetags": "{{doc-right|changetags}}",
+       "grant-generic": "Used if the grant name is not defined. Parameters:\n* $1 - grant name\n\nDefined grants (grant name refers: blockusers, createeditmovepage, ...):\n* {{msg-mw|grant-checkuser}}\n* {{msg-mw|grant-blockusers}}\n* {{msg-mw|grant-createaccount}}\n* {{msg-mw|grant-createeditmovepage}}\n* {{msg-mw|grant-delete}}\n* {{msg-mw|grant-editinterface}}\n* {{msg-mw|grant-editmycssjs}}\n* {{msg-mw|grant-editmyoptions}}\n* {{msg-mw|grant-editmywatchlist}}\n* {{msg-mw|grant-editpage}}\n* {{msg-mw|grant-editprotected}}\n* {{msg-mw|grant-highvolume}}\n* {{msg-mw|grant-oversight}}\n* {{msg-mw|grant-patrol}}\n* {{msg-mw|grant-protect}}\n* {{msg-mw|grant-rollback}}\n* {{msg-mw|grant-sendemail}}\n* {{msg-mw|grant-uploadeditmovefile}}\n* {{msg-mw|grant-uploadfile}}\n* {{msg-mw|grant-basic}}\n* {{msg-mw|grant-viewdeleted}}\n* {{msg-mw|grant-viewmywatchlist}}",
+       "grant-group-page-interaction": "{{Related|grant-group}}",
+       "grant-group-file-interaction": "{{Related|grant-group}}",
+       "grant-group-watchlist-interaction": "{{Related|grant-group}}",
+       "grant-group-email": "{{Related|grant-group}}\n{{Identical|E-mail}}",
+       "grant-group-high-volume": "{{Related|grant-group}}",
+       "grant-group-customization": "{{Related|grant-group}}",
+       "grant-group-administration": "{{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}}",
        "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-createpage": "{{Doc-action|createpage}}\n{{Identical|Create page}}",
        "action-createtalk": "{{Doc-action|createtalk}}",
        "action-createaccount": "{{Doc-action|createaccount}}",
+       "action-autocreateaccount": "{{Doc-action|autocreateaccount}}",
        "action-history": "{{Doc-action|history}}",
        "action-minoredit": "{{Doc-action|minoredit}}",
        "action-move": "{{Doc-action|move}}",
        "upload-too-many-redirects": "Error message shown when uploading a file via URL, if the upload failed because the URL returned too many redirects.",
        "upload-http-error": "Parameters:\n* $1 - error message",
        "upload-copy-upload-invalid-domain": "Error message shown if a user is trying to upload (i.e. copy) a file from a website that is not in $wgCopyUploadsDomains (if set).\n\nSee also:\n* {{msg-mw|http-invalid-url}}\n* {{msg-mw|tmp-create-error}}\n* {{msg-mw|tmp-write-error}}",
+       "upload-foreign-cant-upload": "Error message shown when a user is trying to upload a file to foreign repository that is not configured to receive file uploads from current wiki.",
        "upload-dialog-title": "Title of the upload dialog box\n{{Identical|Upload file}}",
        "upload-dialog-button-cancel": "Button to cancel the dialog\n{{Identical|Cancel}}",
        "upload-dialog-button-done": "Button to close the dialog once upload is complete\n{{Identical|Done}}",
-       "upload-dialog-button-save": "Button to save the file\n{{Identical|Save}}",
-       "upload-dialog-button-upload": "Button to initiate upload\n{{Identical|Upload}}",
+       "upload-dialog-button-save": "Button to save the file after upload finishes and metadata is filled out, part 2 of a multi-step upload form\n{{Identical|Save}}",
+       "upload-dialog-button-upload": "Button to initiate upload, part 1 of a multi-step upload form\n{{Identical|Upload}}",
        "upload-form-label-select-file": "Label for the select file widget\n{{Identical|Select file}}",
        "upload-form-label-infoform-title": "Title for the information form\n{{Identical|Detail}}",
        "upload-form-label-infoform-name": "Label for the file name input\n{{Identical|Name}}",
+       "upload-form-label-infoform-name-tooltip": "The tooltip documenting the title field for the file - used as the filename on-wiki.\n\nIdentical to {{msg-mw|mwe-upwiz-tooltip-title}}.",
        "upload-form-label-infoform-description": "Label for the file description input\n{{Identical|Description}}",
+       "upload-form-label-infoform-description-tooltip": "The tooltip documenting the description fields on the details page.\n\nIdentical to {{msg-mw|mwe-upwiz-tooltip-description}}.",
        "upload-form-label-usage-title": "Title for the insert form showing how to use the uploaded item.\n{{Identical|Usage}}",
        "upload-form-label-usage-filename": "Label for the file name input\n{{Identical|Filename}}",
        "foreign-structured-upload-form-label-own-work": "[[File:Cross-wiki media upload dialog, December 2015 AB test option 1.png|thumb]] Label for own work confirmation checkbox",
        "log-title-wildcard": "* Appears in: [[Special:Log]]\n* Description: A check box to enable prefix search option",
        "showhideselectedlogentries": "Text of the button which brings up the [[mw:RevisionDelete|RevisionDelete]] menu on [[Special:Log]].",
        "log-edit-tags": "Text of button used to access change tagging interface. For more information on tags see [[mw:Manual:Tags]].",
+       "checkbox-select": "Parameters:\n* $1 - three links: {{msg-mw|checkbox-all}}, {{msg-mw|checkbox-none}}, and {{msg-mw|checkbox-invert}} which respectively selects all pages, de-selects all, and inverts the current selection state\n{{Identical|Select}}",
+       "checkbox-all": "Text of button used to mark all revisions or log entries as selected in a list.\n{{Identical|All}}",
+       "checkbox-none": "Text of button used to mark all revisions or log entries as unselected in a list.\n{{Identical|None}}",
+       "checkbox-invert": "Text of button used to invert the currently-selected-state of all revisions or log entries in a list.\n{{Identical|Invert}}",
        "allpages": "{{doc-special|AllPages}}\nFirst part of the navigation bar for the special page [[Special:AllPages]] and [[Special:PrefixIndex]].\nThe other parts are {{msg-mw|Prevpage}} and {{msg-mw|Nextpage}}.\n{{Identical|All pages}}",
        "allpages-summary": "{{doc-specialpagesummary|allpages}}",
        "nextpage": "Third part of the navigation bar for the special page [[Special:AllPages]] and [[Special:PrefixIndex]]. $1 is a page title. The other parts are {{msg-mw|Allpages}} and {{msg-mw|Prevpage}}.\n\n{{Identical|Next page}}",
        "listgrouprights-namespaceprotection-header": "Shown on [[Special:ListGroupRights]] as the header for the namespace restrictions table.",
        "listgrouprights-namespaceprotection-namespace": "Shown on [[Special:ListGroupRights]] as the 'namespace' column header for the namespace restrictions table.\n{{Identical|Namespace}}",
        "listgrouprights-namespaceprotection-restrictedto": "Shown on [[Special:ListGroupRights]] as the \"right(s) allowing user to edit\" column header for the namespace restrictions table.",
+       "listgrants": "The name of the special page [[Special:ListGrants]].\n{{Identical|Grant}}",
+       "listgrants-summary": "Explanatory text shown at the top of the grant/rights mapping table.\n\nRefers to {{msg-mw|Listgrouprights-helppage}}.",
+       "listgrants-grant": "Used as table header for the grant/rights mapping table.\n{{Identical|Grant}}",
+       "listgrants-rights": "Used as table header for the grant/rights mapping table.\n{{Identical|Right}}",
        "trackingcategories": "[[Special:TrackingCategories]] page implementing list of Tracking categories [[mw:Special:MyLanguage/Help:Tracking categories|tracking category]].\n{{Identical|Tracking category}}",
        "trackingcategories-summary": "Description for [[Special:TrackingCategories]] page [[mw:Help:Tracking categories|tracking category]]",
        "trackingcategories-msg": "Header for the message column of the table on [[Special:TrackingCategories]]. This column lists the mediawiki message that controls the tracking category in question.\n{{Identical|Tracking category}}",
        "unblock-summary": "{{doc-specialpagesummary|unblock}}",
        "blockip": "Used as the text of a link in the sidebar toolbox. Clicking this link takes you to [[Special:Block]], with a relevant username or IP address (e.g. \"Username\" on [[User talk:Username]], [[Special:Contributions/Username]], etc.) already filled in.\n\nParameters:\n* $1 - username, for GENDER support\n{{Identical|Block user}}",
        "blockip-legend": "Legend/Header for the fieldset around the input form of [[Special:Block]].\n\n{{Identical|Block user}}",
-       "blockiptext": "Used in the {{msg-mw|Blockip}} form in [[Special:Block]].\n\nRefers to {{msg-mw|Policy-url}}.\n\nThis message may follow the message {{msg-mw|Ipb-otherblocks-header}} and other block messages.\n\nSee also:\n* {{msg-mw|Unblockiptext}}",
+       "blockiptext": "Used in the {{msg-mw|Blockip}} form in [[Special:Block]].\n\nRefers to {{msg-mw|Policy-url}}.\n\nThis message may follow the message {{msg-mw|Ipb-otherblocks-header}} and other block messages.\n\nParameters:\n* $1 - CIDR suffix of the largest allowed IPv4 block (as an integer)\n* $2 - CIDR suffix of the largest allowed IPv6 block (as an integer)\n\nSee also:\n* {{msg-mw|Unblockiptext}}",
        "ipaddressorusername": "{{Identical|IP address or username}}",
        "ipbexpiry": "{{Identical|Expiry}}",
        "ipbreason": "Label of the block reason dropdown in [[Special:BlockIP]] and the unblock reason textfield in [{{fullurl:Special:IPBlockList|action=unblock}} Special:IPBlockList?action=unblock].\n\n{{Identical|Reason}}",
        "block-log-flags-hiddenname": "Used as a block log flag in [[Special:Log/block]] and in [[Special:Block]].\n\n{{Related|Block-log-flags}}",
        "range_block_disabled": "Used as error message in [[Special:Block]].\n\nSee also:\n* {{msg-mw|Range block disabled}}\n* {{msg-mw|Ip range invalid}}\n* {{msg-mw|Ip range toolarge}}",
        "ipb_expiry_invalid": "Used as error message in [[Special:Block]].",
+       "ipb_expiry_old": "Used as error message in [[Special:Block]], if the expiry time is in the past.\n{{Identical|protect_expiry_old}}",
        "ipb_expiry_temp": "Warning message displayed on [[Special:BlockIP]] if the option \"hide username\" is selected but the expiry time is not infinite.",
        "ipb_hide_invalid": "Used as error message in [[Special:Block]].\n* $1 - Number of edits (Value of [[mw:Manual:$wgHideUserContribLimit]])",
        "ipb_already_blocked": "{{Identical|$1 is already blocked}}",
        "export-download": "A label of checkbox option in [[Special:Export]]\n\nSee also:\n* {{msg-mw|Exportlistauthors}}",
        "export-templates": "A label of checkbox option in [[Special:Export]]",
        "export-pagelinks": "This is an input in [[Special:Export]]",
+       "export-manual": "The label for the textarea input on [[Special:Export]]",
        "allmessages": "The title of the special page [[Special:AllMessages]].",
        "allmessagesname": "Used on [[Special:Allmessages]] meaning \"the name of the message\".\n{{Identical|Name}}",
        "allmessagesdefault": "The header for the lower row of each column in the table of [[Special:AllMessages]].",
        "tooltip-t-recentchangeslinked": "Used as tooltip for the link {{msg-mw|Recentchangeslinked}}.\n\nSee also:\n* {{msg-mw|Recentchangeslinked}}\n* {{msg-mw|Accesskey-t-recentchangeslinked}}\n* {{msg-mw|Tooltip-t-recentchangeslinked}}",
        "tooltip-feed-rss": "Used as tooltip for RSS feed link.\n\nSee also:\n* {{msg-mw|Feed-rss}}\n* {{msg-mw|Accesskey-feed-rss}}\n* {{msg-mw|Tooltip-feed-rss}}",
        "tooltip-feed-atom": "Used as tooltip for Atom feed link.\n\nSee also:\n* {{msg-mw|Feed-atom}}\n* {{msg-mw|Accesskey-feed-atom}}\n* {{msg-mw|Tooltip-feed-atom}}",
-       "tooltip-t-contributions": "Tooltip shown when hovering over {{msg-mw|Contributions}} in the toolbox.\n\nSee also:\n* {{msg-mw|Contributions}}\n* {{msg-mw|Accesskey-t-contributions}}\n* {{msg-mw|Tooltip-t-contributions}}",
-       "tooltip-t-emailuser": "Tooltip shown when hovering over the {{msg-mw|Emailuser}} link in the toolbox (sidebar, below).\n\nSee also:\n* {{msg-mw|Emailuser}}\n* {{msg-mw|Accesskey-t-emailuser}}\n* {{msg-mw|Tooltip-t-emailuser}}",
+       "tooltip-t-contributions": "Tooltip shown when hovering over {{msg-mw|Contributions}} in the toolbox.\n\nParameters:\n* $1 - Name of the user\n\nSee also:\n* {{msg-mw|Contributions}}\n* {{msg-mw|Accesskey-t-contributions}}\n* {{msg-mw|Tooltip-t-contributions}}",
+       "tooltip-t-emailuser": "Tooltip shown when hovering over the {{msg-mw|Emailuser}} link in the toolbox (sidebar, below).\n\nParameters:\n* $1 - Name of the user\n\nSee also:\n* {{msg-mw|Emailuser}}\n* {{msg-mw|Accesskey-t-emailuser}}\n* {{msg-mw|Tooltip-t-emailuser}}",
        "tooltip-t-info": "Tooltip shown when hovering over the {{msg-mw|pageinfo-toolboxlink}} link in the toolbox (sidebar, below).",
        "tooltip-t-upload": "Tooltip shown when hovering over the link to upload files shown in the side bar menu on all pages.\n\nSee also:\n* {{msg-mw|Upload}}\n* {{msg-mw|Accesskey-t-upload}}\n* {{msg-mw|Tooltip-t-upload}}\n{{Identical|Upload file}}",
        "tooltip-t-specialpages": "The tooltip when hovering over the link {{msg-mw|Specialpages}} going to a list of all special pages available in the wiki.\n\nSee also:\n* {{msg-mw|Specialpages}}\n* {{msg-mw|Accesskey-t-specialpages}}\n* {{msg-mw|Tooltip-t-specialpages}}",
        "markaspatrolleddiff": "{{doc-actionlink}}\nSee also:\n* {{msg-mw|Markaspatrolledtext}}\n{{Identical|Mark as patrolled}}",
        "markaspatrolledlink": "{{notranslate}}\nParameters:\n* $1 - link which has text {{msg-mw|Markaspatrolledtext}}",
        "markaspatrolledtext": "{{doc-actionlink}}\nSee also:\n* {{msg-mw|Markaspatrolleddiff}}",
+       "markaspatrolledtext-file": "Same as markaspatrolledtext, but for files (new versions included) instead of pages.\n\nSee: {{msg-mw|markaspatrolledtext}}",
        "markedaspatrolled": "Used as title of the message {{msg-mw|Markedaspatrolledtext}}, when marking a change as patrolled.\n{{Related|Markedaspatrolled}}",
        "markedaspatrolledtext": "Used when marking a change as patrolled.\n\nThe title for this message is {{msg-mw|Markedaspatrolled}}.\n\nParameters:\n* $1 - page title\n{{Related|Markedaspatrolled}}",
        "rcpatroldisabled": "Used as title of the error message {{msg-mw|Rcpatroldisabledtext}}, when marking a change as patrolled.\n{{Related|Markedaspatrolled}}",
        "newimages-legend": "Caption of the fieldset for the filter on [[Special:NewImages]]\n\n{{Identical|Filter}}",
        "newimages-label": "Caption of the filter editbox on [[Special:NewImages]]",
        "newimages-showbots": "Used as label for a checkbox. When checked, [[Special:NewImages]] will also display uploads by users in the bots group.",
+       "newimages-hidepatrolled": "Used as label for a checkbox. When checked, [[Special:NewImages]] will not display patrolled uploads.\n\nCf. {{msg-mw|tog-hidepatrolled}} and {{msg-mw|apihelp-feedrecentchanges-param-hidepatrolled}}.",
        "noimages": "This is shown on the special page [[Special:NewImages]], when there aren't any recently uploaded files.",
        "ilsubmit": "Used as label for input box in the MIMESearch form on [[Special:MIMESearch]].\n\nSee also:\n* {{msg-mw|Mimesearch|page title}}\n* {{msg-mw|Mimetype|label for input box}}\n{{Identical|Search}}",
        "bydate": "{{Identical|Date}}",
        "scarytranscludefailed-httpstatus": "Identical to {{msg-mw|Scarytranscludefailed}}, but shows the HTTP error which was received. This will not be parsed as wikitext and will appear as is. Parameters:\n* $1 - URL which points to interwiki template\n* $2 - HTTP status, integer (other than 200)",
        "scarytranscludetoolong": "The URL was too long.",
        "deletedwhileediting": "Used as warning in the EditPage page.",
-       "confirmrecreate": "Followed by the checkbox which has the label {{msg-mw|Recreate}}.\n\nParameters:\n* $1 - username\n* $2 - reason\nSee also:\n* {{msg-mw|Confirmrecreate-noreason}}",
-       "confirmrecreate-noreason": "Followed by the checkbox which has the label {{msg-mw|Recreate}}.\n\nParameters:\n* $1 - username\n* $2 - (Unused) reason\nSee also:\n* {{msg-mw|Confirmrecreate}}",
+       "confirmrecreate": "Followed by the checkbox which has the label {{msg-mw|Recreate}}.\n\nParameters:\n* $1 - username, also used for GENDER support\n* $2 - reason\nSee also:\n* {{msg-mw|Confirmrecreate-noreason}}",
+       "confirmrecreate-noreason": "Followed by the checkbox which has the label {{msg-mw|Recreate}}.\n\nParameters:\n* $1 - username, also used for GENDER support\n* $2 - (Unused) reason\nSee also:\n* {{msg-mw|Confirmrecreate}}",
        "recreate": "Text shown when the editor fails to save the page due to it having been deleted since they opened VE. $1 is the message {{msg-mw|ooui-dialog-process-continue}}.",
        "unit-pixel": "{{optional}}\npx is the abbreviation for \"pixel\".",
        "confirm_purge_button": "Used as Submit button text.\n{{Identical|OK}}",
        "signature": "This will be substituted in the signature (~<nowiki></nowiki>~~ or ~~<nowiki></nowiki>~~ excluding timestamp).\n\nParameters:\n* $1 - the username that is currently login\n* $2 - the customized signature which is specified in [[Special:Preferences|user's preferences]] as non-raw\nUse your language default parentheses ({{msg-mw|parentheses}}), but not use the message direct.\n\nSee also:\n* {{msg-mw|Signature-anon}} - signature for anonymous user",
        "signature-anon": "{{notranslate}}\nUsed as signature for anonymous user. Parameters:\n* $1 - username (IP address?)\n* $2 - nickname (IP address?)\nSee also:\n* {{msg-mw|Signature}} - signature for registered user",
        "timezone-utc": "{{optional}}",
+       "timezone-local": "Label to indicate that a time is in the user's local timezone.\n{{Identical|Local}}",
        "duplicate-defaultsort": "See definition of [[w:Sorting|sort key]] on Wikipedia. Parameters:\n* $1 - old default sort key\n* $2 - new default sort key",
        "duplicate-displaytitle": "Warning shown when a page has its display title set multiple times. Parameters:\n* $1 - old display title\n* $2 - new display title",
        "invalid-indicator-name": "Warning shown when the [https://www.mediawiki.org/wiki/Help:Page_status_indicators &lt;indicator name=\"''unique-identifier''\">''content''&lt;/indicator>] parser tag is used incorrectly.",
        "redirect-page": "Description of lookup type for [[Special:Redirect]].\n{{Identical|Page ID}}",
        "redirect-revision": "Description of lookup type for [[Special:Redirect]].\n\nThis means \"Page revision '''ID'''\".",
        "redirect-file": "Description of lookup type for [[Special:Redirect]].\n{{Identical|Filename}}",
+       "redirect-logid": "Description of lookup type for [[Special:Redirect]].\n{{Identical|Log ID}}",
        "redirect-not-exists": "Used as error message in [[Special:Redirect]]",
        "fileduplicatesearch": "Name of special page [[Special:FileDuplicateSearch]].",
        "fileduplicatesearch-summary": "Summary of [[Special:FileDuplicateSearch]]",
        "expand_templates_preview": "{{Identical|Preview}}",
        "expand_templates_preview_fail_html": "Used as error message in Preview section of [[Special:ExpandTemplates]] page.",
        "expand_templates_preview_fail_html_anon": "Used as error message in Preview section of [[Special:ExpandTemplates]] page.",
+       "expand_templates_input_missing": "Used on [[Special:ExpandTemplates]] as an error message, if no input text was provided.",
        "pagelanguage": "Title for page Special:PageLanguage",
        "pagelang-name": "Input label for page name on Special:PageLanguage\n{{Identical|Page}}",
        "pagelang-language": "Language selector label for Special:PageLanguage\n{{Identical|Language}}",
        "pagelang-use-default": "Radio label for selector on Special:PageLanguage for default language",
        "pagelang-select-lang": "Radio label for selector on Special:PageLanguage for language selection\n{{Identical|Select language}}",
+       "pagelang-submit": "Submit button label for Special:PageLanguage form\n{{Identical|Submit}}",
        "right-pagelang": "{{Doc-right|pagelang}}\nRight to change page language on Special:PageLanguage",
        "action-pagelang": "{{Doc-action|pagelang}}",
        "log-name-pagelang": "Display entry for log name for changes in page language in Special:Log.",
        "mediastatistics-summary": "Used to explain that this page only does statistics over current versions of files. \"Old\" versions of files and deleted files are not counted.",
        "mediastatistics-nfiles": "{{optional}}\nEntry in table on [[Special:MediaStatistics]] that gives total number of files. $1 - number of files. $2 - percentage of total files that is this type (percent will be formatted to have about 3 interesting digits. e.g. 0.121 or 10.2)",
        "mediastatistics-nbytes": "Combined space of this type of file. Bytes and \"human units\" are shown so that users can better get a sense of magnitude when making comparisons.\n*$1 - total space in bytes.\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)\n*$3 - What percentage of the space all uploads take up does this file take up.",
-       "mediastatistics-bytespertype": "Combined space of one section of [[Special:MediaStatistics]]. \n*$1 - total space in bytes",
-       "mediastatistics-allbytes": "Combined space of all uploaded files. \n*$1 - total space in bytes",
+       "mediastatistics-bytespertype": "Combined space of one section of [[Special:MediaStatistics]]. \n*$1 - total space in bytes\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)\n*$3 - What percentage of the space all uploads take up does this file take up.",
+       "mediastatistics-allbytes": "Combined space of all uploaded files. \n*$1 - total space in bytes\n*$2 - total space in \"human units\" (i.e. KB, MB, GB, etc)",
        "mediastatistics-table-mimetype": "Header for table on Special:MediaStatistics. Column that lists MIME types (The values in this column will look like 'image/jpeg', and be linked to Special:MIMESearch).",
        "mediastatistics-table-extensions": "Header for column in tables on [[Special:MediaStatistics]] that lists possible extensions for a given file type. (The values in this column will be a comma separated list of file extensions, such as '.webm' or '.png, .apng').",
        "mediastatistics-table-count": "Column header on Special:MediaStatistics for the number of files column. The headers in this column use {{msg-mw|mediastatistics-nfiles}}.",
        "mediastatistics-header-text": "Header on [[Special:MediaStatistics]] for file types that are in the text category. This includes simple text formats, including plain text formats, json, csv, and xml. Source code of compiled programming languages may be included here in the future, but isn't currently.",
        "mediastatistics-header-executable": "Header on [[Special:MediaStatistics]] for file types that are in the executable category. This includes things like source files for interpreted programming language (Shell scripts, javascript, etc).",
        "mediastatistics-header-archive": "Header on [[Special:MediaStatistics]] for file types that are in the archive category. Includes things like tar, zip, gzip etc.",
+       "mediastatistics-header-total": "Header on [[Special:MediaStatistics]] for a summary of all file types.",
        "json-warn-trailing-comma": "A warning message notifying that JSON text was automatically corrected by removing erroneous commas.\n\nParameters:\n* $1 - number of commas that were removed\n{{Related|Json-error}}",
        "json-error-unknown": "User error message when there’s an unknown error.\n\nThis error is shown if we received an unexpected value from PHP. See http://php.net/manual/en/function.json-last-error.php\n\nParameters:\n* $1 - integer error code\n{{Related|Json-error}}",
        "json-error-depth": "User error message when the maximum stack depth is exceeded.\nSee http://php.net/manual/en/function.json-last-error.php\n{{Related|Json-error}}",
        "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}}."
+       "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}}.",
+       "sessionprovider-nocookies": "Used to inform the user that sessions may be missing due to lack of cookies.",
+       "randomrootpage": "{{doc-special|RandomRootPage}}"
 }
index c0cb0a8..cab7cf2 100644 (file)
@@ -11,7 +11,8 @@
                        "Davent",
                        "아라",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Translaziuns"
                ]
        },
        "tog-underline": "Suttastritgar colliaziuns:",
        "createaccountreason": "Motiv:",
        "createacct-reason": "Motiv",
        "createacct-reason-ph": "Tes motiv per crear in auter conto",
-       "createacct-captcha": "Controlla da segirezza",
-       "createacct-imgcaptcha-ph": "Endatescha il text che vesas survart",
        "createacct-submit": "Crear tes conto",
        "createacct-another-submit": "Crear in auter conto",
        "createacct-benefit-heading": "{{SITENAME}} exista grazia a persunas sco ti.",
        "passwordreset-emailtext-ip": "Insatgi (probablamain ti, da l'adressa IP $1) ha dumandà da redefinir il pled-clav per la pagina {{SITENAME}} ($4). \n{{PLURAL:$3|Il suandant conto d'utilisader è collià|Ils suandants contos d'utilisader èn colliads}} cun questa adressa d'e-mail:\n\n$2\n\n{{PLURAL:$3|Quest pled-clav temporar|Quests pleds-clav temporars}} èn valids {{PLURAL:$5|in di|$5 dis}}.\nTi duessas t'annunziar ussa e tscherner in nov pled-clav. Sch'enzatgi auter ha empustà quests novs pleds-clav u sche ti ta regordas puspè da tes pled-clav original e na vuls betg pli midar el, pos ti ignorar quest messadi e cuntinuar d'utilisar tes pled-clav original.",
        "passwordreset-emailtext-user": "L'utilisader $1 sin {{SITENAME}} ha dumandà da redefinir il pled-clav per {{SITENAME}} ($4). \n{{PLURAL:$3|Il suandant conto d'utilisader è collià|Ils suandants contos d'utilisader èn colliads}} cun questa adressa dad e-mail:\n\n$2\n\n{{PLURAL:$3|Quest pled-clav temporar|Quests pled-clav temporars}} èn valids {{PLURAL:$5|in di|$5 dis}}.\nTi duessas t'annunziar ussa e tscherner in nov pled-clav. Sche ti na levas betg quests novs pleds-clav u sche ti ta regordas puspè da tes pled-clav original e na vuls betg pli midar il pled-clav pos ti ignorar quest messadi e cuntinuar dad utilisar tes pled-clav original.",
        "passwordreset-emailelement": "Num d'utilisader: \n$1\n\nPled-clav temporar: \n$2",
-       "passwordreset-emailsent": "In e-mail per redefinir il pled-clav è vegnì tramess.",
+       "passwordreset-emailsentemail": "In e-mail per redefinir il pled-clav è vegnì tramess.",
        "passwordreset-emailsent-capture": "In e-mail (sco mussà sutvart) per redefinir il pled-clav è vegnì tramess.",
        "passwordreset-emailerror-capture": "In e-mail (sco mussà sutvart) per redefinir il pled-clav è vegnì generà ma n'ha betg pudì envià a l'{{GENDER:$2|utilisader|utilisadra}}: $1",
        "changeemail": "Midar l'adressa dad e-mail",
        "prefs-displayrc": "Opziuns da visualisar",
        "prefs-displaywatchlist": "Opziuns da visualisar",
        "prefs-diffs": "Cumparegliaziun da versiuns",
-       "email-address-validity-valid": "L'adressa da e-mail para dad esser valida",
-       "email-address-validity-invalid": "Endatescha ina adressa dad e-mail valida",
        "userrights": "Administraziun da dretgs d'utilisaders",
        "userrights-lookup-user": "Administrar gruppas d'utilisaders",
        "userrights-user-editname": "Inditgescha in num d'utilisader:",
        "wlheader-showupdated": "Paginas ch'èn vegnidas modifitgadas suenter che ti has vis els la davosa giada èn mussads '''grass'''",
        "wlnote": "Sutvart {{PLURAL:$1|è l'ultima midada|èn las ultimas '''$1''' midadas}} entaifer {{PLURAL:$2|l'ultima ura|las ultimas '''$2''' uras}}. Actualisà ils $3 las $4.",
        "wlshowlast": "Mussar: las ultimas $1 uras, ils ultims $2 dis u .",
+       "watchlistall2": "tut",
        "watchlist-options": "Opziuns per la glista d'observaziun",
        "watching": "observ...",
        "unwatching": "observ betg pli...",
        "movenosubpage": "Questa pagina n'ha naginas sutpaginas.",
        "movereason": "Motiv:",
        "revertmove": "spustar anavos",
-       "delete_and_move": "Stizzar e spustar",
        "delete_and_move_text": "==Stizzar necessari==\n\nL'artitgel da destinaziun \"[[:$1]]\" exista gia. Vul ti stizzar el per far plaz per spustar?",
        "delete_and_move_confirm": "Gea, stizzar il artitgel da destinaziun per spustar",
        "delete_and_move_reason": "Stizzà per far plaz per spustar da \"[[$1]]\"",
        "api-error-filetype-banned-type": "$1 {{PLURAL:$4|n'è betg in tip da datoteca lubì|n'èn betg tips da datoteca lubids}}. Lubidas èn datotecas {{PLURAL:$3|dal tip|dals tips}} $2.",
        "api-error-filetype-missing": "Il num da datoteca n'ha betg ina finiziun da datoteca.",
        "api-error-hookaborted": "La midada che ti has empruvà da far è vegnida interrutta dad ina extensiun.",
-       "api-error-http": "Errur interna: Betg pussaivel da connectar cun il server.",
+       "api-error-http": "Errur interna: Impussibel da connectar cun il server.",
        "api-error-illegal-filename": "Il num da datoteca n'è betg lubì.",
        "api-error-internal-error": "Errur interna: Insatge n'ha betg funcziunà durant transmetter tia datoteca en la vichi.",
        "api-error-invalid-file-key": "Errur interna: La datoteca n'è betg vegnida chattada en la memoria temporara.",
index fb46162..ccac1fe 100644 (file)
        "october-date": "$1 octombrie",
        "november-date": "$1 noiembrie",
        "december-date": "$1 decembrie",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Categorie|Categorii}}",
        "category_header": "Pagini din categoria „$1”",
        "subcategories": "Subcategorii",
        "virus-scanfailed": "scanare eșuată (cod $1)",
        "virus-unknownscanner": "antivirus necunoscut:",
        "logouttext": "'''Acum sunteți deconectat.'''\n\nȚineți minte că anumite pagini pot fi în continuare afișate ca și când ați fi autentificat până când curățați memoria cache a navigatorului.",
+       "cannotlogoutnow-title": "Nu se poate deconecta acum",
+       "cannotlogoutnow-text": "Deconectarea nu este posibilă când se utilizează $1.",
        "welcomeuser": "Bun venit, $1!",
        "welcomecreation-msg": "Contul dumneavoastră a fost creat.\nNu uitați să vă modificați [[Special:Preferences|preferințele]] pentru {{SITENAME}}.",
        "yourname": "Nume de utilizator:",
        "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ă",
+       "cannotloginnow-title": "Nu se poate conecta acum",
+       "cannotloginnow-text": "Conectarea nu este posibilă când se utilizează $1.",
        "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.",
        "resetpass_submit": "Setează parola și autentifică",
        "changepassword-success": "Parola dumneavoastră a fost schimbată cu succes!",
        "changepassword-throttled": "Ați avut prea multe încercări recente de a vă autentifica.\nVă rugăm să așteptați $1 până să reîncercați.",
+       "botpasswords": "Parole roboți",
+       "botpasswords-summary": "<em>Parolele de roboți</em> permit accesul la un cont de utilizator prin intermediul API-ului fără utilizarea identificatorilor de conectare principali ai contului. Este posibil ca drepturile de utilizator disponibile după conectarea cu parole de roboți să fie restricționate.\n\nDacă nu știți exact de ce ați recurge la această metodă, probabil ar trebui să nu o faceți. Nimeni nu ar trebui să vă ceară vreodată să generați acest tip de parolă și să le-o furnizați.",
+       "botpasswords-disabled": "Parolele de roboți sunt dezactivate.",
        "resetpass_forbidden": "Parolele nu pot fi schimbate.",
        "resetpass-no-info": "Trebuie să fiți autentificat pentru a accesa această pagină direct.",
        "resetpass-submit-loggedin": "Modifică parola",
        "passwordreset-emailtext-ip": "Cineva (probabil dumneavoastră, de la adresa IP $1) a solicitat resetarea parolei \npentru {{SITENAME}} ($4). {{PLURAL:$3|Următorul cont este asociat|Următoarele conturi sunt asociate}}\ncu această adresă de e-mail:\n\n$2\n\n{{PLURAL:$3|Această parolă temporară va|Aceste parole temporare vor}} expira {{PLURAL:$5|într-o zi|în $5 zile}}.\nAr trebui să vă autentificați și să schimbați parola acum. Dacă altcineva a făcut această cerere \nsau dacă v-ați reamintit parola inițială și nu mai doriți să o schimbați,\nputeți ignora acest mesaj, continuând să utilizați vechea parolă.",
        "passwordreset-emailtext-user": "Utilizatorul $1 de pe {{SITENAME}} a solicitat o resetare a parolei dumneavoastră pentru {{SITENAME}} ($4). Următorul utilizator are {{PLURAL:$3|contul asociat|conturile asociate}} cu această adresă de e-mail:\n\n$2\n\n{{PLURAL:$3|Această parolă temporară va|Aceste parole temporare vor}} expira {{PLURAL:$5|într-o zi|în $5 zile}}.\nAr trebui să vă autentificați și să alegeți acum o nouă parolă. Dacă altcineva a făcut această solicitare, ori dacă v-ați reamintit parola originală și nu mai doriți modificarea ei, puteți ignora acest mesaj, continuând cu vechea parolă.",
        "passwordreset-emailelement": "Nume de utilizator: \n$1\n\nParolă temporară: \n$2",
-       "passwordreset-emailsentemail": "Dacă aceasta este o adresă de e-mail înregistrată pentru contul dumneavoastră, atunci se va trimite un e-mail de resetare a parolei.",
-       "passwordreset-emailsentusername": "Dacă există o adresă de e-mail înregistrată pentru contul dumneavoastră, atunci se va trimite un e-mail de resetare a parolei.",
+       "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",
        "changeemail": "Modificare sau înlăturare adresă de e-mail",
        "right-blockemail": "Blochează alți utilizatori la trimiterea e-mailurilor",
        "right-hideuser": "Blochează un nume de utilizator, ascunzându-l de public",
        "right-ipblock-exempt": "Ocolește blocarea adresei IP, autoblocările și blocarea intervalelor IP",
-       "right-proxyunbannable": "Trece peste blocarea automată a proxy-urilor",
        "right-unblockself": "Se deblochează singur",
        "right-protect": "Schimbă nivelurile de protejare și modifică pagini protejate în cascadă",
        "right-editprotected": "Modifică pagini protejate ca „{{int:protect-level-sysop}}”",
        "upload-form-label-select-file": "Selectează fișier",
        "upload-form-label-infoform-title": "Detalii",
        "upload-form-label-infoform-name": "Nume",
+       "upload-form-label-infoform-name-tooltip": "Un titlu unic, descriptiv, care va deveni și numele fișierului. Puteți folosi limbaj simplu cu spații. Nu includeți extensia fișierului.",
        "upload-form-label-infoform-description": "Descriere",
+       "upload-form-label-infoform-description-tooltip": "Descrieți pe scurt orice este notabil despre lucrare.\nPentru o fotografie, menționați principalele lucruri care sunt reprezentate, evenimentul sau locul.",
        "upload-form-label-usage-title": "Utilizare",
        "upload-form-label-usage-filename": "Numele fișierului",
        "foreign-structured-upload-form-label-own-work": "Aceasta este propria mea operă",
        "wlshowhideanons": "utilizatori anonimi",
        "wlshowhidepatr": "modificări patrulate",
        "wlshowhidemine": "modificările mele",
+       "wlshowhidecategorization": "categorisirea paginilor",
        "watchlist-options": "Opțiuni listă de pagini urmărite",
        "watching": "Se urmărește...",
        "unwatching": "Așteptați...",
        "export-download": "Salvează ca fișier",
        "export-templates": "Include formate",
        "export-pagelinks": "Includere pagini legate de la o adâncime de:",
+       "export-manual": "Adăugați pagini manual:",
        "allmessages": "Toate mesajele",
        "allmessagesname": "Nume",
        "allmessagesdefault": "Textul standard",
        "javascripttest-pagetext-frameworks": "Alegeți unul din următoarele cadre de testare: $1",
        "javascripttest-pagetext-skins": "Alegeți un aspect pentru care să rulați teste:",
        "javascripttest-qunit-intro": "A se vedea [$1 documentația de testare] pe mediawiki.org.",
-       "tooltip-pt-userpage": "Pagina dumneavoastră de utilizator",
+       "tooltip-pt-userpage": "Pagina {{GENDER:|dumneavoastră}} de utilizator",
        "tooltip-pt-anonuserpage": "Pagina de utilizator pentru adresa IP curentă",
-       "tooltip-pt-mytalk": "Pagina dumneavoastră de discuții",
+       "tooltip-pt-mytalk": "Pagina {{GENDER:|dumneavoastră}} de discuții",
        "tooltip-pt-anontalk": "Discuții despre editări pentru adresa IP curentă",
-       "tooltip-pt-preferences": "Preferințele dumneavoastră",
+       "tooltip-pt-preferences": "Preferințele {{GENDER:|dumneavoastră}}",
        "tooltip-pt-watchlist": "Lista paginilor pe care le monitorizați",
-       "tooltip-pt-mycontris": "Listă de contribuții",
+       "tooltip-pt-mycontris": "Lista contribuțiilor {{GENDER:|dumneavoastră}}",
        "tooltip-pt-anoncontribs": "O listă de modificări efectuate de la această adresă IP",
        "tooltip-pt-login": "Sunteți încurajat să vă autentificați, deși acest lucru nu este obligatoriu.",
        "tooltip-pt-logout": "Închide sesiunea de lucru",
        "tooltip-t-recentchangeslinked": "Schimbări recente în legătură cu această pagină",
        "tooltip-feed-rss": "Alimentează fluxul RSS pentru această pagină",
        "tooltip-feed-atom": "Alimentează fluxul Atom pentru această pagină",
-       "tooltip-t-contributions": "Vezi lista de contribuții ale acestui utilizator",
-       "tooltip-t-emailuser": "Trimite un e-mail acestui utilizator",
+       "tooltip-t-contributions": "Vezi lista de contribuții ale {{GENDER:$1|acestui utilizator|acestei utilizatoare}}",
+       "tooltip-t-emailuser": "Trimite un e-mail {{GENDER:$1|acestui utilizator|acestei utilizatoare}}",
        "tooltip-t-info": "Mai multe informații despre această pagină",
        "tooltip-t-upload": "Încărcare fișiere",
        "tooltip-t-specialpages": "Lista tuturor paginilor speciale",
        "watchlisttools-edit": "Vezi și modifică lista paginilor urmărite",
        "watchlisttools-raw": "Modifică lista brută a paginilor urmărite",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|discuție]])",
+       "timezone-local": "Local",
        "duplicate-defaultsort": "'''Atenție:''' Cheia de sortare implicită („$2”) o înlocuiește pe precedenta („$1”).",
        "duplicate-displaytitle": "<strong>Atenție:</strong> Titlul afișat „$2” înlocuieşte titlul afișat anterior, „$1”.",
        "invalid-indicator-name": "<strong>Eroare:</strong> Parametrul <code>nume</code> al indicatorilor de stare a paginii nu trebuie să fie gol.",
        "expand_templates_preview": "Previzualizare",
        "expand_templates_preview_fail_html": "<em>Întrucât la {{SITENAME}} este activat HTML brut și a avut loc o pierdere a sesiunii de date, previzualizarea a fost ascunsă ca măsură de precauție împotriva atacurilor prin JavaScript.</em>\n\n<strong>Dacă aceasta este o încercare legitimă de a previzualiza, încercați din nou.</strong>\nDacă nici astfel nu funcționează, încercați să [[Special:UserLogout|închideţi sesiunea]] şi să vă autentificaţi din nou.",
        "expand_templates_preview_fail_html_anon": "<em>Întrucât la {{SITENAME}} este activat HTML brut și nu v-ați autentificat, previzualizarea a fost ascunsă ca măsură de precauție împotriva atacurilor prin JavaScript.</em>\n\n<strong>Dacă aceasta este o încercare legitimă de a previzualiza, [[Special:UserLogin|autentificați-vă]] și încercați din nou.</strong>",
+       "expand_templates_input_missing": "Trebuie să furnizați cel puțin un text ca date de intrare.",
        "pagelanguage": "Selector limbă pagină",
        "pagelang-name": "Pagină",
        "pagelang-language": "Limbă",
        "pagelang-use-default": "Folosește limba implicită",
        "pagelang-select-lang": "Alege limba",
+       "pagelang-submit": "Trimite",
        "right-pagelang": "Modifică limba paginii",
        "action-pagelang": "modificați limba paginii",
        "log-name-pagelang": "Jurnal modificare limbă",
        "mediastatistics": "Statistici multimedia",
        "mediastatistics-summary": "Statistici despre tipurile fișierelor încărcate. Sunt incluse doar cele mai recente versiuni ale fișierelor. Versiunile mai vechi sau șterse ale fișierelor sunt excluse.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 octet|$1 octeți|$1 de octeți}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Dimensiunea totală a fișierului pentru această secțiune: {{PLURAL:$1|$1 octet|$1 octeți|$1 de octeți}} ($2; $3%).",
+       "mediastatistics-allbytes": "Dimensiunea totală pentru toate fișierele: {{PLURAL:$1|$1 octet|$1 octeți|$1 de octeți}} ($2).",
        "mediastatistics-table-mimetype": "Tip MIME",
        "mediastatistics-table-extensions": "Extensii posibile",
        "mediastatistics-table-count": "Număr de fișiere",
        "mediastatistics-header-text": "Text",
        "mediastatistics-header-executable": "Executabile",
        "mediastatistics-header-archive": "Formate comprimate",
+       "mediastatistics-header-total": "Toate fișierele",
        "json-warn-trailing-comma": "$1 {{PLURAL:$1|virgulă|virgule|de virgule}} în exces înlăturat{{PLURAL:$1|ă|e}} din JSON",
        "json-error-unknown": "A apărut o problemă cu JSON. Eroare: $1",
        "json-error-depth": "S-a depășit adâncimea maximă a stivei",
        "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."
+       "api-error-blacklisted": "Vă rugăm să alegeți un alt titlu, mai descriptiv.",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "sesiuni pe bază de module cookie."
 }
index 08ad0bb..d7d17c8 100644 (file)
@@ -86,7 +86,8 @@
                        "Nzeemin",
                        "INS Pirat",
                        "Краснорядцева Елена",
-                       "Frhdkazan"
+                       "Frhdkazan",
+                       "Ядерный Трамвай"
                ]
        },
        "tog-underline": "Подчёркивание ссылок:",
        "october-date": "Октябрь $1",
        "november-date": "Ноябрь $1",
        "december-date": "Декабрь $1",
+       "period-am": "ДП",
+       "period-pm": "ПП",
        "pagecategories": "{{PLURAL:$1|1=Категория|Категории}}",
        "category_header": "Страницы в категории «$1»",
        "subcategories": "Подкатегории",
        "virus-scanfailed": "ошибка сканирования (код $1)",
        "virus-unknownscanner": "неизвестный антивирус:",
        "logouttext": "'''Вы завершили сеанс работы.'''\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": "Произошла ошибка при аутентификации с помощью внешней базы данных или у вас недостаточно прав для внесения изменений в свою внешнюю учётную запись.",
        "resetpass_submit": "Установить пароль и представиться",
        "changepassword-success": "Ваш пароль был успешно изменён!",
        "changepassword-throttled": "Вы сделали слишком много попыток представиться системе.\nПожалуйста, подождите $1 перед тем, как попробовать снова.",
+       "botpasswords": "Пароли ботов",
+       "botpasswords-summary": "<em>Пароли бота</em> позволяют получить доступ к учётной записи пользователя через API без использования логина и пароля главной учётной записи. Права участника при входе с паролем бота могут быть ограничены.\n\nЕсли Вы не знаете, зачем вам это, вероятно, лучше этого не делайте. Никто никогда не должен просить вас, чтобы вы создали и сообщили его.",
+       "botpasswords-disabled": "Пароли бота отключены.",
+       "botpasswords-no-central-id": "Для использования паролей бота вы должны войти в централизованную учётную запись.",
+       "botpasswords-existing": "Существующие пароли бота",
+       "botpasswords-createnew": "Создать новый пароль бота",
+       "botpasswords-editexisting": "Редактировать существующий пароль бота",
+       "botpasswords-label-appid": "Название бота:",
+       "botpasswords-label-create": "Создать",
+       "botpasswords-label-update": "Обновить",
+       "botpasswords-label-cancel": "Отмена",
+       "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": "Пароль бота создан",
+       "botpasswords-created-body": "Пароль бота «$1» был успешно создан.",
+       "botpasswords-updated-title": "Пароль бота обновлён",
+       "botpasswords-updated-body": "Пароль бота «$1» был успешно обновлён.",
+       "botpasswords-deleted-title": "Пароль бота удалён",
+       "botpasswords-deleted-body": "Пароль бота «$1» был удалён.",
+       "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-no-info": "Чтобы обращаться непосредственно к этой странице, вам следует представиться системе.",
        "resetpass-submit-loggedin": "Изменить пароль",
        "passwordreset-emailtext-ip": "Кто-то (возможно, вы, с IP-адреса $1) запросил сброс пароля к вашей учётной записи в проекте {{SITENAME}} ($4).\nС этим адресом электронной почты {{PLURAL:$3|1=связана следующая учётная запись|связаны следующие учётные записи}}:\n\n$2\n\n{{PLURAL:$3|1=Этот временный пароль будет|Эти временные пароли будут}} действовать {{PLURAL:$5|$5 день|$5 дня|$5 дней|1=один день}}.\nВы должны представиться системе и выбрать новый пароль. \nЕсли вы не делали этого запроса, или вспомнили свой исходный пароль и не желаете его менять, \nто можете проигнорировать это сообщение и продолжить использовать свой старый пароль.",
        "passwordreset-emailtext-user": "Участник $1 из проекта {{SITENAME}} запросил сброс пароля для вашей учётной записи в проекте {{SITENAME}} ($4).\nС этим адресом электронной почты {{PLURAL:$3|1=связана следующая учётная запись|связаны следующие учётные записи}}:\n\n$2\n\n{{PLURAL:$3|1=Этот временный пароль будет|Эти временные пароли будут}} действовать {{PLURAL:$5|$5 день|$5 дней|$5 дня|1=один день}}.\nВы должны представиться системе и выбрать новый пароль.\nЕсли вы не делали этого запроса или вспомнили свой исходный пароль и не желаете его менять, \nто можете проигнорировать это сообщение и продолжить использовать свой старый пароль.",
        "passwordreset-emailelement": "Имя участника: \n$1\n\nВременный пароль: \n$2",
-       "passwordreset-emailsentemail": "Если это адрес электронной почты, на которую зарегистрирована ваша учётная запись, вам будет отправлено письмо для сброса пароля.",
-       "passwordreset-emailsentusername": "Если есть соответствующий зарегистрированный адрес электронной почты, будет отправлено письмо для восстановления пароля.",
+       "passwordreset-emailsentemail": "Если это адрес электронной почты связан с вашей учётной записью, вам будет отправлено письмо для сброса пароля.",
+       "passwordreset-emailsentusername": "Если есть адрес электронной почты, связанный с этим именем участника, то будет отправлено письмо для восстановления пароля.",
        "passwordreset-emailsent-capture": "Отправлено электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже.",
        "passwordreset-emailerror-capture": "Было создано электронное письмо с информацией о сбросе пароля, текст которого можно увидеть ниже, однако его не удалось отправить {{GENDER:$2|участнику|участнице}} по следующей причине: $1",
        "changeemail": "Изменить или удалить адрес электронной почты",
        "showhideselectedversions": "Показать/скрыть выбранные версии",
        "editundo": "отменить",
        "diff-empty": "(нет различий)",
-       "diff-multi-sameuser": "(не {{PLURAL:$1|показана одна промежуточная версия|показаны $1 промежуточные версии|показано $1 промежуточных версий}} этого же участника)",
+       "diff-multi-sameuser": "(не {{PLURAL:$1|показана $1 промежуточная версия|показаны $1 промежуточные версии|показано $1 промежуточных версий}} этого же участника)",
        "diff-multi-otherusers": "(не {{PLURAL:$1|показана $1 промежуточная версия|показаны $1 промежуточные версии|показано $1 промежуточных версий}} {{PLURAL:$2|$2 участника|$2 участников}})",
        "diff-multi-manyusers": "({{PLURAL:$1|не показана $1 промежуточная версия, сделанная|не показаны $1 промежуточных версий, сделанных|не показаны $1 промежуточные версии, сделанные}} более чем {{PLURAL:$2|$2 участником|$2 участниками}})",
        "difference-missing-revision": "Не {{PLURAL:$2|1=найдена|найдены}} {{PLURAL:$2|$2 версия|$2 версий|$2 версии|1=одна из версий}} для этого сравнения ($1).\n\nТакое обычно случается при переходе по устаревшей ссылке сравнения версий для страницы, которая была удалена.\nПодробности могут быть в [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} журнале удалений].",
        "prefs-help-gender": "Этот параметр задавать необязательно.\nДвижок использует это значение, чтобы обращаться к вам и упоминать вас в правильном грамматическом роде.\nЭта информация будет общедоступной.",
        "email": "Электронная почта",
        "prefs-help-realname": "Вводить настоящее имя необязательно.\nЕсли вы заполните его, оно может быть использовано для указания авторства ваших работ.",
-       "prefs-help-email": "Адрес электронной почты указывать необязательно, но он будет необходим в том случае, если вы забудете пароль.",
+       "prefs-help-email": "Адрес почты не обязателен, но это единственный способ восстановить забытый пароль.",
        "prefs-help-email-others": "Он также позволит другим участникам связаться с вами по электронной почте с помощью ссылки на вашей персональной странице или на вашей странице обсуждения. При этом ваш адрес электронной почты не будет никому раскрыт.",
        "prefs-help-email-required": "Необходимо указать адрес электронной почты.",
        "prefs-info": "Основные сведения",
        "userrights": "Управление правами участника",
        "userrights-lookup-user": "Управление группами участников",
        "userrights-user-editname": "Введите имя учётной записи:",
-       "editusergroup": "Изменить членство в группах",
+       "editusergroup": "Изменить группы {{GENDER:$1|участника|участницы}}",
        "editinguser": "Изменение прав {{GENDER:$1|участника|участницы}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Изменение членства в группах",
-       "saveusergroups": "Сохранить группы участника",
+       "saveusergroups": "Сохранить группы {{GENDER:$1|участника|участницы}}",
        "userrights-groupsmember": "Состоит в группах:",
        "userrights-groupsmember-auto": "Неявно состоит в группах:",
        "userrights-groups-help": "Вы можете изменить группы, в которые входит этот участник.\n* Если около названия группы стоит отметка, значит участник входит в эту группу.\n* Если отметка не стоит — участник не относится к соответствующей группе.\n* Знак * отмечает, что вы не сможете удалить участника из группы, если добавите его в неё, или наоборот.",
        "group-bot": "Боты",
        "group-sysop": "Администраторы",
        "group-bureaucrat": "Бюрократы",
-       "group-suppress": "РевизоÑ\80Ñ\8b",
+       "group-suppress": "СкÑ\80Ñ\8bваÑ\8eÑ\89ие",
        "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|Ñ\80евизоÑ\80}}",
+       "group-suppress-member": "{{GENDER:$1|Ñ\81кÑ\80Ñ\8bваÑ\8eÑ\89ий}}",
        "grouppage-user": "{{ns:project}}:Участники",
        "grouppage-autoconfirmed": "{{ns:project}}:Автоподтверждённые участники",
        "grouppage-bot": "{{ns:project}}:Боты",
        "grouppage-sysop": "{{ns:project}}:Администраторы",
        "grouppage-bureaucrat": "{{ns:project}}:Бюрократы",
-       "grouppage-suppress": "{{ns:project}}:РевизоÑ\80Ñ\8b",
+       "grouppage-suppress": "{{ns:project}}:СкÑ\80Ñ\8bваÑ\8eÑ\89ие",
        "right-read": "просмотр страниц",
        "right-edit": "правка страниц",
        "right-createpage": "создание страниц, не являющихся обсуждениями",
        "right-createtalk": "создание страниц обсуждений",
        "right-createaccount": "создание новых учётных записей участников",
+       "right-autocreateaccount": "Автоматический вход с помощью внешней учётной записи участника",
        "right-minoredit": "простановка отметки «малое изменение»",
        "right-move": "переименование страниц",
        "right-move-subpages": "переименование страниц с их подстраницами",
        "right-blockemail": "установка запрета на отправку электронной почты",
        "right-hideuser": "запрет имени участника и его сокрытие",
        "right-ipblock-exempt": "обход блокировок по IP, автоблокировок и блокировок диапазонов",
-       "right-proxyunbannable": "обход автоматической блокировки прокси",
        "right-unblockself": "разблокировка себя",
        "right-protect": "изменение уровня защиты страниц и правка каскадно защищённых страниц",
        "right-editprotected": "правка страниц, защищённых как «{{int:protect-level-sysop}}»",
        "right-managechangetags": "создание и удаление [[Special:Tags|меток]] из базы данных",
        "right-applychangetags": "применение [[Special:Tags|меток]] вместе со своими правками",
        "right-changetags": "добавление и удаление произвольных [[Special:Tags|меток]] на отдельных правках и записях в журнале",
+       "grant-generic": "Набор прав «$1»",
+       "grant-group-page-interaction": "Взаимодействие со страницами",
+       "grant-group-file-interaction": "Взаимодействие с медиафайлами",
+       "grant-group-watchlist-interaction": "Взаимодействие с вашим списком наблюдения",
+       "grant-group-email": "Отправка писем",
+       "grant-group-high-volume": "Выполнение действий с высокой интенсивностью",
+       "grant-group-customization": "Настройки и предпочтения",
+       "grant-group-administration": "Выполнение административных действий",
+       "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-editprotected": "Редактирование защищённых страниц",
+       "grant-highvolume": "Редактирование с высокой интенсивностью",
+       "grant-oversight": "Сокрытие правок участников и версий страниц",
+       "grant-patrol": "Патрулирование изменений страниц",
+       "grant-protect": "Защита страниц и снятие защиты",
+       "grant-rollback": "Откат изменений страниц",
+       "grant-sendemail": "Отправка электронной почты другим участникам",
+       "grant-uploadeditmovefile": "Загрузка, замена и переименовывание файлов",
+       "grant-uploadfile": "Загрузка новых файлов",
+       "grant-basic": "Основные права",
+       "grant-viewdeleted": "Просмотр удалённых файлов и страниц",
+       "grant-viewmywatchlist": "Просмотр вашего списка наблюдения",
        "newuserlogpage": "Журнал регистрации участников",
        "newuserlogpagetext": "Список недавно зарегистрировавшихся участников",
        "rightslog": "Журнал прав участника",
        "action-createpage": "создание страниц",
        "action-createtalk": "создание страниц обсуждений",
        "action-createaccount": "создание этой учётной записи",
+       "action-autocreateaccount": "автоматический вход с помощью внешней учётной записи участника",
        "action-history": "просмотр истории этой страницы",
        "action-minoredit": "пометку этой правки как малой",
        "action-move": "переименование этой страницы",
        "rcshowhidemine": "$1 свои правки",
        "rcshowhidemine-show": "Показать",
        "rcshowhidemine-hide": "Скрыть",
-       "rcshowhidecategorization": "$1 категоризацию страницы",
+       "rcshowhidecategorization": "$1 категоризацию страниц",
        "rcshowhidecategorization-show": "Показать",
        "rcshowhidecategorization-hide": "Скрыть",
        "rclinks": "Показать последние $1 изменений за $2 дней<br />$3",
        "upload-form-label-select-file": "Выбрать файл",
        "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": "Коротко опишите всё самое важное об этом произведении. Для фото — укажите, что главное изображено, обстоятельства съёмки или место.",
        "upload-form-label-usage-title": "Использование",
        "upload-form-label-usage-filename": "Имя файла",
        "foreign-structured-upload-form-label-own-work": "Это моя собственная работа",
        "foreign-structured-upload-form-2-label-noderiv": "Оно не должно <strong>содержать чьей-то чужой работы</strong> или быть вдохновлено ей",
        "foreign-structured-upload-form-2-label-useful": "Оно должно быть <strong>образовательным и полезным</strong> для обучения других",
        "foreign-structured-upload-form-2-label-ccbysa": "Вы должны быть согласны на то, чтобы <strong>опубликовать его в Интернете навсегда</strong> под лицензией [https://creativecommons.org/licenses/by-sa/4.0/deed.ru Creative Commons Attribution-ShareAlike 4.0]",
+       "foreign-structured-upload-form-2-label-alternative": "Если не всё вышеперечисленное верно, вы все равно можете загрузить этот файл, используя кнопку [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастера загрузки Викисклада], в том случае, если он доступен под свободной лицензией.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Загружая данный файл, вы подтверждаете, что являетесь владельцем авторских прав на этот файл и безоговорочно согласны загрузить его на Викисклад под лицензией Creative Commons Attribution-ShareAlike 4.0, а также соглашаетесь с [https://wikimediafoundation.org/wiki/Условия_использования Условиями использования].",
        "foreign-structured-upload-form-3-label-question-website": "Вы скачали это изображение с какого-то сайта или, может быть, нашли его через поиск изображений?",
        "foreign-structured-upload-form-3-label-question-ownwork": "Вы создали это изображение (сделали фото, эскиз, чертёж и т. д.) сами?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Содержит ли он работу, принадлежащую кому-то другому (или вдохновлён ей), например, логотип?",
        "foreign-structured-upload-form-3-label-yes": "Да",
        "foreign-structured-upload-form-3-label-no": "Нет",
+       "foreign-structured-upload-form-3-label-alternative": "К сожалению, в данном случае, этот инструмент не поддерживает загрузку данного файла. Вы все равно можете загрузить этот файл, используя [https://commons.wikimedia.org/wiki/Special:UploadWizard Мастер загрузки Викисклада], в том случае, если он доступен под свободной лицензией.",
        "foreign-structured-upload-form-4-label-good": "Используя этот инструмент, вы можете загрузить образовательную графику, которую вы создали, и фотографии, которые вы сняли, если они не содержат работ, принадлежащих кому-то другому.",
+       "foreign-structured-upload-form-4-label-bad": "Вы не можете загружать изображения, найденные в поисковой системе или скачанные с других сайтов.",
        "backend-fail-stream": "Не удалось транслировать файл $1.",
        "backend-fail-backup": "Невозможно сделать резервную копию файла $1.",
        "backend-fail-notexists": "Файл $1 не существует.",
        "log-title-wildcard": "Найти заголовки, начинающиеся с данных символов",
        "showhideselectedlogentries": "Показать/скрыть выбранные записи журнала",
        "log-edit-tags": "Редактировать теги из выбранной записи журнала",
+       "checkbox-select": "Выбор: $1",
+       "checkbox-all": "Все",
+       "checkbox-none": "Ничего",
+       "checkbox-invert": "Инвертировать",
        "allpages": "Все страницы",
        "nextpage": "Следующая страница ($1)",
        "prevpage": "Предыдущая страница ($1)",
        "listgrouprights-namespaceprotection-header": "Ограничения пространства имён",
        "listgrouprights-namespaceprotection-namespace": "Пространство имён",
        "listgrouprights-namespaceprotection-restrictedto": "Права, позволяющие участнику редактировать",
+       "listgrants": "Разрешения",
+       "listgrants-summary": "Ниже приведён список разрешений с указанием на то, к каким связанным пользовательским правам они дают доступ. Участники могут разрешить приложениям использовать свою учётную запись, но с ограниченными правами на основе разрешений, которые участник предоставляет приложению. Однако, приложение, действующее  от имени участника, на самом деле не сможет воспользоваться правами, отсутствующими у учётной записи.\nОб отдельных правах можно получить [[{{MediaWiki:Listgrouprights-helppage}}|дополнительную  информацию]].",
+       "listgrants-grant": "Разрешение",
+       "listgrants-rights": "Права",
        "trackingcategories": "Отслеживающие категории",
        "trackingcategories-summary": "На этой странице перечислены отслеживающие категории, которые автоматически заполняются программным обеспечением MediaWiki. Их можно переименовать, изменив соответствующие системные сообщения в пространстве имён {{ns:8}}.",
        "trackingcategories-msg": "Отслеживающая категория",
        "wlshowhideanons": "анонимных участников",
        "wlshowhidepatr": "проверенные правки",
        "wlshowhidemine": "мои правки",
+       "wlshowhidecategorization": "категоризацию страниц",
        "watchlist-options": "Настройки списка наблюдения",
        "watching": "Добавление в список наблюдения…",
        "unwatching": "Удаление из списка наблюдения…",
        "unblock": "Разблокировка участника",
        "blockip": "Заблокировать {{GENDER:$1|участника}}",
        "blockip-legend": "Блокировка участника",
-       "blockiptext": "Используйте форму ниже, чтобы заблокировать возможность записи с определённого IP-адреса.\nЭто может быть сделано только для предотвращения вандализма и только в соответствии с [[{{MediaWiki:Policy-url}}|правилами]].\nНиже укажите конкретную причину (к примеру, процитируйте некоторые страницы с признаками вандализма).",
+       "blockiptext": "Используйте форму ниже, чтобы заблокировать возможность записи с определённого IP-адреса или имени участника.\nЭто может быть сделано только для предотвращения вандализма и только в соответствии с [[{{MediaWiki:Policy-url}}|правилами]].\nНиже укажите конкретную причину (к примеру, процитируйте некоторые страницы с признаками вандализма).\nВы можете заблокировать диапазоны IP-адресов, используя [https://ru.wikipedia.org/wiki/Бесклассовая_адресация CIDR]-синтаксис. Максимально допустимый диапазон — /$1 для протокола IPv4 и /$2 для протокола IPv6.",
        "ipaddressorusername": "IP-адрес или имя участника:",
        "ipbexpiry": "Закончится через:",
        "ipbreason": "Причина:",
        "block-log-flags-hiddenname": "имя участника скрыто",
        "range_block_disabled": "Администраторам запрещено блокировать диапазоны.",
        "ipb_expiry_invalid": "Недопустимый период действия.",
+       "ipb_expiry_old": "Время окончания — в прошлом.",
        "ipb_expiry_temp": "Блокировки с сокрытием имени участника должны быть бессрочными.",
        "ipb_hide_invalid": "Невозможно скрыть эту учётную запись, с неё сделано более {{PLURAL:$1|одной правки|$1 правок}}.",
        "ipb_already_blocked": "«$1» уже заблокирован.",
        "export-download": "Предложить сохранить как файл",
        "export-templates": "Включить шаблоны",
        "export-pagelinks": "Включить связанные страницы глубиной:",
+       "export-manual": "Добавить страницы вручную:",
        "allmessages": "Системные сообщения",
        "allmessagesname": "Сообщение",
        "allmessagesdefault": "Текст по умолчанию",
        "javascripttest-pagetext-frameworks": "Пожалуйста, выберите одну из следующих сред тестирования: $1",
        "javascripttest-pagetext-skins": "Выберите тему оформления для запуска тестов:",
        "javascripttest-qunit-intro": "См. [$1 документацию по тестированию] на mediawiki.org.",
-       "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-mycontris": "Список {{GENDER:|ваших}} правок",
        "tooltip-pt-anoncontribs": "Список правок, сделанных с этого IP-адреса",
        "tooltip-pt-login": "Здесь можно зарегистрироваться в системе, но это необязательно.",
        "tooltip-pt-logout": "Завершить сеанс работы",
        "tooltip-t-recentchangeslinked": "Последние изменения в страницах, на которые ссылается эта страница",
        "tooltip-feed-rss": "Трансляция в RSS для этой страницы",
        "tooltip-feed-atom": "Трансляция в Atom для этой страницы",
-       "tooltip-t-contributions": "Список страниц, которые изменял этот участник",
-       "tooltip-t-emailuser": "Отправить письмо этому участнику",
+       "tooltip-t-contributions": "Список страниц, которые {{GENDER:$1|изменял этот участник|изменяла эта участница}}",
+       "tooltip-t-emailuser": "Отправить письмо {{GENDER:$1|этому участнику|этой участнице}}",
        "tooltip-t-info": "Подробнее об этой странице",
        "tooltip-t-upload": "Загрузить файлы",
        "tooltip-t-specialpages": "Список служебных страниц",
        "pageinfo-category-files": "Количество файлов",
        "markaspatrolleddiff": "Отметить как проверенную",
        "markaspatrolledtext": "Отметить эту статью как проверенную",
+       "markaspatrolledtext-file": "Пометить эту версию файла как отпатрулированную",
        "markedaspatrolled": "Отмечена как проверенная",
        "markedaspatrolledtext": "Выбранная версия статьи [[:$1]] была отмечена как отпатрулированная.",
        "rcpatroldisabled": "Патрулирование последних изменений запрещено",
        "newimages-legend": "Фильтр",
        "newimages-label": "Имя файла (или его часть):",
        "newimages-showbots": "Показать загрузки ботов",
+       "newimages-hidepatrolled": "Скрыть отпатрулированные загрузки",
        "noimages": "Изображения отсутствуют.",
        "ilsubmit": "Найти",
        "bydate": "по дате",
        "confirmemail_body": "Кто-то (возможно вы) с IP-адресом $1 зарегистрировал\nна сервере проекта {{SITENAME}} учётную запись «$2»,\nуказав этот адрес электронной почты.\n\nЧтобы подтвердить, что эта учётная запись действительно\nпринадлежит вам и включить возможность отправки электронной почты\nс сайта {{SITENAME}}, откройте приведённую ниже ссылку в браузере:\n\n$3\n\nЕсли вы *не* регистрировали подобной учётной записи, то перейдите\nпо следующей ссылке, чтобы отменить подтверждение адреса:\n\n$5\n\nКод подтверждения действителен до $4.",
        "confirmemail_body_changed": "Кто-то (возможно вы) с IP-адресом $1\nуказал данный адрес электронной почты в качестве нового для учётной записи «$2» в проекте {{SITENAME}}.\n\nЧтобы подтвердить, что эта учётная запись действительно принадлежит вам,\nи включить возможность отправки писем с сайта {{SITENAME}}, откройте приведённую ниже ссылку в браузере.\n\n$3\n\nЕсли данная учётная запись *не* относится к вам, то перейдите по следующей ссылке,\nчтобы отменить подтверждение адреса\n\n$5\n\nКод подтверждения действителен до $4.",
        "confirmemail_body_set": "Кто-то (возможно вы) с IP-адресом $1\nуказал данный адрес электронной почты для учётной записи «$2» в проекте «{{SITENAME}}».\n\nЧтобы подтвердить, что эта учётная запись действительно принадлежит вам,\nи включить возможность отправки писем с сайта «{{SITENAME}}», откройте в браузере приведённую ниже ссылку:\n\n$3\n\nЕсли данная учётная запись *не* относится к вам, то перейдите по следующей ссылке,\nчтобы отменить подтверждение адреса электронной почты:\n\n$5\n\nКод подтверждения действителен до $4.",
-       "confirmemail_invalidated": "Подтверждение адреса электронной почты отменено",
-       "invalidateemail": "Ð\9eÑ\82мениÑ\82Ñ\8c Ð¿Ð¾Ð´Ñ\82веÑ\80ждение Ð°Ð´Ñ\80еÑ\81а Ñ\8dл. почты",
+       "confirmemail_invalidated": "Подтверждение адреса электронной почты отменено.",
+       "invalidateemail": "Ð\9eÑ\82мена Ð¿Ð¾Ð´Ñ\82веÑ\80ждениÑ\8f Ð°Ð´Ñ\80еÑ\81а Ñ\8dлекÑ\82Ñ\80онной почты",
        "scarytranscludedisabled": "[Интервики-включение отключено]",
        "scarytranscludefailed": "[Ошибка обращения к шаблону $1]",
        "scarytranscludefailed-httpstatus": "[Не удалось загрузить шаблон для $1: HTTP $2]",
        "scarytranscludetoolong": "[Слишком длинный URL]",
        "deletedwhileediting": "'''Внимание'''. Эта страница была удалена после того, как вы начали её править!",
-       "confirmrecreate": "{{GENDER:$1|Участник|Участница|}}&nbsp;[[User:$1|$1]] ([[User talk:$1|обс]]) {{GENDER:$1|удалил|удалила}} эту страницу после того, как вы начали её редактировать, по следующей причине:\n: ''$2''.\nПожалуйста, подтвердите, что вы хотите вновь создать эту страницу.",
+       "confirmrecreate": "{{GENDER:$1|Участник|Участница|}}&nbsp;[[User:$1|$1]] ([[User talk:$1|обс]]) {{GENDER:$1|удалил|удалила}} эту страницу после того, как вы начали её редактировать, по следующей причине:\n: <em>$2</em>.\nПожалуйста, подтвердите, что вы хотите вновь создать эту страницу.",
        "confirmrecreate-noreason": "{{GENDER:$1|Участник|Участница|}}&nbsp;[[User:$1|$1]] ([[User talk:$1|обс]]) {{GENDER:$1|удалил|удалила}} эту страницу после того, как вы начали её редактировать. Пожалуйста, подтвердите, что вы действительно хотите вновь создать эту страницу.",
        "recreate": "Создать заново",
        "unit-pixel": " пикс.",
        "hebrew-calendar-m11-gen": "Ава",
        "hebrew-calendar-m12-gen": "Элула",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|обсуждение]])",
+       "timezone-local": "Местное",
        "duplicate-defaultsort": "Внимание. Ключ сортировки по умолчанию «$2» переопределяет прежний ключ сортировки по умолчанию «$1».",
        "duplicate-displaytitle": "<strong>Внимание:</strong> Отображаемое название «$2» переопределяет ранее заданное отображаемое название «$1».",
        "invalid-indicator-name": "<strong>Ошибка:</strong> Атрибут <code>name</code> индикаторов состояния страницы не должен быть пустым.",
        "version-libraries-license": "Лицензия",
        "version-libraries-description": "Описание",
        "version-libraries-authors": "Авторы",
-       "redirect": "Перенаправление с файла, участника, страницы или идентификатора версии",
+       "redirect": "Перенаправление с идентификатора файла, участника, страницы, версии или журнала",
        "redirect-legend": "Перенаправление на файл или страницу",
-       "redirect-summary": "Эта служебная страница перенаправляет на файл (с имени файла), страницу (с идентификатора версии или страницы) или страницу участника (с числового идентификатора участника). Использование: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] или [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Эта служебная страница перенаправляет на файл (с имени файла), страницу (с идентификатора версии или страницы), страницу участника (с числового идентификатора участника) или запись журнала (с идентификатора журнала). Использование: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]], [[{{#Special:Redirect}}/user/101]] или [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Перейти",
        "redirect-lookup": "Поиск:",
        "redirect-value": "Значение:",
        "redirect-page": "Идентификатор страницы",
        "redirect-revision": "Версия страницы",
        "redirect-file": "Название файла",
+       "redirect-logid": "ID журнала",
        "redirect-not-exists": "Значение не найдено",
        "fileduplicatesearch": "Поиск одинаковых файлов",
        "fileduplicatesearch-summary": "Поиск одинаковых файлов по хэш-коду.",
        "tags-deactivate": "отключить",
        "tags-hitcount": "$1 {{PLURAL:$1|изменение|изменения|изменений}}",
        "tags-manage-no-permission": "У вас нет прав на управление изменениями меток.",
+       "tags-manage-blocked": "Вы не можете управлять метками правок, пока вы заблокированы.",
        "tags-create-heading": "Создать новую метку",
        "tags-create-explanation": "Вновь созданные метки по умолчанию будут созданы доступными для использования участниками и ботами.",
        "tags-create-tag-name": "Название метки:",
        "tags-deactivate-not-allowed": "Невозможно отключить метку «$1».",
        "tags-deactivate-submit": "Отключить",
        "tags-apply-no-permission": "У вас нет права применять метки изменения к своими изменениям.",
+       "tags-apply-blocked": "Вы не можете применять метки правок к своим правкам, пока вы заблокированы.",
        "tags-apply-not-allowed-one": "Метка «$1» не может быть применена вручную.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Следующая метка не может быть применена|Следующие метки не могут быть применены}} вручную: $1",
        "tags-update-no-permission": "У вас нет права на добавление или изменение меток изменения из отдельных версий или записей журналов.",
+       "tags-update-blocked": "Вы не можете добавлять или удалять метки правок, пока вы заблокированы.",
        "tags-update-add-not-allowed-one": "Тег \"$1\" не может быть добавлен вручную.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Следующий тег|Следующие теги}} нельзя добавлять вручную: $1",
        "tags-update-remove-not-allowed-one": "Метка «$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|войдите]] и попробуйте ещё раз.",
+       "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": "$1 байт{{PLURAL:$1||а|ов}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Общий размер файла для этого раздела: $1 байт{{PLURAL:$1||ов|а}} ($2; $3%).",
+       "mediastatistics-allbytes": "Общий размер всех файлов: $1 байт{{PLURAL:$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": "Превышена максимальная глубина стека",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страница ещё не существует",
        "mw-widgets-titleinput-description-redirect": "перенаправление на $1",
-       "api-error-blacklisted": "Пожалуйста, выберите другое, более понятное название."
+       "api-error-blacklisted": "Пожалуйста, выберите другое, более понятное название.",
+       "sessionmanager-tie": "Невозможно использовать одновременно несколько типов проверки подлинности запроса: $1.",
+       "sessionprovider-generic": "$1 сессий",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "сессий на основе куки",
+       "sessionprovider-nocookies": "Могут быть отключены куки. Убедитесь, что у вас включены куки и начните заново.",
+       "randomrootpage": "Случайная корневая страница"
 }
index 61415d6..48d24e8 100644 (file)
@@ -22,7 +22,8 @@
                        "రాకేశ్వర",
                        "아라",
                        "Macofe",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "రహ్మానుద్దీన్"
                ]
        },
        "tog-underline": "परिसन्धेः अधो रेखाङ्कनम्:",
        "morenotlisted": "एषाऽऽवलिः अपूर्णा अस्ति ।",
        "mypage": "पृष्ठम्",
        "mytalk": "सम्भाषणम्",
-       "anontalk": "à¤\85नà¥\8dतरà¥\8dà¤\9cालसà¤\82वितà¥\8dसमà¥\8dभाषणमà¥\8d",
+       "anontalk": "सम्भाषणम्",
        "navigation": "सञ्चरणम्",
        "and": "&#32;तथा च",
        "qbfind": "अन्विष्यताम्",
        "right-blockemail": "वि-पत्रप्रेषयितुम् एषः सदस्यः अवरुध्यताम्",
        "right-hideuser": "सदस्यनाम अवरुध्यताम्, तत् अन्ययोजकेभ्यः गोप्यतां च",
        "right-ipblock-exempt": "स्वयम् अवरोधितं, समूहावरोधितम् अन्तर्जालसङ्केतम् (IP) अवगण्य अग्रे गच्छतु",
-       "right-proxyunbannable": "प्रतिनिधीनां (of prxies) स्वयम्-अवरोधान् अवगण्य अग्रे गच्छतु",
        "right-unblockself": "स्वं मा अवरुध्यताम्",
        "right-protect": "सुरक्षास्तरं परिवर्त्यतां, क्रमबद्धानि सुरक्षितपृष्ठानि सम्पाद्यन्तां च",
        "right-editprotected": "\"{{int:protect-level-sysop}}\"-त्वेन संरक्षितानि पृष्ठानि सम्पाद्यन्ताम्",
        "right-managechangetags": "दत्तांशात् [[Special:Tags|चिह्नानि]] निर्मीयन्ताम्, अपाक्रियन्तां च",
        "right-applychangetags": "[[Special:Tags|चिह्नानि]] एकस्य परिवर्तनेन सह प्रयुञ्जताम् ।",
        "right-changetags": "स्वतन्त्रसंस्करणे, प्रवेशावल्यां च [[Special:Tags|चिह्नानि]] ऐच्छितरीत्या स्थापयतु, निष्कासयतु च",
+       "grant-group-email": "वि-पत्रं प्रेषयतु",
+       "grant-uploadfile": "नवीनसञ्चिकाः आरोप्यन्ताम्",
        "newuserlogpage": "प्रयोक्तृ-सृजन-सूचिका",
        "newuserlogpagetext": "अयं योजकनिर्माणास्य प्रवेशः ।",
        "rightslog": "प्रयोक्तृ-अधिकार-सूचिका",
        "recentchanges-label-plusminus": "पृष्ठस्य आकारः एतावद्भिः बैट्स्-संख्याभिः परिवर्तितः",
        "recentchanges-legend-heading": "'''विकल्पविषयकम्'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|अत्र नूतनपृष्ठानाम् आवलिः]] अपि दृश्यताम्)",
-       "rcnotefrom": "<strong>$3, $4</strong> तः आरभ्य (<strong>$1</strong> पर्यन्तं) जातानि {{PLURAL:$5|is the change|परिवर्तनानि}} अधः प्रदर्शितानि ।",
+       "rcnotefrom": "<strong>$3, $4</strong> तः आरभ्य (<strong>$1</strong> पर्यन्तं) जातानि {{PLURAL:$5|परिवर्तनानि}} अधः प्रदर्शितानि ।",
        "rclistfrom": "$3 $2 पश्चात् जातानि नूतनानि परिवर्तनानि दृश्यन्ताम्",
        "rcshowhideminor": "$1 लघुसम्पादनानि",
        "rcshowhideminor-show": "दृश्यताम्",
        "listgrouprights-namespaceprotection-header": "नामाकाशप्रतिबन्धाः",
        "listgrouprights-namespaceprotection-namespace": "नामाकाशः",
        "listgrouprights-namespaceprotection-restrictedto": "सम्पादयितु योजकाय अधिकारदानम्",
+       "listgrants-rights": "अधिकाराः",
        "trackingcategories": "वर्गाणाम् अनुसरणम्",
        "trackingcategories-summary": "एतस्मिन् पृष्ठे आनुपदिकवर्गाणां (tracking) सूची विद्यते । ते आनुपदिकवर्गाः MediaWiki software-संस्थया स्वरचिताः सन्ति । तेषां नामानि परिवर्तयितुं शक्नुमः । नामपरिवर्तयितुं {{ns:8}} इत्यत्र नामाकाशे सम्बन्धिते सन्देशप्रक्रियायां परिवर्तनं करिणीयं भवति ।",
        "trackingcategories-msg": "वर्गाणाम् अनुसरणम्",
        "contributions": "{{GENDER:$1|प्रयोक्तॄणां}} योगदानानि",
        "contributions-title": "$1 कृते सदस्यस्य योगदानानि",
        "mycontris": "योगदानानि",
+       "anoncontribs": "अंशदाता",
        "contribsub2": "($2) कृते {{GENDER:$3|$1}}",
        "contributions-userdoesnotexist": "\"$1\" इत्यषा सदस्यलेखा पञ्जीकृतं नास्ति ।",
        "nocontribs": "एतादृशयोग्यताभिः समं परिवर्तनानि न दृष्टानि ।",
        "ipb-change-block": "एतैः विन्यासैः सदस्यं पुनः अवरुणद्धु ।",
        "ipb-confirm": "अवरोधं दृढयतु ।",
        "badipaddress": "अमान्यः ऐपिसङ्केतः ।",
-       "blockipsuccesssub": "अवरोधः सफलः ।",
+       "blockipsuccesssub": "अवरोधः सफलः",
        "blockipsuccesstext": "[[Special:Contributions/$1|$1]]इत्येतत् अवरुद्धम् । <br />\nअवरोधानां समीक्षां करोतु । [[Special:BlockList|IP अवरोधसूचिका]]",
        "ipb-blockingself": "भवान् स्वयम् अवरोधने निरतः । निश्चयेन स्वावरोधनम् इच्छति वा ?",
        "ipb-confirmhideuser": "सदस्यगोपनस्य पिञ्जं निपीडयन् भवान् सदस्यावरुद्धिं यतते । एतत् सर्वावलीषु सर्वप्रवेशसूचिकासु च सदस्यनाम निग्रहति । भवान् निश्चयेन एतत् कर्तुमिच्छति वा ?",
        "intentionallyblankpage": "इदं पृष्ठं बुद्ध्या एव रिक्तं रक्षितमस्ति ।",
        "external_image_whitelist": "# एषा पङ्क्तिः न परिवर्त्यताम् <pre>\n# अत्र केवलं सामान्यचिह्नानाम् उपयोगः क्रियताम् (यथा // इत्यनयोः मध्ये स्थापनीयः भागः)\n# बहिस्तात् आगतानां चित्राणां सार्वसङ्केतैः (U R L) सह एतेषां तुलना भवति\n# यत् चित्रम् अनुकूलं भवति तत् योज्यते, अन्यथा तस्य चित्रस्य परिसन्धिः योज्यते । \n# याः पङ्क्तयः # इत्यस्मात् आरभन्ते, ताः सूचनाः\n# अत्र सर्वं पक्षविगुणं (case-insensitive) वर्तते \n# सर्वान् regex भागान् अस्याः पङ्क्तेः उपरि स्थापयतु । एतां पङ्क्तिम् एवमेव स्थापयतु </pre>",
        "tags": "तर्कसिद्धानि परिवर्तनाङ्कनानि",
-       "tag-filter": "[[Special:Tags|Tag]] शोधनी:",
+       "tag-filter": "[[Special:Tags|अङ्कनम्]] शोधनी :",
        "tag-filter-submit": "शोधनी",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|अङ्कनम्|अङ्कनानि}}]] : $2)",
        "tags-title": "अङ्कनानि",
index b206367..11dbca2 100644 (file)
        "tog-hideminor": "تازين تبديلين منجھہ معمولي تبديليون لڪايو",
        "tog-hidepatrolled": "تازيون نگرانيل تبديليون لڪايو",
        "tog-newpageshidepatrolled": "نَوَن صفحن واري فهرست مان نگرانيل صفحا لڪايو",
-       "tog-hidecategorization": "صÙ\81Ø­Ù\86 Ø¬Ø§ Ø°مرا لڪايو",
-       "tog-extendwatchlist": "تازه ترين بدران سموريون تبديليون ڏيکارڻ لاءِ ٽيٽ لسٽ کي وسيع ڪريو.",
+       "tog-hidecategorization": "صÙ\81Ø­Ù\86 Ø¬Ø§ Ø²مرا لڪايو",
+       "tog-extendwatchlist": "تازه ترين بدران سموريون تبديليون ڏيکارڻ لاءِ زير نظر فهرست کي وسيع ڪريو.",
        "tog-numberheadings": "سُرخين کي خودڪاراً نمبر ڏيو",
        "tog-showtoolbar": "سنوار اوزار ڏيکاريو",
        "tog-editondblclick": "ٻٽي ڪلڪ تي صفحا سنواريو",
        "tog-watchcreations": "منهنجا سرجيل صفحا ۽ منهنجا چاڙهيل فائيل منهنجي زيرِ نظر فهرست تي رکو",
-       "tog-watchdefault": "منهنجا ترميميل صفحا منهنجي ٽيٽ فهرست تي رکو",
-       "tog-watchmoves": "جيڪي صفحا ۽ فائيلس آئون چوريان، سي منهنجي ٽيٽ لسٽ ۾ شامل ڪريو.",
-       "tog-watchdeletion": "آئÙ\88Ù\86 Ø¬Ù\8aÚªÙ\8a ØµÙ\81حا Ú\8aاÙ\87Ù\8aاÙ\86Ø\8c Ø³Ù\8a Ù\85Ù\86Ù\87Ù\86جÙ\8a Ù½Ù\8aÙ½ فهرست تي رکو",
-       "tog-watchrollback": "انهن صفحن کي منهنجي ٽيٽ فهرست تي رکو، جن ۾ تبديلين کي مون واپس ورايو آهي.",
+       "tog-watchdefault": "منهنجا ترميميل صفحا ۽ فائيل  منهنجي زير نظر فهرست تي رکو",
+       "tog-watchmoves": "جيڪي صفحا ۽ فائيل آءُٗ چوريان، سي منهنجي زير نظر فهرست ۾ شامل ڪريو.",
+       "tog-watchdeletion": "آءÙ\8fÙ\97 Ø¬Ù\8aÚªÙ\8a ØµÙ\81حا Û½ Ù\81ائÙ\8aÙ\84  Ú\8aاÙ\87Ù\8aاÙ\86Ø\8c Ø³Ù\8a Ù\85Ù\86Ù\87Ù\86جÙ\8a Ø²Ù\8aر Ù\86ظر فهرست تي رکو",
+       "tog-watchrollback": "انهن صفحن کي منهنجي زير نظر فهرست تي رکو، جن ۾ تبديلين کي مون واپس ورايو آهي.",
        "tog-minordefault": "سمورين تبديلين کي بنان چئي معمولي ترميم تصور ڪريو",
        "tog-previewontop": "ترميمي باڪس مٿان پيش نگاهہ ڏيکاريو",
        "tog-previewonfirst": "پهرين ترميم تي پيش نگاهہ ڏيکاريو",
+       "tog-enotifwatchlistpages": "مونکي ايميل ڪريو جڏهن منهنجي زير نظر فهرست ڪا صفحو يا فائيل تبديل ڪيو وڃي",
        "tog-enotifusertalkpages": "منهنجي مباحثي صفحي ۾ تبديليءَ جي صورت ۾ مون کي برق ٽپال اماڻيو",
        "tog-enotifminoredits": "صفحن ۾ معمولي ترميمن جي صورت ۾ بہ مون کي برق ٽپال ڪريو",
        "tog-enotifrevealaddr": "پڌراين ۾ منهنجو برق ٽپال پتو ظاهر ڪريو.",
-       "tog-shownumberswatching": "Ù½Ù\8aÙ½Ù\8aÙ\86دÚ\99 Ù\8aÙ\88زرس Ø¬Ù\88 ØªØ¹Ø¯Ø§Ø¯ Ú\8fÙ\8aکارÙ\8aÙ\88",
+       "tog-shownumberswatching": "Ú\8fسÙ\86دÚ\99 Ù\8aÙ\88زرس Ø¬Ù\88 Ø§Ù\86Ú¯ Ú\8fÙ\8aکارÙ\8aÙ\88",
        "tog-oldsig": "موجوده دستخط",
        "tog-uselivepreview": "سڌي سنئين پيش نگاھہ استعمال ڪريو",
-       "tog-watchlisthideown": "ٽيٽ فهرست مان منهنجون ڪيل ترميمون لڪايو",
+       "tog-watchlisthideown": "زير نظر فهرست مان منهنجون ڪيل ترميمون لڪايو",
        "tog-watchlisthidebots": "ٽيٽ فهرست تان بوٽ جون ترميمون لڪايو",
        "tog-watchlisthideminor": "ٽيٽ فهرست تان معمولي ترميمون لڪايو",
-       "tog-watchlisthideliu": "لاگ اِن ٿيل يوزرس جون ڪيل ترميمون ٽيٽ فهرست ۾ نہ ڏيکاريو",
+       "tog-watchlisthideliu": "لاگ اِن ٿيل يوزرس جون ڪيل ترميمون زيرنظر فهرست ۾ نہ ڏيکاريو",
        "tog-watchlisthideanons": "ٽيٽ فهرست تان اڻڄاتل يوزر جون ترميمون لڪايو",
        "tog-watchlisthidecategorization": "صفحن جا زمرا لڪايو",
        "tog-ccmeonemails": "ٻين يوزرس ڏانهن منهنجي موڪليل برق ٽپال جو پرت مون کي اماڻيو",
@@ -44,6 +45,7 @@
        "tog-prefershttps": "هميشه محفوظ ڪنيڪشن استعمال ڪريو جڏهن لاگ اِن ٿيل هجو",
        "underline-always": "هميشہ",
        "underline-never": "ڪڏهن بہ نہ",
+       "editfont-style": "ايراضي جو فونٽ اسٽائيل سنواريو:",
        "sunday": "آچر",
        "monday": "سومر",
        "tuesday": "اڱارو",
@@ -62,7 +64,7 @@
        "february": "فيبروري",
        "march": "مارچ",
        "april": "اپريل",
-       "may_long": "مَي",
+       "may_long": "مَئي",
        "june": "جُونِ",
        "july": "جُولاءِ",
        "august": "آگسٽ",
@@ -74,7 +76,7 @@
        "february-gen": "فيبروري",
        "march-gen": "مارچ",
        "april-gen": "اپريل",
-       "may-gen": "مَي",
+       "may-gen": "مَئي",
        "june-gen": "جُونِ",
        "july-gen": "جُولاءِ",
        "august-gen": "آگسٽ",
@@ -86,7 +88,7 @@
        "feb": "فيبروري",
        "mar": "مارچ",
        "apr": "اپريل",
-       "may": "مَي",
+       "may": "مَئي",
        "jun": "جُونِ",
        "jul": "جُولاءِ",
        "aug": "آگسٽ",
        "february-date": "فيبروري $1",
        "march-date": "مارچ $1",
        "april-date": "اپريل $1",
-       "may-date": "مَي $1",
+       "may-date": "مَئي $1",
        "june-date": "جُون $1",
        "july-date": "جُولاءِ $1",
        "august-date": "آگسٽ $1",
        "qbedit": "سنواريو",
        "qbpageoptions": "هيءُ صفحو",
        "qbmyoptions": "منهنجا صفحا",
-       "faq": "ڪپوس",
+       "faq": "ڪپس",
        "faqpage": "Project:ڪپوس",
        "actions": "فعل",
        "namespaces": "نانءُ پولار:",
        "notloggedin": "لاگ اِن ٿيل ناهيو",
        "userlogin-noaccount": "کاتو نہ ٿا رکو؟",
        "userlogin-joinproject": "{{SITENAME}} ۾ شامل ٿيو",
-       "nologin": "پنهنجو کاتو نہ ٿا رکو؟ '''$1'''.",
+       "nologin": " کاتو نہ ٿا رکو؟ '''$1'''.",
        "nologinlink": "نئون کاتو کوليو",
        "createaccount": "کاتو کوليو",
        "gotaccount": "ڇا اڳي ئي کاتو رکو ٿا؟ '''$1'''.",
        "resetpass-recycled": "مهرباني ڪري پنهنجي هاڻوڪي ڳجھي لفظ کان ڪو مختلف ڳجھو لفظ چونڊيو.",
        "resetpass-temp-emailed": "توهان برق ٽپال ذريعي اماڻيل عارضي ڳجھي لفظ سان لاگ اِن ٿيا آهيو. لاگ اِن کي مڪمل ڪرڻ لاءِ توهان کي هتي نئون ڳجھو لفظ طَي ڪرڻو ئي پوندو:",
        "resetpass-temp-password": "عارضي ڳجھو لفظ:",
+       "resetpass-expired": "توهان جو ڳجھو لفظ مدي خارج ٿي چڪو آهي. نئون ڳجھو لفظ مقرر ڪريو ۽ لاگ اِن ٿيو.",
        "resetpass-expired-soft": "توهان جو ڳجھو لفظ مدي خارج ٿي چڪو آهي. مهرباني ڪري نئون ڳجھو لفظ چونڊيو، يا ساڳيو ڪم ڪنهن ٻي وقت ڪرڻ لاءِ \"{{int:resetpass-submit-cancel}}\" تي ڪلڪ ڪريو.",
        "resetpass-validity-soft": "توهان جو ڳجھو لفظ ناقابل ڪار آهي: $1\nمهرباني ڪري نئون ڳجھو لفظ چونڊيو، يا ساڳيو ڪم ڪنهن ٻي وقت ڪرڻ لاءِ \"{{int:resetpass-submit-cancel}}\" تي ڪلڪ ڪريو.",
        "passwordreset": "ڳجھو لفظ مَٽايو",
        "passwordreset-text-one": "برق ٽپال ذريعي عارضي ڳجھو لفظ حاصل ڪرڻ لاءِ هيءُ فارم پُر ڪريو.",
+       "passwordreset-disabled": "هن وڪيءَ تي ڳجھو لفظ نئين سِر مقرر ڪرڻ وارو چارو غير فعال بڻايو ويو آهي.",
+       "passwordreset-emaildisabled": "هن وڪيءَ تي برق‌ٽپال واريون خصوصيتون غير فعال بڻايون ويون آهن.",
        "passwordreset-username": "يُوزرنانءُ:",
        "passwordreset-domain": "ميدان:",
        "passwordreset-email": "برق ٽپال پتو:",
        "changeemail-submit": "برق ٽپال پتو بدلايو",
        "changeemail-throttled": "توهان تازو ئي لاگ اِن ٿيڻ جون هيڪانديون گھڻيون ڪوششون ڪيون آهن. مهرباني ڪري $1 لاءِ ترسي پوءِ وري ڪوشش ڪريو.",
        "changeemail-nochange": "مهرباني ڪري مختلف نئون برق ٽپال پتو ڄاڻايو.",
+       "resettokens": "ٻيهر ترتيب ڪرڻ جا ٽوڪن",
+       "resettokens-no-tokens": "ٻيهر ترتيب ڪرڻ لاءِ ڪي بہ ٽوڪن نہ آهن.",
        "resettokens-tokens": "ٽوڪنس:",
        "resettokens-token-label": "$1 (حاليہ قدر: $2)",
+       "resettokens-resetbutton": "چونڊيل ٽوڪن ٻيهر ترتيب ڪريو",
        "bold_sample": "گهري تحرير",
        "bold_tip": "گهري لکت",
        "italic_sample": "ترڇي لکت",
        "noarticletext": "في‌الوقت هن صفحي اندر ڪو بہ ٽيڪسٽ نہ آهي. توهان ٻين صفحن ۾ [[Special:Search/{{PAGENAME}}|search ساڳي عنوان جي ڳولا]] ڪري سگھو ٿا.  \n\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} لاڳاپيل لاگس ۾ ڳوليو]،\nor [{{fullurl:{{FULLPAGENAME}}|action=edit}} هيءُ صفحو ترميميو]</span>.",
        "userpage-userdoesnotexist-view": "يُوزر کاتو $1 درج ٿيل نہ آهي.",
        "blocked-notice-logextract": "هيءَ يُوزر في‌الحال بندشيل آهي. تازو بندش لاگ حوالي طور پيش ڪجي ٿو:",
+       "updated": "(تجديديل)",
+       "note": "<strong>نوٽ:</strong>",
        "previewnote": "<strong>'''هيءَ محظ پيش نگاهہ آهي.</strong> ترميمون اڃا سانڍجوين ناهن!'''",
        "continue-editing": "ترميم گاھ ڏانهن وڃو",
        "editing": "زير ترميم $1",
        "history-feed-description": "وڪي جي هن صفحي جي ترميمي سوانح",
        "history-feed-item-nocomment": "$2 تي $1",
        "rev-deleted-user": "(يُوزرنانءُ ڊاٺو ويو)",
+       "rev-deleted-event": "(لاگ تفصيل هٽايا ويا)",
+       "rev-deleted-user-contribs": "[يُوزرنانءُ يا آءِ پِي پتو مِٽايو ويو - ڀاڱيدارين مان ترميمون لڪايون ويون]",
        "rev-delundel": "نمائش تبديل ڪريو",
        "rev-showdeleted": "ڏيکاريو",
        "revisiondelete": "مسوادا ڊاهيو/اڻ‌ڊاهيو",
        "revdelete-show-file-submit": "ها",
        "revdelete-legend": "نمائش جون پابنديون ترتيب ڪريو",
        "revdelete-hide-image": "فائيل جو مواد لڪايو",
+       "revdelete-hide-name": "هدف ۽ نيمپيما لڪايو",
        "revdelete-hide-comment": "ترميم جو تتُ",
        "revdelete-hide-user": "ايڊيٽر جو يوزرنانءُ / آء پي پتو",
+       "revdelete-hide-restricted": "منتظمن توڙي ٻين کان مليل اعداد دٻايو",
        "revdelete-radio-same": "(نہ بدلايو)",
        "revdelete-radio-set": "لڪل",
        "revdelete-radio-unset": "ظاهر",
+       "revdelete-suppress": "منتظمن توڙي ٻين کان مليل اعداد دٻايو",
        "revdelete-log": "سبب:",
        "revdel-restore": "نمائش تبديل ڪريو",
        "pagehist": "صفحي جي سوانح",
        "mergehistory-from": "ذريعہ صفحو:",
        "mergehistory-into": "مقصود صفحو:",
        "mergehistory-list": "ضمائتي ترميم سوانح",
+       "mergehistory-go": "ضم ڪرڻ لائق ترميمون ڏيکاريو",
        "mergehistory-submit": "ڀيرن کي ضم ڪريو",
        "mergehistory-empty": "ڪي بہ ڀيرا ضم ڪري نہ ٿا سگھجن.",
        "mergehistory-no-source": "مصدر صفحو $1 وجود نٿو رکي.",
        "right-createpage": "صفحا سنواريو (جيڪي مباحثي صفحا نہ آهن)",
        "right-createtalk": "مباحثي صفحا سرجيو",
        "right-createaccount": "نوان يُوزر کاتا کوليو",
+       "right-minoredit": "ترميمُن کي معمُولي ڄاڻايو",
        "right-move": "صفحا چوريو",
        "right-move-subpages": "ذيلي صفحن سميت صفحا چوريو",
+       "right-move-categorypages": "زمراتي صفحا چوريو",
        "right-movefile": "فائيل چوريو",
        "right-upload": "فائيل چاڙهيو",
        "right-upload_by_url": "ڪنهن يُوآرايل تان فائيل چاڙهيو",
        "right-mergehistory": "صفحن جي سوانح سنواريو",
        "right-userrights": "سڀ يوزر حق ترميم ڪريو",
        "right-userrights-interwiki": "هن وڪي جي يوزرس جا حق ترميم ڪريو",
+       "right-siteadmin": "اعدادخانو بنديو ۽ کوليو",
+       "right-override-export-depth": "5ئين اونهائيءَ تائين ڳنڍيل صفحن سميت صفحا برآمديو",
        "right-sendemail": "ٻين يوزرس ڏانهن ايميل موڪليو",
+       "right-passwordreset": "ڳجھو لفظ مقرري برق ٽپالون ڏسو",
+       "right-managechangetags": "اعدادخاني ۾ [[Special:Tags|ٽيگس]] سرجيو ۽ ڊاهيو.",
        "newuserlogpage": "يوزر کاتن جو لاگ",
        "rightslog": "يُوزر حق لاگ",
        "action-read": "هي صفحو پڙهو",
        "action-movefile": "هيءُ فائيل چوريو",
        "action-upload": "هيءُ فائيل چاڙهيو",
        "action-delete": "هيءُ صفحو ڊاهيو",
+       "action-deleterevision": "هيءُ ڀيرو ڊاهيو",
+       "action-deletedhistory": "هن صفحي جي ڊاٺ سوانح ڏسو",
        "action-browsearchive": "ڊاٺل صفحن ۾ ڳوليو",
        "action-undelete": "هيءُ صفحو اڻڊاهيو",
+       "action-suppressrevision": "هن لڪيل ڀيري تي نظرثاني ڪريو ۽ بحاليو",
+       "action-suppressionlog": "هيءُ ذاتي لاگ ڏسو",
+       "action-block": "هن يُوزر کي سنوارڻ کان روڪيو",
+       "action-protect": "هن صفحي جي تحفظاتي سطح بدلايو",
+       "action-rollback": "ڪنهن مخصوص صفحي تي آخري ترميم ڪندڙ يُوزر جي سمورين ترميمن کي ترت واپس ورايو",
        "action-import": "ٻي ڪنهن وڪي کان صفحا درآمد ڪريو",
        "action-importupload": "ڪو فائيل چاڙهي صفحا درآمديو",
+       "action-unwatchedpages": "اڻ ٽيٽيل صفحن جي فهرست ڏسو",
        "action-mergehistory": "هن صفحي جي سوانح ضم ڪريو",
        "action-userrights": "سڀ يوزر حق ترميم ڪريو",
        "action-userrights-interwiki": "ٻين وڪيز جي يوزرس جا حق ترميم ڪريو",
        "rcshowhidemine": "منهنجون ترميمون $1",
        "rcshowhidemine-show": "نمايو",
        "rcshowhidemine-hide": "لڪايو",
+       "rcshowhidecategorization": "$1 صفحاتي زمراڪاري",
        "rcshowhidecategorization-show": "ڏيکاريو",
        "rcshowhidecategorization-hide": "لڪايو",
        "rclinks": "پوين $2 ڏينهن ۾ آيل پويون $1 تبديليون ڏيکاريو <br />$3",
        "minoreditletter": "م",
        "newpageletter": "ن",
        "boteditletter": "گ",
+       "number_of_watching_users_pageview": "[$1، {{PLURAL:$1|يُوزر|يُوزرس}} کي ٽيٽيندي]",
        "rc-change-size-new": "$1 {{PLURAL:$1|بائيٽ|بائيٽس}} تبديليءَ کان پوءِ",
        "rc-enhanced-expand": "تفصيل ڏيکاريو",
        "rc-enhanced-hide": "تفصيل لڪايو",
        "recentchangeslinked-to": "رڳو ڄاڻايل صفحي سان ڳانڍيل صفحن ۾ ٿيل تبديليون نمايو",
        "upload": "فائيل چاڙهيو",
        "uploadbtn": "فائيل چاڙهيو",
+       "uploadnologin": "لاگ اِن ٿيل نہ آهيو",
+       "uploadnologintext": "فائيل چاڙهڻ لاءِ $1.",
        "uploaderror": "چاڙهہ چُڪَ",
        "uploadlogpage": "چاڙهہ لاگ",
        "filename": "فائيل نانءُ",
        "filereuploadsummary": "فائيل تبديليون:",
        "filesource": "ذريعو:",
        "filename-tooshort": "فائيل نانءَُ هيڪاندو ننڍو آهي.",
+       "filetype-banned": "فائيل جو هيءُ قسم بندشيل آهي.",
+       "verification-error": "هن فائيل جي تصديق ٿي نہ سگھي.",
+       "illegal-filename": "اهو فائيل‌نانءُ ناقابل قبول آهي.",
+       "unknown-error": "ڪا اڻجاتل چُڪَ ٿي.",
+       "tmp-create-error": "عارضي فائيل سرجي نہ سگھيو.",
        "uploadwarning": "چاڙھ جو چتاءُ",
        "savefile": "فائيل سانڍيو",
        "uploaddisabled": "چاڙھ ناقابلِ ڪار بڻيل.",
        "watchlistall2": "سڀ",
        "watchlist-hide": "لڪايو",
        "watchlist-submit": "ڏيکاريو",
-       "wlshowtime": "ÚªÙ\87Ú\99و عرصو ڏيکارجي:",
+       "wlshowtime": "ÚªÙ\8aترو عرصو ڏيکارجي:",
        "wlshowhideminor": "معمولي ترميم",
        "wlshowhidebots": "بوٽس",
        "wlshowhideliu": "کاتيدار يُوزرس",
        "tags-delete-reason": "سبب:",
        "tags-activate-reason": "سبب:",
        "tags-activate-submit": "فعاليو",
-       "tags-deactivate-title": "Ù½Ù\8aÚ¯ Ú©Ù\8a Ø§Ú» Ù\81عاÙ\84Ù\8aÙ\88.",
+       "tags-deactivate-title": "Ù½Ù\8aÚ¯ Ú©Ù\8a ØºÙ\8aر Ù\81عاÙ\84 ÚªØ±Ù\8aÙ\88",
        "tags-deactivate-reason": "سبب:",
        "tags-edit-existing-tags-none": "\"ڪو بہ نہ\"",
        "tags-edit-new-tags": "نوان ٽيگس:",
index 2730515..1752d8c 100644 (file)
@@ -11,6 +11,7 @@
        },
        "tog-underline": "Pabriežtė nūruodas:",
        "tog-hideminor": "Pakavuotė mažus pataisėmus vielībūju keitėmu sārošė",
+       "tog-hidecategorization": "Kavuotė poslapiu kateguorėzacėjė",
        "tog-extendwatchlist": "Ėšpliestė keravuojamu sāroša, ka ruodītu ne tėktās vielībūsius pakeitėmus",
        "tog-usenewrc": "Skėrstītė keitėmus vielībūsiūs pakeitėmūs ė keravuojamu poslapiu sārašė",
        "tog-numberheadings": "Autuomatėškā numeroutė skėrsnelius",
        "tog-watchlisthideliu": "Kavuotė prėsėjongosiu nauduotuoju keitėmus keravuojamu sārošė",
        "tog-watchlisthideanons": "Kavuotė anonimėniu nauduotuoju keitėmus keravuojamu sārošė",
        "tog-watchlisthidepatrolled": "Kavuotė sodabuotus pakeitėmus keravuojamu sārošė",
+       "tog-watchlisthidecategorization": "Kavuotė poslapiu kateguorėzacėjė",
        "tog-ccmeonemails": "Siōstė monėi gromatu kuopėjės, katros siontio kėtėims nauduotuojams",
        "tog-diffonly": "Neruodītė poslapė torėnė puo skėrtomās",
        "tog-showhiddencats": "Ruodītė pakavuotas kateguorėjės",
        "tog-norollbackdiff": "Nekrēptė diemesė i skėrtoma atlėkus atmetėma",
        "tog-useeditwarning": "Monėi dout žėnuot, kāp ėšēno ėš poslapė anon naėšsauguojis",
+       "tog-prefershttps": "Vėsūmet nauduotė saugu rīši kāp būno prėsijongė̄s",
        "underline-always": "Vėsūmet",
        "underline-never": "Nikūmet",
        "underline-default": "Vagol naršīklės nustatīmus",
        "october-date": "Spalė $1",
        "november-date": "Lapkristė $1",
        "december-date": "Groudė $1",
+       "period-am": "prīšpėit",
+       "period-pm": "pupėit",
        "pagecategories": "{{PLURAL:$1|Kateguorėjė|Kateguorėjės|Kateguorėju}}",
        "category_header": "Kateguorėjės „$1“ straipsnē",
        "subcategories": "Pukateguorėjės",
        "morenotlisted": "Tas sārošos ožbengts nie.",
        "mypage": "Poslapis",
        "mytalk": "Aptarėms",
-       "anontalk": "Ton IP adresa aptarėms",
+       "anontalk": "Aptarėms",
        "navigation": "Naršīms",
        "and": "&#32;ėr",
        "qbfind": "Ėiškuotė",
        "jumptosearch": "paėiška",
        "view-pool-error": "Atsėprašuom, bat serverē daba īr parkrautė.\nNuognē pardaug nauduotoju skait ton poslapi.\nPrašuom palaukat ė mieginkat i ton poslapi patekt apent.\n\n$1",
        "pool-errorunknown": "Nežėnuoma klaida",
+       "poolcounter-usage-error": "Naudojėma soklīdėms: $1",
        "aboutsite": "Aple {{SITENAME}}",
        "aboutpage": "Project:Aple",
        "copyright": "Turinīs pateikts so $1 lėcencėjė.",
        "privacypage": "Project:Privatoma puolitėka",
        "badaccess": "Privėlėju klaida",
        "badaccess-group0": "Tomstā nelēdama ivīkdītė veiksma, katruo prašiet.",
+       "badaccess-groups": "Tou, kon nuoriejot padėrbtė ī leidama tėktās {{PLURAL:$2|skīriou nauduotuoju|skīriam nauduotuoju}}: $1.",
        "versionrequired": "Rēk $1 MediaWiki atmaina",
        "ok": "Gerā",
        "retrievedfrom": "Gautė ėš „$1“",
        "nospecialpagetext": "Tamsta prašiet nelaistėna specēlė̄jė poslapė, laistėnū specēliūju poslapiu sōraša rasėt [[Special:SpecialPages|specēliūju poslapiu sārošė]].",
        "error": "Klaida",
        "databaseerror": "Doumenū bazės klaida",
+       "databaseerror-query": "Ožprašīms: $1",
+       "databaseerror-function": "Paskėrtės: $1",
        "databaseerror-error": "Klaida: $1",
        "laggedslavemode": "Atėduos: Poslapie gal' nesmatītė vielībiausiu pakeitėmu.",
        "readonly": "Doumenū bazė ožrakėnta",
        "filecopyerror": "Naėšēn parkeltė abruozdielė nug „$1“ i „$2“.",
        "filerenameerror": "Naėšēn parvadintė abruozdielė ėš „$1“ i „$2“.",
        "filedeleteerror": "Naėšēn ėštrintė abruozdielė „$1“.",
+       "directoryreadonlyerror": "Adresos $1 ī tėktās skaitīmou.",
        "filenotfound": "Nepavīkst rastė faila „$1“.",
        "unexpected": "Natėkieta raikšmie: „$1“=„$2“.",
+       "badarticleerror": "Tamė poslapie tokė vēksma padėrbtė nie galam.",
        "cannotdelete": "Nepavīka ėštrintė nuruodīta poslapė a faila \"$1\". (Mažo kažkas padarė pėrmesnis šėta)",
        "cannotdelete-title": "Negal ėštrintė poslapė \"$1\"",
        "badtitle": "Bluogs pavadėnėms",
        "viewsource-title": "Veizietė poslapė $1 teksta",
        "protectedpagetext": "Šėts poslapis īr ožrakints, saugont anū nū taisīma.",
        "viewsourcetext": "Tamsta galat veizietė ė parsėkeltė poslapė teksta.",
+       "viewyourtext": "Tamsta galat parveizietė ė parsikeltė <strong>Tamstas pakeitėmu</strong> pradėni teksta i šėtou poslapi.",
        "protectedinterface": "Šėtom poslapi īr pruogramėnės īronguos sasajuos teksts katros īr apsauguots, kū neprietelē anū nasogadėntu.",
        "editinginterface": "<strong>Diemesė:</strong> Tamsta keitat poslapi, katros īr nauduojams programėnės irongas sōsajės tekstė. Pakeitėmā tamė poslapū tēpuogi pakeis nauduotuojė sōsajės ėšruoda ė kėtėims nauduotujams. Jēgo nuorėt pargoldītė, siūluom pasėnauduotė [//translatewiki.net „translatewiki.net“], „MediaWiki“ lokalėzacėjės pruojėktu.",
+       "translateinterface": "Ka pamainītomiet pargoldīmus vėsūs wiki pruojektus, nauduokat [//translatewiki.net/ translatewiki.net] tėnklapi.",
        "namespaceprotected": "Tamsta netorėt teisiu keistė poslapiu '''$1''' srėtī.",
        "ns-specialprotected": "Specēlė̄jė poslapē nagal būtė keitamė.",
        "titleprotected": "Nauduotuos [[User:$1|$1]] ožgīnė padėrbtė straipsni tuokio pavadėnėmo.\nDingstės īr „<em>$2</em>“.",
        "exception-nologin": "Nesat prėsėjongis",
        "exception-nologin-text": "Ka galietomiet ton padėrbtė, Tamstā būtėnā rēk prėsėjongtė.",
        "exception-nologin-text-manual": "Ka galietomiet ton padėrbtė, Tamstā būtėnā rēk $1.",
-       "logouttext": "'''Daba Tamsta esat atsėjongis.'''\n\nGalat ė tuoliau nauduotė {{SITENAME}} anuonėmėškā aba <span class='plainlinks'>[$1 prėsėjonkat]</span> ėš naujė tuo patio aba kėto nauduotuojė vardo.\nAtėduos: katrūs nakatrūs poslapiūs ė tuoliau gal ruodītė būktā būtomiet prėsėjongis lėgė tuol, kumet ėšvalīsat sava naršīklės dietovė (''cache'').",
+       "logouttext": "'''Daba Tamsta esat atsėjongė̄s.'''\n\nGalat ė tuoliau nauduotė {{SITENAME}} anuonėmėškā aba <span class='plainlinks'>[$1 prėsėjonkat]</span> ėš naujė tuo patio aba kėto nauduotuojė vardo.\nAtėduos: katrūs nakatrūs poslapiūs ė tuoliau gal ruodītė būktā būtomiet prėsėjongis lėgė tuol, kumet ėšvalīsat sava naršīklės dietovė (''cache'').",
+       "cannotlogoutnow-title": "Nēn atsijongtė",
        "welcomeuser": "Svēks, $1!",
        "welcomecreation-msg": "Tamstas paskīra jau padėrbta.\nNūnā galat pakeistė sava {{SITENAME}} [[Special:Preferences|nustatīmus]], jēgo tėktās nuorat.",
        "yourname": "Nauduotuojė vards:",
        "remembermypassword": "Atmintė prisėjongėma infuormacėjė šėtom kuompioteri (daugiausē $1 {{PLURAL:$1|dėina|dėinė|dėinas}})",
        "userlogin-remembermypassword": "Ka liktō prisėjongis",
        "userlogin-signwithsecure": "Apsauguots rīšīs",
+       "cannotloginnow-title": "Nēn prėsijongtė",
        "yourdomainname": "Tamstas domens:",
        "password-change-forbidden": "Negalat tuo wiki keistė slaptažuodiu.",
        "login": "Prėsėjongtė",
        "userlogin-resetlink": "Ožmiršat sava prėsėjongėma doumenis?",
        "userlogin-resetpassword-link": "Ožmiršat sava slaptažuodi?",
        "userlogin-helplink2": "Prėsėjongėma pagelba",
+       "userlogin-loggedin": "Tamsta jau īr prėsijongė̄s kāp {{GENDER:$1|$1}}.\nJēb nuorat prisėjongtė kāp kėts žmuogos, nauduokat skvarma apatiuo.",
        "userlogin-createanother": "Padėrbtė kėta paskīra",
        "createacct-emailrequired": "El. pašta adresos",
        "createacct-emailoptional": "El. paštos (nie būtėns)",
        "createacct-realname": "Tėkros vardos (nie būtėns)",
        "createaccountreason": "Dingstės:",
        "createacct-reason": "Dingstės",
+       "createacct-reason-ph": "Kūdie dėrbat kėta nauduotojė poslapi",
        "createacct-submit": "Padėrbkat savėi paskīra",
-       "createacct-another-submit": "Padėrbtė kėta paskīra",
+       "createacct-another-submit": "Padėrbtė paskīra",
        "createacct-benefit-heading": "{{SITENAME}} īr sokorta prietėliu, tuokiu, kāp Tamsta.",
        "createacct-benefit-body1": "{{PLURAL:$1|pataisīms|pataisīmā|pataisīmu}}",
        "createacct-benefit-body2": "{{PLURAL:$1|poslapis|poslapē|poslapiu}}",
        "badretype": "Ivestė slaptažuodē nasotink.",
        "userexists": "Irašīts nauduotuojė vards jau īr nauduojams.\nPrašuom pasėrėnktė kėtuoki varda.",
        "loginerror": "Prisėjongėma klaida",
+       "createacct-error": "Paskīruos dėrbėma klaida",
        "createaccounterror": "Nė̄šiejė padėrbtė paskīruos: $1",
        "nocookiesnew": "Nauduotuojė paskīra bova sokurta, bat Tamsta nēsot prėsėjongis. {{SITENAME}} nauduo pakavukus (''cookies''), ka prėkergtom nauduotuojus. Tamsta esot ėšjongis anūs. Prašuom ijongtė pakavukus, tumet prisėjonkat so sava naujo nauduotuojė vardo ė slaptažuodio.",
        "nocookieslogin": "{{SITENAME}} nauduo pakavukus (''cookies''), ka prėkergtom nauduotuojus. Tamsta esat ėšjongis anūs. Prašuom ijongtė pakavukus ė pamiegītė apent.",
        "passwordreset-username": "Nauduotuojė vards:",
        "passwordreset-domain": "Domens:",
        "passwordreset-email": "El. pašta adresos:",
-       "changeemail": "Keistė el. pašta adresa",
+       "passwordreset-emailelement": "Nauduotuos:\n$1\n\nČiesėšks slaptažuodis:\n$2",
+       "changeemail": "Keistė aba trintė el. pašta adresa",
        "changeemail-oldemail": "Vielībs el. pašta adresosː",
        "changeemail-newemail": "Naus el. pašta adresosː",
        "changeemail-none": "(nie)",
        "search-interwiki-caption": "Dokterėnē pruojektā",
        "search-interwiki-default": "Soėiškuota nug $1ː",
        "search-interwiki-more": "(daugiau)",
-       "search-relatedarticle": "Sosėjėn",
-       "searchrelated": "sosėjėn",
+       "search-relatedarticle": "Sosėjė̄",
+       "searchrelated": "sosėjė̄",
        "searchall": "vėsė",
        "showingresults": "Žemiau ruodoma lėgė '''$1''' {{PLURAL:$1|rezoltata|rezoltatu|rezoltatu}} pradedont #'''$2'''.",
        "showingresultsinrange": "Apatiuo ruod lėgė {{PLURAL:$1|<strong>1</strong> gavėnė|<strong>$1</strong> gavėniū}} nug #<strong>$2</strong> lėgė #<strong>$3</strong>.",
        "contributions": "Nauduotuojė duovis",
        "contributions-title": "Nauduotuojė $1 duovis",
        "mycontris": "Duovis",
+       "anoncontribs": "Kūriejē",
        "contribsub2": "Nauduotuojė $1 ($2)",
        "uctop": " (vielībs)",
        "month": "Nug mienėsė (ėr onkstiau):",
        "sp-contributions-toponly": "Ruodītė tėktās paskiausius keitėmus",
        "sp-contributions-newonly": "Ruodītė tėktās tūs keitėmus, katrās padėrbtė straipsnē",
        "sp-contributions-submit": "Ėiškuotė",
-       "whatlinkshere": "Sosėjėn straipsnē",
+       "whatlinkshere": "Sosėjė̄ straipsnē",
        "whatlinkshere-title": "Poslapē, katrėi ruod i \"$1\"",
        "whatlinkshere-page": "Poslapis:",
        "linkshere": "Anėi poslapē ruod i '''[[:$1]]''':",
        "tooltip-t-recentchangeslinked": "Paskotėnē pakeitėmā straipsniūs, pasėikiamūs nug šėta straipsnė",
        "tooltip-feed-rss": "Šėta poslapė RSS šaltėnis",
        "tooltip-feed-atom": "Ton poslapė Atom šaltėnis",
-       "tooltip-t-contributions": "Ruodītė ton nauduotuojė duovi",
+       "tooltip-t-contributions": "Ruodītė tou nauduotuojė duovi",
        "tooltip-t-emailuser": "Siōstė gromata šėtom prietėliō",
        "tooltip-t-upload": "Ožkrautė abruozdielius",
        "tooltip-t-specialpages": "Specēliūju poslapiu sārašos",
        "exif-imagelength": "Aukštoms",
        "exif-orientation": "Regėnė pasokėms",
        "exif-xresolution": "Golos dėdloms",
+       "exif-yresolution": "Statma rezoliocėjė",
        "exif-datetime": "Abruozdielė parkeitėma čiesos",
        "exif-imagedescription": "Abruozdielė pavadėnėms",
        "exif-make": "Puortaparata dėrbies",
        "exif-model": "Puortaparata muodelis",
+       "exif-software": "Nauduota pruogramėnė īronga",
        "exif-artist": "Autuorios",
        "exif-exifversion": "Exif atmains",
        "exif-colorspace": "Spalvū lauks",
        "exif-usercomment": "Pāiškėnėmā",
        "exif-relatedsoundfile": "Prėgolons garsos",
        "exif-datetimeoriginal": "Žėnės paderbėma čiesos",
+       "exif-datetimedigitized": "Čiesa soskaitmenėnėms",
        "exif-exposuretime": "Ėšlaikīma čiesos",
        "exif-exposuretime-format": "$1 s. ($2)",
        "exif-fnumber": "F skaitlius",
        "logentry-delete-delete": "$1 ėštrīnė poslapi $3",
        "logentry-delete-restore": "$1 atkūrė poslapi $3",
        "revdelete-content-hid": "torėnīs pakavuots",
-       "logentry-block-block": "ožgīnė „[[$1]]“ nug dėrbėma, tas vēk ton čiesa - $2 $3",
+       "logentry-block-block": "$1 {{GENDER:$2|ožgīnė}} {{GENDER:$4|$3}} nug dėrbėma, tas vēk ton čiesa - $5 $6",
        "logentry-move-move": "$1 {{GENDER:$2|parvadėna}} poslapi $3 i $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|parvadėna}} poslapi nug $3 i $4 nepalinkdoms nusokėma",
        "logentry-move-move_redir": "$1 {{GENDER:$2|parvadėna}} poslapi nog $3 i $4 ont bovosė nusokėma",
index f8629b1..5e6d7bc 100644 (file)
        "category-file-count-limited": "V tejto kategórii sa {{PLURAL:$1|nachádza jeden súbor|nachádzajú $1 súbory|nachádza $1 súborov}}",
        "listingcontinuesabbrev": "pokrač.",
        "index-category": "Indexované stránky",
-       "noindex-category": "neindexované stránky",
+       "noindex-category": "Neindexované stránky",
        "broken-file-category": "Stránky s odkazom na neexistujúci súbor",
        "about": "Projekt",
        "article": "Stránka s obsahom",
        "undo-success": "Úpravu je možné vrátiť. Prosím skontrolujte tento rozdiel, čím overíte, že táto úprava je tá, ktorú chcete, a následne uložte zmeny, čím ukončíte vrátenie.",
        "undo-failure": "Úpravu nie je možné vrátiť kvôli konfliktným medziľahlým úpravám.",
        "undo-norev": "Túto úpravu nie je možné vrátiť, pretože neexistuje alebo bola zmazaná.",
-       "undo-nochange": "Zdá se, že úprava už bola zrušená.",
+       "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",
        "right-blockemail": "Zablokovať používateľovi posielanie emailu",
        "right-hideuser": "Zablokovať používateľské meno tak, že bude verejnosti skryté",
        "right-ipblock-exempt": "Obchádzať blokovanie IP adries, rozsahov a automatické blokovanie",
-       "right-proxyunbannable": "Obchádzať automatické blokovanie proxy serverov",
        "right-unblockself": "Odblokovať seba samého",
        "right-protect": "Meniť úroveň zamknutia a upravovať kaskádovito zamknuté stránky",
        "right-editprotected": "Upravovať stránky zamknuté ako „{{int:protect-level-sysop}}“",
        "right-override-export-depth": "Exportovať stránky vrátane okdazovaných stránok do hĺbky 5 odkazov",
        "right-sendemail": "Posielať e-mail ostatným používateľom",
        "right-passwordreset": "Prezeranie e-mailov pre znovunastavovanie hesla",
+       "grant-group-email": "Poslať email",
        "newuserlogpage": "Záznam vytvorených používateľov",
        "newuserlogpagetext": "Toto je záznam naposledy vytvorených používateľských účtov.",
        "rightslog": "Záznam používateľských práv",
        "pageinfo-header-edits": "História úprav",
        "pageinfo-header-restrictions": "Ochrana stránky",
        "pageinfo-header-properties": "Vlastnosti stránky",
-       "pageinfo-display-title": "Zobraz názov",
+       "pageinfo-display-title": "Zobrazovaný názov",
        "pageinfo-default-sort": "Predvolený kľúč zoraďovania:",
        "pageinfo-length": "Dĺžka stránky (v bajtoch)",
        "pageinfo-article-id": "ID stránky",
-       "pageinfo-language": "Jazyk obsahu stránok",
+       "pageinfo-language": "Jazyk obsahu stránky",
        "pageinfo-content-model": "Model obsahu stránky",
-       "pageinfo-robot-policy": "Indexovanie pomocou robotov",
+       "pageinfo-robot-policy": "Indexovanie robotmi",
        "pageinfo-robot-index": "Povolené",
        "pageinfo-robot-noindex": "Nepovolené",
        "pageinfo-watchers": "Počet používateľov sledujúcich stránku",
-       "pageinfo-visiting-watchers": "Počet aktívnych sledujúcich používateľov (takí, ktorí videli nedávne úpravy)",
+       "pageinfo-visiting-watchers": "Počet aktívnych sledujúcich (takí, ktorí videli nedávne úpravy)",
        "pageinfo-few-watchers": "Menej ako $1 {{PLURAL:$1|sledujúci|sledujúci|sledujúcich}}",
        "pageinfo-few-visiting-watchers": "Nie je isté, či existujú aktívni sledujúci používatelia (takí, ktorí videli nedávne úpravy)",
        "pageinfo-redirects-name": "Počet presmerovaní na túto stránku",
        "pageinfo-subpages-name": "Podstránky tejto stránky",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|presmerovanie|presmerovania|presmerovaní}}; $3 {{PLURAL:$3|nie je presmerovanie|nie sú presmerovania}})",
-       "pageinfo-firstuser": "Tvorca stránky",
-       "pageinfo-firsttime": "Dátum vytvorenia stránky",
+       "pageinfo-firstuser": "Stránku založil",
+       "pageinfo-firsttime": "Dátum založenia stránky",
        "pageinfo-lastuser": "Naposledy upravil",
        "pageinfo-lasttime": "Dátum poslednej úpravy",
        "pageinfo-edits": "Celkový počet úprav",
        "pageinfo-toolboxlink": "Informácie o stránke",
        "pageinfo-redirectsto": "Presmerovanie na",
        "pageinfo-redirectsto-info": "info",
-       "pageinfo-contentpage": "Počíta sa ako obsah stránky",
+       "pageinfo-contentpage": "Stránka sa počíta ako článok",
        "pageinfo-contentpage-yes": "Áno",
        "pageinfo-protect-cascading": "Kaskádové zamknutie",
        "pageinfo-protect-cascading-yes": "Áno",
        "watchlistedit-clear-done": "Váš zoznam sledovaných stránok bol vyprázdnený.",
        "watchlistedit-clear-removed": "{{PLURAL:$1|Bol odstránený jeden názov|Boli odstránené $1 názvy|Bolo odstránených $1 názvov}}:",
        "watchlistedit-too-many": "Zoznam obsahuje priveľa stránok na zobrazenie.",
-       "watchlisttools-clear": "Vyprázdniť zoznam sledovaných stránok",
+       "watchlisttools-clear": "Vyprázdniť zoznam",
        "watchlisttools-view": "Zobraziť súvisiace zmeny",
        "watchlisttools-edit": "Zobraziť a upraviť zoznam sledovaných stránok",
-       "watchlisttools-raw": "Upraviť textovú verziu zoznamu sledovaných stránok",
+       "watchlisttools-raw": "Upraviť textovú verziu zoznamu",
        "iranian-calendar-m1": "Farvardín",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskusia]])",
+       "timezone-local": "miestny čas",
        "duplicate-defaultsort": "Upozornenie: DEFAULTSORT s triediacim kľúčom „$2“ prepisuje vyššie nastavenú hodnotu „$1“.",
        "duplicate-displaytitle": "<strong>Upozornenie:</strong> Predchádzajúci titulok (DISPLAYTITLE) „$1“ je nahradený titulkom „$2“.",
+       "invalid-indicator-name": "<strong>Chyba:</strong> Atribút <code>name</code> indikátoru stavu stránky nesmie byť prázdny.",
        "version": "Verzia",
        "version-extensions": "Nainštalované rozšírenia",
        "version-skins": "Témy vzhľadu",
        "version-hook-name": "Názov prípojného bodu",
        "version-hook-subscribedby": "Pripojené",
        "version-version": "($1)",
+       "version-no-ext-name": "[bez názvu]",
        "version-license": "Licencia",
        "version-ext-license": "Licencia",
        "version-ext-colheader-name": "Rozšírenie",
        "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."
+       "api-error-blacklisted": "Prosím, zvoľte iný, opisný názov.",
+       "randomrootpage": "Náhodná koreňová stránka"
 }
index 800e28f..b77d20b 100644 (file)
        "october-date": "$1. oktober",
        "november-date": "$1. november",
        "december-date": "$1. december",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kategorija|Kategoriji|Kategorije}}",
        "category_header": "Strani v kategoriji »$1«",
        "subcategories": "Podkategorije",
        "virus-scanfailed": "pregled ni uspel (koda $1)",
        "virus-unknownscanner": "neznan antivirusni program:",
        "logouttext": "'''Odjavili ste se.'''\n\nNekatere strani bodo morda še naprej prikazane, kot da ste prijavljeni, dokler ne boste izpraznili predpomnilnika brskalnika.",
+       "cannotlogoutnow-title": "Trenutno se ne morete odjaviti",
+       "cannotlogoutnow-text": "Odjava ni možna pri uporabi $1.",
        "welcomeuser": "$1, dobrodošli!",
        "welcomecreation-msg": "Ustvarili ste račun.\nNe pozabite si prilagoditi vaših [[Special:Preferences|nastavitev {{GRAMMAR:rodilnik|{{SITENAME}}}}]].",
        "yourname": "Uporabniško ime:",
        "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",
+       "cannotloginnow-title": "Trenutno se ne morete prijaviti",
+       "cannotloginnow-text": "Prijava ni možna pri uporabi $1.",
        "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.",
        "resetpass_submit": "Nastavi geslo in se prijavi",
        "changepassword-success": "Vaše geslo je bilo uspešno spremenjeno!",
        "changepassword-throttled": "Nedavno ste izvedli preveč poskusov prijave.\nProsimo, počakajte $1, preden poskusite znova.",
+       "botpasswords": "Gesla botov",
+       "botpasswords-summary": "<em>Gesla botov</em> omogočajo dostop do uporabniškega računa z API-jem brez uporabe glavnih poverilnic računa za prijavo. Omejite lahko uporabniške pravice, ki so na voljo pri prijavi z geslom bota.\n\nČe ne veste, zakaj bi to morda uporabljali, tega najverjetneje ne potrebujete. Nihče vas ne sme prositi, da mu zgenerirate in daste geslo bota.",
+       "botpasswords-disabled": "Gesla botov so onemogočena.",
+       "botpasswords-no-central-id": "Za uporabo gesel botov morate biti prijavljeni v centraliziran račun.",
+       "botpasswords-existing": "Obstoječa gesla botov",
+       "botpasswords-createnew": "Ustvari novo geslo bota",
+       "botpasswords-editexisting": "Uredi obstoječe geslo bota",
+       "botpasswords-label-appid": "Ime bota:",
+       "botpasswords-label-create": "Ustvari",
+       "botpasswords-label-update": "Posodobi",
+       "botpasswords-label-cancel": "Prekliči",
+       "botpasswords-label-delete": "Izbriši",
+       "botpasswords-label-resetpassword": "Ponastavi geslo",
+       "botpasswords-label-grants": "Veljavne pravice:",
+       "botpasswords-help-grants": "Vsaka pravica dodeli dostop do navedenih uporabniških pravic, ki jih uporabniški račun že ima. Za več informacij si oglejte [[Special:ListGrants|tabelo pravic]].",
+       "botpasswords-label-restrictions": "Omejitve uporabe:",
+       "botpasswords-label-grants-column": "Odobreno",
+       "botpasswords-bad-appid": "Ime bota »$1« ni veljavno.",
+       "botpasswords-insert-failed": "Dodajanje imena bota »$1« ni uspelo. Ste ga že dodali?",
+       "botpasswords-update-failed": "Posodobitev imena bota »$1« je spodletelo. Ste ga izbrisali?",
+       "botpasswords-created-title": "Ustvarili smo geslo bota",
+       "botpasswords-created-body": "Uspešno smo ustvarili geslo bota »$1«.",
+       "botpasswords-updated-title": "Posodobili smo geslo bota",
+       "botpasswords-updated-body": "Uspešno smo posodobili geslo bota »$1«.",
+       "botpasswords-deleted-title": "Izbrisali smo geslo bota",
+       "botpasswords-deleted-body": "Uspešno smo izbrisali geslo bota »$1«.",
+       "botpasswords-newpassword": "Novo geslo za prijavo z imenom <strong>$1</strong> je <strong>$2</strong>. <em>Prosimo, zabeležite si to za uporabo v prihodnje.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider ni na voljo.",
+       "botpasswords-restriction-failed": "Omejitve gesla bota preprečujejo to prijavo.",
+       "botpasswords-invalid-name": "Navedeno uporabniško ime ne vsebuje ločila za geslo bota (»$1«).",
+       "botpasswords-not-exist": "Uporabnik »$1« nima gesla bota z imenom »$2«.",
        "resetpass_forbidden": "Gesla ne morete spremeniti",
        "resetpass-no-info": "Za neposreden dostop do te strani morate biti prijavljeni.",
        "resetpass-submit-loggedin": "Spremenite geslo",
        "passwordreset-emailtext-ip": "Nekdo (verjetno vi, z IP-naslova $1) je zahteval ponastavitev vašega\ngesla na {{SITENAME}} ($4). S tem e-poštnim naslovom\n{{PLURAL:$3|je povezan naslednji uporabniški račun|sta povezana naslednja uporabniška računa|so povezani naslednji uporabniški računi}}:\n\n$2\n\n{{PLURAL:$3|Začasno geslo bo poteklo|Začasni gesli bosta potekli|Začasna gesla bodo potekla}} v {{PLURAL:$5|enem dnevu|$5 dneh}}.\nPrijavite se in izberite novo geslo. Če je zahtevo podal\nnekdo drug ali pa ste se spomnili svojega prvotnega gesla in ga več\nne želite spremeniti, lahko to sporočilo prezrete in nadaljujete z uporabo\nsvojega starega gesla.",
        "passwordreset-emailtext-user": "Uporabnik $1 na strani {{SITENAME}} je zahteval ponastavitev vašega gesla na {{SITENAME}}\n($4). S tem e-poštnim naslovom {{PLURAL:$3|je povezan naslednji uporabniški račun|sta povezana naslednja uporabniška računa|so povezani naslednji uporabniški računi}}:\n\n$2\n\n{{PLURAL:$3|Začasno geslo bo poteklo|Začasni gesli bosta potekli|Začasna gesla bodo potekla}} v {{PLURAL:$5|enem dnevu|$5 dneh}}.\nPrijavite se in izberite novo geslo sedaj. Če je zahtevo podal\nnekdo drug ali pa ste se spomnili svojega prvotnega gesla in ga več\nne želite spremeniti, lahko to sporočilo prezrete in nadaljujete z uporabo\nsvojega starega gesla.",
        "passwordreset-emailelement": "Uporabniško ime: \n$1\n\nZačasno geslo: \n$2",
-       "passwordreset-emailsentemail": "Če je to registriran e-poštni naslov za vaš račun, vam bomo poslali e-pošto za postavitev gesla.",
-       "passwordreset-emailsentusername": "Če obstaja pripadajoč registriran e-poštni naslov, vam bomo poslali e-pošto za postavitev gesla.",
+       "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",
        "changeemail": "Sprememba ali odstranitev e-poštnega naslova",
        "userrights": "Upravljanje s pravicami uporabnikov",
        "userrights-lookup-user": "Upravljanje z uporabniškimi skupinami",
        "userrights-user-editname": "Vpišite uporabniško ime:",
-       "editusergroup": "Uredi uporabniške skupine",
+       "editusergroup": "Uredi {{GENDER:$1|uporabnikove|uporabničine}} skupine",
        "editinguser": "Urejanje pravic {{GENDER:$1|uporabnika|uporabnice}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Urejanje uporabniških skupin",
-       "saveusergroups": "Shrani spremembe",
+       "saveusergroups": "Shrani {{GENDER:$1|uporabnikove|uporabničine}} skupine",
        "userrights-groupsmember": "Član skupine:",
        "userrights-groupsmember-auto": "Posreden član:",
        "userrights-groups-help": "Spreminjate lahko skupine uporabnika:\n* Obkljukano polje pomeni uporabnika, ki je v skupini\n* Odkljukano polje pomeni uporabnika, ki ni v skupini\n* Zvezdica (*) kaže, da uporabnika ne boste mogli odstraniti iz skupine, ko ga vanjo dodate oz. obratno.",
        "right-createpage": "Ustvarjanje strani (ki niso pogovorne)",
        "right-createtalk": "Ustvarjanje pogovornih strani",
        "right-createaccount": "Ustvarjanje novih uporabniških računov",
+       "right-autocreateaccount": "Samodejna prijava z zunanjim uporabniškim računom",
        "right-minoredit": "Označevanje urejanj kot manjših",
        "right-move": "Premikanje strani",
        "right-move-subpages": "Premikanje strani s pripadajočimi podstranmi",
        "right-blockemail": "Drugemu uporabniku lahko prepreči pošiljanje e-pošte",
        "right-hideuser": "Blokiranje uporabnika, in skritje pred javnostjo",
        "right-ipblock-exempt": "Izogne se blokadam IP-naslova, samodejnim blokadam in blokadam območij",
-       "right-proxyunbannable": "Izogne se samodejnim blokadam proxyjev",
        "right-unblockself": "Odblokiraj se",
        "right-protect": "Spreminjanje stopenj zaščite in urejanje kaskadno zaščitenih strani",
        "right-editprotected": "Urejanje strani, zaščitenih kot »{{int:protect-level-sysop}}«",
        "right-managechangetags": "Ustvarjanje in brisanje [[Special:Tags|oznak]] iz zbirke podatkov",
        "right-applychangetags": "Uveljavitev [[Special:Tags|oznak]] skupaj s spremembami",
        "right-changetags": "Dodajanje in odstranjevanje poljubnih [[Special:Tags|oznak]] na posameznih redakcijah in dnevniških vnosih",
+       "grant-generic": "Snov pravic »$1«",
+       "grant-group-page-interaction": "Interakcija s stranmi",
+       "grant-group-file-interaction": "Interakcija s predstavnostjo",
+       "grant-group-watchlist-interaction": "Interakcija s svojim spiskom nadzorov",
+       "grant-group-email": "Pošiljanje e-pošte",
+       "grant-group-high-volume": "Izvajanje visokoobsežnih dejavnosti",
+       "grant-group-customization": "Prilagoditve in nastavitve",
+       "grant-group-administration": "Izvajanje administrativnih dejanj",
+       "grant-group-other": "Druga dejavnost",
+       "grant-blockusers": "Blokiranje in odblokiranje uporabnikov",
+       "grant-createaccount": "Ustvarjanje računov",
+       "grant-createeditmovepage": "Ustvarjanje, urejanje in prestavljanje strani",
+       "grant-delete": "Brisanje strani, redakcij in dnevniških vnosov",
+       "grant-editinterface": "Urejanje imenskega prostora MediaWiki in uporabniškega CSS/JavaScripta",
+       "grant-editmycssjs": "Urejanje svojega uporabniškega CSS/JavaScripta",
+       "grant-editmyoptions": "Urejanje svojih uporabniških nastavitev",
+       "grant-editmywatchlist": "Urejanje svojega spiska nadzorov",
+       "grant-editpage": "Urejanje obstoječih strani",
+       "grant-editprotected": "Urejanje zaščitenih strani",
+       "grant-highvolume": "Visokoobsežno urejanje",
+       "grant-oversight": "Skrivanje uporabnikov in zatiranje redakcij",
+       "grant-patrol": "Nadzor sprememb strani",
+       "grant-protect": "Zaščita in odstranitev zaščite strani",
+       "grant-rollback": "Razveljavitev sprememb strani",
+       "grant-sendemail": "Pošiljanje e-pošte drugim uporabnikom",
+       "grant-uploadeditmovefile": "Nalaganje, zamenjava in prestavljanje datotek",
+       "grant-uploadfile": "Nalaganje novih datotek",
+       "grant-basic": "Osnovne pravice",
+       "grant-viewdeleted": "Ogled izbrisanih datotek in strani",
+       "grant-viewmywatchlist": "Ogled svojega spiska nadzorov",
        "newuserlogpage": "Dnevnik registracij uporabnikov",
        "newuserlogpagetext": "Prikazan je dnevnik nedavnih registracij novih uporabnikov.",
        "rightslog": "Dnevnik uporabniških pravic",
        "action-createpage": "ustvarjenje strani",
        "action-createtalk": "ustvarjanje pogovornih strani",
        "action-createaccount": "registracija tega uporabniškega računa",
+       "action-autocreateaccount": "samodejno ustvarjanje zunanjega uporabniškega računa",
        "action-history": "ogled zgodovine strani",
        "action-minoredit": "označevanje tega urejanja kot manjšega",
        "action-move": "premik te strani",
        "upload-form-label-select-file": "Izberi datoteko",
        "upload-form-label-infoform-title": "Podrobnosti",
        "upload-form-label-infoform-name": "Ime",
+       "upload-form-label-infoform-name-tooltip": "Edinstven opisen naslov datoteke, ki bo služil kot ime datoteke. Uporabljate lahko navadni jezik s presledki. Ne vključujte datotečne končnice.",
        "upload-form-label-infoform-description": "Opis",
+       "upload-form-label-infoform-description-tooltip": "Na kratko opišite vse opazno o delu.\nPri fotografijah omenite glavne stvari, ki so upodobljene, priložnost ali kraj.",
        "upload-form-label-usage-title": "Uporaba",
        "upload-form-label-usage-filename": "Ime datoteke",
        "foreign-structured-upload-form-label-own-work": "To je moje lastno delo",
        "log-title-wildcard": "Iskanje po naslovih, začenši s tem besedilom",
        "showhideselectedlogentries": "Pokaži/skrij izbrane dnevniške vnose",
        "log-edit-tags": "Urejanje oznak izbranih dnevniških vnosov",
+       "checkbox-select": "Izberi: $1",
+       "checkbox-all": "Vse",
+       "checkbox-none": "Nič",
+       "checkbox-invert": "Obrni izbor",
        "allpages": "Vse strani",
        "nextpage": "Naslednja stran ($1)",
        "prevpage": "Prejšnja stran ($1)",
        "listgrouprights-namespaceprotection-header": "Omejitve imenskih prostorov",
        "listgrouprights-namespaceprotection-namespace": "Imenski prostor",
        "listgrouprights-namespaceprotection-restrictedto": "Pravice, ki uporabniku omogočajo urejanje",
+       "listgrants": "Pravice",
+       "listgrants-summary": "V nadaljevanju je seznam pravic z njihovim pripadajočim dostopom do uporabniških pravic. Uporabniki lahko overijo aplikacije za uporabo njihovega računa, a z omejenimi pravicami glede na pravice, ki jih je uporabnik dodelil aplikaciji. Vendar aplikacija, ki deluje v imenu uporabnika, ne more uporabljati pravic, ki jih uporabnik nima.\nMorda obstajajo [[{{MediaWiki:Listgrouprights-helppage}}|dodatne informacije]] o posameznih pravicah.",
+       "listgrants-grant": "Pravica",
+       "listgrants-rights": "Pravice",
        "trackingcategories": "Sledilne kategorije",
        "trackingcategories-summary": "Stran navaja sledilne kategorije, ki jih samodejno napolni programje MediaWiki. Njihova imena lahko spremenite s spreminjanjem ustreznih sistemskih sporočil v imenskem prostoru {{ns:8}}.",
        "trackingcategories-msg": "Sledilna kategorija",
        "wlshowhideanons": "brezimni uporabniki",
        "wlshowhidepatr": "pregledana urejanja",
        "wlshowhidemine": "moja urejanja",
+       "wlshowhidecategorization": "kategorizacija strani",
        "watchlist-options": "Možnosti spiska nadzorov",
        "watching": "Nadziranje ...",
        "unwatching": "Nenadziranje ...",
        "unblock": "Odblokiraj uporabnika",
        "blockip": "Blokiraj {{GENDER:$1|uporabnika|uporabnico}}",
        "blockip-legend": "Blokiraj uporabnika",
-       "blockiptext": "Naslednji obrazec vam omogoča, da določenemu IP-naslovu ali uporabniškemu imenu preprečite urejanje.\nTo storimo le zaradi zaščite pred nepotrebnim uničevanjem in po [[{{MediaWiki:Policy-url}}|pravilih]].\nVnesite tudi razlog (''na primer'' seznam strani, ki jih je uporabnik po nepotrebnem kvaril).",
+       "blockiptext": "Naslednji obrazec vam omogoča, da določenemu IP-naslovu ali uporabniškemu imenu preprečite urejanje.\nTo storimo le zaradi zaščite pred nepotrebnim uničevanjem in po [[{{MediaWiki:Policy-url}}|pravilih]].\nVnesite tudi razlog (''na primer'' seznam strani, ki jih je uporabnik po nepotrebnem kvaril).\nBlokirate lahko razpone IP s skladnjo[https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR]; najširši dovoljen razpon je /$1 za IPv4 in /$2 za IPv6.",
        "ipaddressorusername": "IP-naslov ali uporabniško ime",
        "ipbexpiry": "Pretek",
        "ipbreason": "Razlog:",
        "block-log-flags-hiddenname": "uporabniško ime skrito",
        "range_block_disabled": "Možnost administratorjev za blokiranje urejanja IP-razponom je onemogočena.",
        "ipb_expiry_invalid": "Neveljaven čas preteka",
+       "ipb_expiry_old": "Čas izteka je v preteklosti.",
        "ipb_expiry_temp": "Blokade skritih uporabniških imen morajo biti trajne.",
        "ipb_hide_invalid": "Ne morem skriti tega računa; ima več kot $1 {{PLURAL:$1|urejanje|urejanji|urejanja|urejanj}}.",
        "ipb_already_blocked": "\"$1\" je že blokiran",
        "lockedbyandtime": "($1 dne $2 ob $3)",
        "move-page": "Preimenuj $1",
        "move-page-legend": "Prestavitev strani",
-       "movepagetext": "Z naslednjim obrazcem lahko stran preimenujete in hkrati prestavite tudi vso njeno zgodovino.\nDosedanja stran se bo spremenila v preusmeritev na prihodnje mesto.\nSamodejno lahko posodobite preusmeritve, ki kažejo na dosedanji naslov.\nČe se za to ne odločite, ne pozabite preveriti vseh [[Special:DoubleRedirects|dvojnih]] ali [[Special:BrokenRedirects|pretrganih preusmeritev]].\nOdgovorni ste, da bodo povezave še naprej kazale na prava mesta.\n\nKjer stran z izbranim novim imenom že obstaja, dejanje '''ne''' bo izvedeno, razen če je sedanja stran preusmeritev in brez zgodovine urejanj.\nTo pomeni, da lahko, če se zmotite, strani vrnete prvotno ime, ne morete pa prepisati že obstoječe strani.\n\n'''Opozorilo!'''\nPrestavitev strani je lahko za priljubljeno stran velika in nepričakovana sprememba, zato pred izbiro ukaza dobro premislite.",
-       "movepagetext-noredirectfixer": "Z uporabo spodnjega obrazca lahko preimenujete stran tako, da prestavite vso njeno zgodovino na novo ime.\nStar naslov bo postal preusmeritvena stran na nov naslov.\nNe pozabite preveriti [[Special:DoubleRedirects|dvojnih]] ali [[Special:BrokenRedirects|pretrganih preusmeritev]].\nVi ste odgovorni, da vse povezave še naprej kažejo tja, kamor naj bi.\n\nUpoštevajte, da stran '''ne''' bo prestavljena, če že obstaja stran z novim naslovom, razen če je prazna ali preusmeritev brez pretekle zgodovine urejanj.\nTo pomeni, da lahko stran preimenujete nazaj, če ste naredili napako, vendar ne morete prepisati obstoječe strani.\n\n'''Opozorilo!'''\nTo je lahko velika in nepričakovana sprememba za priljubljeno stran;\nprosimo, pred nadaljevanjem se prepričajte, da razumete posledice tega dejanja.",
+       "movepagetext": "Z naslednjim obrazcem lahko stran preimenujete in hkrati prestavite tudi vso njeno zgodovino.\nDosedanja stran se bo spremenila v preusmeritev na prihodnje mesto.\nSamodejno lahko posodobite preusmeritve, ki kažejo na dosedanji naslov.\nČe se za to ne odločite, ne pozabite preveriti vseh [[Special:DoubleRedirects|dvojnih]] ali [[Special:BrokenRedirects|pretrganih preusmeritev]].\nOdgovorni ste, da bodo povezave še naprej kazale na prava mesta.\n\nKjer stran z izbranim novim imenom že obstaja, dejanje <strong>ne</strong> bo izvedeno, razen če je sedanja stran preusmeritev in brez zgodovine urejanj.\nTo pomeni, da lahko, če se zmotite, strani vrnete prvotno ime, ne morete pa prepisati že obstoječe strani.\n\n<strong>Opomba:</strong>\nPrestavitev strani je lahko za priljubljeno stran velika in nepričakovana sprememba, zato pred izbiro ukaza dobro premislite.",
+       "movepagetext-noredirectfixer": "Z uporabo spodnjega obrazca lahko preimenujete stran tako, da prestavite vso njeno zgodovino na novo ime.\nStar naslov bo postal preusmeritvena stran na nov naslov.\nNe pozabite preveriti [[Special:DoubleRedirects|dvojnih]] ali [[Special:BrokenRedirects|pretrganih preusmeritev]].\nVi ste odgovorni, da vse povezave še naprej kažejo tja, kamor naj bi.\n\nUpoštevajte, da stran <strong>ne</strong> bo prestavljena, če že obstaja stran z novim naslovom, razen če je preusmeritev brez pretekle zgodovine urejanj.\nTo pomeni, da lahko stran preimenujete nazaj, če ste naredili napako, vendar ne morete prepisati obstoječe strani.\n\n<strong>Opomba:</strong>\nTo je lahko velika in nepričakovana sprememba za priljubljeno stran;\nprosimo, pred nadaljevanjem se prepričajte, da razumete posledice tega dejanja.",
        "movepagetalktext": "Če označite to polje, bomo pripadajočo pogovorno stran samodejno prestavili na nov naslov, razen kadar tam že obstaja neprazna pogovorna stran.\n\nČe je tako, boste morali pogovorno stran, če želite, prestaviti ali združiti ročno.",
        "moveuserpage-warning": "'''Opozorilo:''' Premikate uporabniško stran. To pomeni, da bo premaknjena samo stran in uporabnik ''ne'' bo preimenovan.",
        "movecategorypage-warning": "<strong>Opozorilo:</strong> Prestavili boste stran kategorije. Pomnite, da boste prestavili samo stran; vse strani v stari kategoriji <em>ne</em> bomo prekategorizirali v novo kategorijo.",
        "movenosubpage": "Ta stran nima podstrani.",
        "movereason": "Razlog:",
        "revertmove": "vrni",
-       "delete_and_move_text": "==Treba bi bilo brisati==\n\nCiljna stran »[[:$1]]« že obstaja. Ali jo želite, da bi pripravili prostor za prestavitev, izbrisati?",
+       "delete_and_move_text": "Ciljna stran »[[:$1]]« že obstaja.\nAli jo želite izbrisati, da bi pripravite prostor za prestavitev?",
        "delete_and_move_confirm": "Da, izbriši stran",
        "delete_and_move_reason": "Izbrisano z namenom pripraviti prostor za »[[$1]]«",
        "selfmove": "Izvirni in ciljni naslov sta enaka;\nstrani ni mogoče prestaviti samo vaše.",
        "move-leave-redirect": "Na prejšnji strani ustvari preusmeritev",
        "protectedpagemovewarning": "'''Opozorilo:''' Stran je bila zaklenjena in jo lahko prestavljajo samo uporabniki z administratorskimi pravicami.\nZa sklicevanje je naveden zadnji vnos v dnevnik:",
        "semiprotectedpagemovewarning": "'''Opomba:''' Stran je bila zaklenjena in jo lahko prestavljajo samo registrirani uporabniki.\nZa sklicevanje je naveden zadnji vnos v dnevniku:",
-       "move-over-sharedrepo": "== Datoteka obstaja ==\n[[:$1]] obstaja v deljeni shrambi. Premik datoteke na ta naslov bo prepisalo deljeno datoteko.",
+       "move-over-sharedrepo": "[[:$1]] obstaja v deljeni shrambi. Premik datoteke na ta naslov bo prepisalo deljeno datoteko.",
        "file-exists-sharedrepo": "Izbrano ime datoteke je že v uporabi v deljeni shrambi.\nProsimo, izberite drugo ime.",
        "export": "Izvoz strani",
        "exporttext": "Besedilo in urejevalno zgodovino ene ali več strani lahko izvozite v obliki XML.\nTo je mogoče uvoziti v drug wiki z uporabo MediaWiki preko [[Special:Import|strani za uvoz]].\n\nČe želite izvoziti strani, v spodnje polje vpišite naslove (enega v vsako vrstico) in označite, ali želite vse prejšnje različice z vrsticami o zgodovini strani ali le trenutno različico s podatki o trenutnem urejanju.\n\nČe gre za slednje, lahko uporabite tudi povezavo, npr. [[{{#Special:Export}}/{{MediaWiki:Mainpage}}]] za\nstran »[[{{MediaWiki:Mainpage}}]]«.",
        "export-download": "Shrani kot datoteko",
        "export-templates": "Vključi predloge",
        "export-pagelinks": "Vključi povezane strani do globine:",
+       "export-manual": "Dodaj strani ročno:",
        "allmessages": "Sistemska sporočila",
        "allmessagesname": "Ime",
        "allmessagesdefault": "Prednastavljeno besedilo",
        "javascripttest-pagetext-frameworks": "Prosimo, izberite enega od naslednjih ogrodjev za preizkušanje: $1",
        "javascripttest-pagetext-skins": "Izberite kožo, v kateri želite pognati preizkuse:",
        "javascripttest-qunit-intro": "Oglejte si [$1 dokumentacijo o preizkušanju] na mediawiki.org.",
-       "tooltip-pt-userpage": "Vaša uporabniška stran",
+       "tooltip-pt-userpage": "{{GENDER:|Vaša}} uporabniška stran",
        "tooltip-pt-anonuserpage": "Uporabniška stran IP-naslova, ki ga uporabljate",
-       "tooltip-pt-mytalk": "Vaša pogovorna stran",
+       "tooltip-pt-mytalk": "{{GENDER:|Vaša}} pogovorna stran",
        "tooltip-pt-anontalk": "Pogovor o urejanjih s tega IP-naslova",
-       "tooltip-pt-preferences": "Vaše nastavitve",
+       "tooltip-pt-preferences": "{{GENDER:|Vaše}} nastavitve",
        "tooltip-pt-watchlist": "Seznam strani, katerih spremembe spremljate",
-       "tooltip-pt-mycontris": "Seznam vaših prispevkov",
+       "tooltip-pt-mycontris": "Seznam {{GENDER:|vaših}} prispevkov",
        "tooltip-pt-anoncontribs": "Seznam urejanj s tega IP-naslova",
        "tooltip-pt-login": "Prijava ni obvezna, vendar je zaželena",
        "tooltip-pt-logout": "Odjavite se",
        "tooltip-t-recentchangeslinked": "Zadnje spremembe na s trenutno povezanih straneh",
        "tooltip-feed-rss": "RSS-vir strani",
        "tooltip-feed-atom": "Atom-vir strani",
-       "tooltip-t-contributions": "Preglejte seznam uporabnikovih prispevkov",
-       "tooltip-t-emailuser": "Pošljite uporabniku e-pismo",
+       "tooltip-t-contributions": "Preglejte seznam {{GENDER:$1|uporabnikovih|uporabničinih}} prispevkov",
+       "tooltip-t-emailuser": "Pošljite {{GENDER:$1|uporabniku|uporabnici}} e-pošto",
        "tooltip-t-info": "Več informacij o strani",
        "tooltip-t-upload": "Naložite slike ali predstavnostne datoteke",
        "tooltip-t-specialpages": "Preglejte seznam vseh posebnih strani",
        "pageinfo-category-files": "Število datotek",
        "markaspatrolleddiff": "Označite kot nadzorovano",
        "markaspatrolledtext": "Označite stran kot nadzorovano",
+       "markaspatrolledtext-file": "Označite različico datoteke kot nadzorovano",
        "markedaspatrolled": "Označeno kot nadzorovano",
        "markedaspatrolledtext": "Izbrana redakcija [[:$1]] je bila označena kot nadzorovana.",
        "rcpatroldisabled": "Spremljanje zadnjih sprememb je onemogočeno.",
        "newimages-legend": "Filter",
        "newimages-label": "Ime datoteke (ali njen del):",
        "newimages-showbots": "Prikaži nalaganja botov",
+       "newimages-hidepatrolled": "Skrij nadzorovana nalaganja",
        "noimages": "Nič ni videti.",
        "ilsubmit": "Išči",
        "bydate": "po datumu",
        "scarytranscludefailed-httpstatus": "[Pridobivanje predloge za $1 ni uspelo: HTTP $2]",
        "scarytranscludetoolong": "[Spletni naslov je predolg]",
        "deletedwhileediting": "'''Opozorilo''': Med vašim urejanjem je bila stran izbrisana!",
-       "confirmrecreate": "Medtem ko ste stran urejali, jo je uporabnik [[User:$1|$1]] ([[User talk:$1|pogovor]]) izbrisal z razlogom:\n:''$2''\nProsimo, potrdite, da jo resnično želite znova ustvariti.",
-       "confirmrecreate-noreason": "Uporabnik [[User:$1|$1]] ([[User talk:$1|pogovor]]) je izbrisal to stran po začetku vašega urejanja. Potrdite, da jo resnično želite znova ustvariti.",
+       "confirmrecreate": "Medtem ko ste stran urejali, jo je {{GENDER:$1|uporabnik|uporabnica}} [[User:$1|$1]] ([[User talk:$1|pogovor]]) {{GENDER:$1|izbrisal|izbrisala}} z razlogom:\n: <em>$2</em>\nProsimo, potrdite, da jo resnično želite znova ustvariti.",
+       "confirmrecreate-noreason": "{{GENDER:$1|Uporabnik|Uporabnica}} [[User:$1|$1]] ([[User talk:$1|pogovor]]) je {{GENDER:$1|izbrisal|izbrisala}} to stran po začetku vašega urejanja. Potrdite, da jo resnično želite znova ustvariti.",
        "recreate": "Ponovno ustvari",
        "unit-pixel": " točk",
        "confirm_purge_button": "Osveži",
        "watchlisttools-edit": "Prikaz in urejanje spiska nadzorov",
        "watchlisttools-raw": "Uredi gol spisek nadzorov",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|pogovor]])",
+       "timezone-local": "Krajevno",
        "duplicate-defaultsort": "'''Opozorilo:''' Privzeti ključ razvrščanja »$2« prepiše prejšnji privzeti ključ razvrščanja »$1«.",
        "duplicate-displaytitle": "<strong>Opozorilo:</strong> Prikazni naslov »$2« prepiše prejšnji prikazni naslov »$1«.",
        "invalid-indicator-name": "<strong>Napaka:</strong> Atribut <code>name</code> indikatorjev stanja strani ne sme biti prazen.",
        "version-libraries-license": "Dovoljenje",
        "version-libraries-description": "Opis",
        "version-libraries-authors": "Avtorji",
-       "redirect": "Preusmeri po datoteki, uporabniku, strani ali ID-ju redakcije",
+       "redirect": "Preusmeri po datoteki ali ID-ju uporabnika, strani, redakcije ali dnevnika",
        "redirect-legend": "Preusmeritev na datoteko ali stran",
-       "redirect-summary": "Posebna stran preusmeri na datoteko (če podate ime datoteke), stran (če podate ID redakcije ali ID strani) ali uporabniško stran (če podate številski ID uporabnika). Uporaba: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]] ali [[{{#Special:Redirect}}/user/101]].",
+       "redirect-summary": "Posebna stran preusmeri na datoteko (če podate ime datoteke), stran (če podate ID redakcije ali ID strani), uporabniško stran (če podate številski ID uporabnika) ali dnevniški vnos (če podate ID dnevnika). Uporaba: [[{{#Special:Redirect}}/file/Example.jpg]], [[{{#Special:Redirect}}/page/64308]], [[{{#Special:Redirect}}/revision/328429]][[{{#Special:Redirect}}/user/101]] ali [[{{#Special:Redirect}}/logid/186]].",
        "redirect-submit": "Pojdi",
        "redirect-lookup": "Iskanje:",
        "redirect-value": "Vrednost:",
        "redirect-page": "ID strani",
        "redirect-revision": "Redakcija strani",
        "redirect-file": "Ime datoteke",
+       "redirect-logid": "ID dnevnika",
        "redirect-not-exists": "Vrednosti ni mogoče najti",
        "fileduplicatesearch": "Iskanje podvojenih datotek",
        "fileduplicatesearch-summary": "Iskanje podvojenih datotek, ki temelji na podlagi njenih hashvrednosti.",
        "expand_templates_preview": "Predogled",
        "expand_templates_preview_fail_html": "<em>Ker ima {{SITENAME}} omogočen surov HTML in je prišlo do izgube podatkov o seji, smo predogled skrili kot previdnostni ukrep pred napadi z JavaScriptom.</em>\n\n<strong>Če je to veljaven poskus predogleda, poskusite znova.</strong>\nČe še vedno ne deluje, se poskusite [[Special:UserLogout|odjaviti]] in znova prijaviti.",
        "expand_templates_preview_fail_html_anon": "<em>Ker ima {{SITENAME}} omogočen surov HTML in niste prijavljeni, smo predogled skrili kot previdnostni ukrep pred napadi z JavaScriptom.</em>\n\n<strong>Če je to veljaven poskus predogleda, se [[Special:UserLogin|prijavite]] in poskusite znova.</strong>",
+       "expand_templates_input_missing": "Navesti boste morali vsaj nekaj vhodnega besedila.",
        "pagelanguage": "Izbirnik jezika strani",
        "pagelang-name": "Stran",
        "pagelang-language": "Jezik",
        "pagelang-use-default": "Uporabi privzeti jezik",
        "pagelang-select-lang": "Izberite jezik",
+       "pagelang-submit": "Potrdi",
        "right-pagelang": "Spreminjanje jezika strani",
        "action-pagelang": "spreminjanje jezika strani",
        "log-name-pagelang": "Dnevnik spreminjanja jezika",
        "mediastatistics": "Statistika predstavnosti",
        "mediastatistics-summary": "Statistika o naloženih vrstah datotek. To vključuje samo najnovejše različice datotek. Stare in izbrisane različice niso vključene.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 bajt|$1 bajta|$1 bajti|$1 bajtov}} ($2; $3 %)",
+       "mediastatistics-bytespertype": "Skupna velikost datoteke za ta razdelek: $1 {{PLURAL:$1|bajt|bajta|bajte|bajtov}} ($2; $3 %).",
+       "mediastatistics-allbytes": "Skupna velikost datoteke za vse datoteke: $1 {{PLURAL:$1|bajt|bajta|bajte|bajtov}} ($2).",
        "mediastatistics-table-mimetype": "Vrsta MIME",
        "mediastatistics-table-extensions": "Možne razširitve",
        "mediastatistics-table-count": "Število datotek",
        "mediastatistics-header-text": "Besedilno",
        "mediastatistics-header-executable": "Izvedljive datoteke",
        "mediastatistics-header-archive": "Stisnjene oblike",
+       "mediastatistics-header-total": "Vseh datotek",
        "json-warn-trailing-comma": "Iz JSON-a smo odstranili $1 {{PLURAL:$1|končno vejico|končni vejici|končne vejice|končnih vejic}}",
        "json-error-unknown": "Naleteli smo na težavo z JSON-om. Napaka: $1",
        "json-error-depth": "Presegli smo največjo globino sklada",
        "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."
+       "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",
+       "sessionprovider-nocookies": "Piškotki so morda onemogočeni. Prepričaje se, da imate piškotke omogočene, in začnite znova.",
+       "randomrootpage": "Naključna korenska stran"
 }
index 5130deb..7fafaed 100644 (file)
        "right-viewmyprivateinfo": "Eegista macluumaadka gaarka ah (sida ciwaanka e-mail-ka, iyo magaca saxda ah)",
        "right-editmyprivateinfo": "Bedel macluumaadka gaarka ah (sida. ciwaanka e-mail-ka , iyo magaca saxda ah)",
        "right-editmyoptions": "Bedelka dooqyada",
+       "grant-createeditmovepage": "Sameeyn, bedel, iyo wareejin bog",
        "newuserlogpage": "Gudagalaha Isticmaale sameeyay",
        "action-read": "akhri boggaan",
        "action-edit": "wax ka bedel boggaan",
        "nolinkstoimage": "Ma'ay jiraan beyjaj ku xiran faylkaan.",
        "sharedupload-desc-here": "Faylkaan wuxuu ka socdaa  $1 waxaana laga yaabaa in lagu isticmaalay mashruucyada kale.\nTafaasiishiisa waxee ku qorantahay [$2 bogga tafaasiisha faylka] oo ka arki kartid hoostaan.",
        "filerevert-comment": "Sababta:",
+       "filerevert-success": "<strong>[[Media:$1|$1]]</strong> waxaa dib loogu celiyay [$4 nuqulkii taariikhdiisu ahayd $3, $2].",
        "filedelete": "Tirtir $1",
        "filedelete-legend": "Tirtir fayl",
        "filedelete-intro": "Waxaad tirtiri rabtaa faylka '''[[Media:$1|$1]]''' iyo dhamaan taariikhdiisa.",
        "imagelisttext": "Hoos waxaa yaala liiska '''$1''' {{PLURAL:$1|file|faylalka}} oo u kala soocan $2.",
        "ilsubmit": "Raadi",
        "bydate": "hab taariikheed",
+       "ago": "$1 kahor",
        "metadata-help": "Faylkaan wuxuu leeyahay  faah faahin dheeraad ah,waxaa laga yaabaa in lagu  isticmaalay digital kaamiro ama skanner oo lagu sameeyo sawir ama lagu digitilays gareeyay.\nHadii faylka wax laga badalay sida oo markiisa hore ahaa, waxaa laga yaabaa in ee  faah faahinta faylkaan eesan dhameestirneen.",
        "metadata-fields": "Sawirka qeybihiisa metadata oo ku tixan fariintaan waxaa lagu dari doonaa bogga sawirka meesha laga arko markii miiska metadata la qariyo.Kuwa kale waxaa loo qarinaa sida 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",
        "exif-iimcategory": "Qeybta",
index 5bf69da..89b1bcd 100644 (file)
        "permalink": "Lidhje e përhershme",
        "print": "Printo",
        "view": "Shiko",
-       "view-foreign": "Pamja <span class=\"notranslate\" translate=\"asnjë\">$1</span>",
+       "view-foreign": "Shikoje në $1",
        "edit": "Redakto",
        "edit-local": "Redakto përshkrimin lokal",
        "create": "Krijo",
        "powersearch-legend": "Kërkim i përparuar",
        "powersearch-ns": "Kërkim në hapësira:",
        "powersearch-togglelabel": "Zgjedh:",
-       "powersearch-toggleall": "Tâna",
-       "powersearch-togglenone": "Asnji",
+       "powersearch-toggleall": "Të gjitha",
+       "powersearch-togglenone": "Asnjë",
        "search-external": "Kërkim i jashtëm",
        "searchdisabled": "<p>Kërkimi me tekst të plotë është bllokuar tani për tani ngaqë shërbyesi është shumë i ngarkuar; shpresojmë ta nxjerrim prapë në gjendje normale pas disa punimeve. Deri atëherë mund të përdorni Google-in për kërkime:</p>",
        "preferences": "Parapëlqimet",
index 89d80df..85ae24f 100644 (file)
@@ -65,6 +65,7 @@
        "tog-watchlisthidebots": "Сакриј измене ботова са списка надгледања",
        "tog-watchlisthideminor": "Сакриј мање измене са списка надгледања",
        "tog-watchlisthideliu": "Сакриј измене пријављених корисника са списка надгледања",
+       "tog-watchlistreloadautomatically": "Аутоматски освежи списак надгледања кад год се филтер измени (потребна JavaScript-а)",
        "tog-watchlisthideanons": "Сакриј измене анонимних корисника са списка надгледања",
        "tog-watchlisthidepatrolled": "Сакриј патролиране измене са списка надгледања",
        "tog-watchlisthidecategorization": "Сакриј категоризацију страница",
        "resetpass_submit": "Постави лозинку и пријави ме",
        "changepassword-success": "Ваша лозинка је успешно промењена!",
        "changepassword-throttled": "Превише пута сте покушали да се пријавите.\nМолимо вас да сачекате $1 пре него што покушате поново.",
+       "botpasswords-label-cancel": "Откажи",
+       "botpasswords-label-delete": "Обриши",
        "resetpass_forbidden": "Лозинка не може бити промењена",
        "resetpass-no-info": "Морате бити пријављени да бисте приступили овој страници.",
        "resetpass-submit-loggedin": "Промени лозинку",
        "right-blockemail": "онемогућавање корисницима да шаљу имејлове",
        "right-hideuser": "блокирање корисничког имена и његово сакривање од јавности",
        "right-ipblock-exempt": "заобилажење блокирања ИП адресе, аутоматска блокирања и блокирања опсега",
-       "right-proxyunbannable": "заобилажење аутоматских блокирања посредника",
        "right-unblockself": "деблокирање самог себе",
        "right-protect": "мењање степена заштите и уређивање страница под преносивом заштитом",
        "right-editprotected": "уређивање страница под заштитом „{{int:protect-level-sysop}}“",
        "right-managechangetags": "прављење и/или брисање [[Special:Tags|ознака]] из базе података",
        "right-applychangetags": "примењивање [[Special:Tags|ознака]] на нечије измене",
        "right-changetags": "додавање и уклањање разних [[Special:Tags|ознака]] на појединачним изменама и уносима у дневницима",
+       "grant-group-page-interaction": "Уређивање страница",
+       "grant-group-file-interaction": "Уређивање датотека",
+       "grant-group-watchlist-interaction": "Уређивање вашег списка надгледања",
+       "grant-group-email": "Пошаљи имејл",
+       "grant-createaccount": "Отварање налога",
+       "grant-createeditmovepage": "Прављење, уређивање и премештање страница",
+       "grant-delete": "Брисање страница, измена и уноса у дневницима",
+       "grant-editinterface": "Уређивање Медијавики именског простора и корисничких CSS/JavaScript страница",
+       "grant-editmywatchlist": "Уређивање вашег списка надгледања",
+       "grant-editpage": "Уређивање постојећих страница",
+       "grant-editprotected": "Уређивање заштићених страница",
+       "grant-uploadeditmovefile": "Отпремање, замена и премештање датотека",
+       "grant-uploadfile": "Слање нових датотека",
        "newuserlogpage": "Дневник нових корисника",
        "newuserlogpagetext": "Ово је дневник нових корисника.",
        "rightslog": "Дневник корисничких права",
        "foreign-structured-upload-form-label-own-work": "Ово је моје сопствено дело",
        "foreign-structured-upload-form-label-infoform-categories": "Категорије",
        "foreign-structured-upload-form-label-infoform-date": "Датум",
+       "foreign-structured-upload-form-2-label-ownwork": "Мора бити <strong>искључиво Ваше дело</strong>, а не скинуто са интернета",
+       "foreign-structured-upload-form-2-label-noderiv": "Не сме бити <strong>туђе дело</strong> или прерада истог",
+       "foreign-structured-upload-form-2-label-useful": "Мора бити <strong>образовна и корисна</strong> за друге",
+       "foreign-structured-upload-form-2-label-ccbysa": "Мора бити <strong>у реду да се објави заувек</strong> на интернету под лиценцом [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Ауторство-Делити под истим условима 4.0]",
+       "foreign-structured-upload-form-2-label-termsofuse": "Отпремањем ове датотеке потврђујете да сте носилац ауторских права исте и непозиво је предајте Викимедијиној остави под лиценцом Creative Commons Ауторство-Делити под истим условима 4.0 и прихватате [https://wikimediafoundation.org/wiki/Terms_of_Use услове коришћења].",
+       "foreign-structured-upload-form-3-label-question-website": "Да ли сте ову слику преузели са неког сајта или претрагом слика?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Да ли сте ви направили ову слику (сликали фотоапаратом, нацртали и сл.)?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Да ли садржи логотип или је инспирисана неким туђим делом?",
+       "foreign-structured-upload-form-3-label-yes": "Да",
+       "foreign-structured-upload-form-3-label-no": "Не",
        "backend-fail-stream": "Не могу да емитујем датотеку $1.",
        "backend-fail-backup": "Не могу да направим резерву датотеке $1.",
        "backend-fail-notexists": "Датотека $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-rights": "Права",
        "trackingcategories": "Медијавики категорије",
        "trackingcategories-summary": "Ова посебна страница је списак категорија које су део Медијавикија, оне се аутоматски ажурирају и њихови називи се могу мењањати уређивањем системских порука у именском простору {{ns:8}}.",
        "trackingcategories-name": "Име поруке",
        "pageinfo-category-files": "Број датотека",
        "markaspatrolleddiff": "Означи као патролирано",
        "markaspatrolledtext": "Означи страницу као патролирану",
+       "markaspatrolledtext-file": "Означи ову верзију датотеке као патролирану",
        "markedaspatrolled": "Означено као патролирано",
        "markedaspatrolledtext": "Изабрана измена на [[:$1]] је означена као патролирана.",
        "rcpatroldisabled": "Патролирање скорашњих измена је онемогућено",
        "newimages-legend": "Филтер",
        "newimages-label": "Назив датотеке (или њен део):",
        "newimages-showbots": "Прикажи датотеке које су послали ботови",
+       "newimages-hidepatrolled": "Сакриј патролирана отпремања",
        "noimages": "Нема ништа.",
        "ilsubmit": "Претражи",
        "bydate": "по датуму",
        "pagelang-name": "Страница",
        "pagelang-language": "Језик",
        "pagelang-select-lang": "Изабери језик",
+       "pagelang-submit": "Пошаљи",
        "right-pagelang": "мењање језика странице",
        "action-pagelang": "промену језика странице",
        "logentry-pagelang-pagelang": "$1 је {{GENDER:$2|променио|променила}} језик странице $3 из $4 у $5.",
        "mediastatistics-header-text": "Текстуалне",
        "mediastatistics-header-executable": "Извршне",
        "mediastatistics-header-archive": "Компресоване",
+       "mediastatistics-header-total": "Све датотеке",
        "json-error-syntax": "Грешка у синтакси",
        "headline-anchor-title": "Веза до овог одељка",
        "special-characters-group-latin": "латиница",
        "mw-widgets-dateinput-placeholder-month": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страница још увек не постоји",
        "mw-widgets-titleinput-description-redirect": "преусмерава на $1",
-       "api-error-blacklisted": "Изаберите другачији, описан назив."
+       "api-error-blacklisted": "Изаберите другачији, описан назив.",
+       "randomrootpage": "Случајна коренска страница"
 }
index a877573..7e42258 100644 (file)
        "right-blockemail": "onemogućavanje korisnicima da šalju e-poruke",
        "right-hideuser": "blokiranje korisničkog imena i njegovo sakrivanje od javnosti",
        "right-ipblock-exempt": "zaobilaženje blokiranja IP adrese, automatska blokiranja i blokiranja opsega",
-       "right-proxyunbannable": "zaobilaženje automatskih blokiranja posrednika",
        "right-unblockself": "deblokiranje samog sebe",
        "right-protect": "menjanje stepena zaštite i uređivanje stranica pod prenosivom zaštitom",
        "right-editprotected": "uređivanje stranice pod zaštitom „{{int:protect-level-sysop}}“",
        "right-sendemail": "slanje e-poruka drugim korisnicima",
        "right-passwordreset": "pregledanje poruka za obnavljanje lozinke",
        "right-managechangetags": "pravljenje i/ili brisanje [[Special:Tags|oznaka]] iz baze podataka",
+       "grant-group-page-interaction": "Uređivanje stranica",
+       "grant-group-file-interaction": "Uređivanje datoteka",
+       "grant-group-watchlist-interaction": "Uređivanje vašeg spiska nadgledanja",
+       "grant-group-email": "Slanje e-poruka",
+       "grant-createaccount": "Otvaranje naloga",
+       "grant-createeditmovepage": "Pravljenje, uređivanje i premeštanje stranica",
+       "grant-delete": "Brisanje stranica, izmena i unosa u dnevnicima",
+       "grant-editinterface": "Uređivanje Medijaviki imenskog prostora i korisničkih CSS/JavaScript stranica",
+       "grant-editpage": "Uređivanje postojećih stranica",
+       "grant-uploadeditmovefile": "Otpremanje, zamena i premeštanje datoteka",
+       "grant-uploadfile": "Slanje novih datoteka",
        "newuserlogpage": "Dnevnik novih korisnika",
        "newuserlogpagetext": "Ovo je dnevnik novih korisnika.",
        "rightslog": "Dnevnik korisničkih prava",
        "listgrouprights-namespaceprotection-header": "Ograničenja imenskih prostora",
        "listgrouprights-namespaceprotection-namespace": "Imenski prostor",
        "listgrouprights-namespaceprotection-restrictedto": "Prava potrebna za uređivanje",
+       "listgrants-rights": "Prava",
        "trackingcategories": "Medijaviki kategorije",
        "trackingcategories-name": "Ime poruke",
        "trackingcategories-desc": "Koje stranice se nalaze u kategoriji",
        "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."
+       "api-error-blacklisted": "Izaberite drugačiji, opisan naziv.",
+       "randomrootpage": "Slučajna korenska stranica"
 }
index f70bb5b..b8d7ae0 100644 (file)
        "october-date": "$1 oktober",
        "november-date": "$1 november",
        "december-date": "$1 december",
+       "period-am": "fm",
+       "period-pm": "em",
        "pagecategories": "{{PLURAL:$1|Kategori|Kategorier}}",
        "category_header": "Sidor i kategorin \"$1\"",
        "subcategories": "Underkategorier",
        "laggedslavemode": "<strong>Varning:</strong> Sidan kan sakna de senaste uppdateringarna.",
        "readonly": "Databasen är låst",
        "enterlockreason": "Ange varför databasen låsts och inkludera en uppskattning om när låsningen kommer att hävas",
-       "readonlytext": "Databasen är tillfälligt låst för nya inlägg och andra modifieringar, förmodligen på grund av rutinmässigt underhåll, efter vilket den kommer den att återgå till normalläge.\n\nDen systemadministratör som låste den har angivit följande förklaring: $1",
+       "readonlytext": "Databasen är tillfälligt låst för nya inlägg och andra modifieringar, förmodligen på grund av rutinmässigt underhåll, efter vilket den kommer att återgå till normalläge.\n\nDen systemadministratör som låste den har angivit följande förklaring: $1",
        "missing-article": "Databasen hittade inte texten för en sida som den borde ha funnit, med namnet \"$1\" $2.\n\nDetta orsakas oftast av att man följer en inaktuell länk till en jämförelse mellan versioner (diff) eller en historiklänk för en sida som raderats.\n\nOm inte så är fallet, kan du ha hittat en bugg i mjukvaran.\nRapportera gärna problemet till någon [[Special:ListUsers/sysop|administratör]], ange då URL:en (webbadressen).",
        "missingarticle-rev": "(versionsnummer: $1)",
        "missingarticle-diff": "(Skillnad: $1, $2)",
        "virus-scanfailed": "skanning misslyckades (kod $1)",
        "virus-unknownscanner": "okänt antivirusprogram:",
        "logouttext": "<strong>Du är nu utloggad.</strong>\n\nObservera att det, tills du tömmer din webbläsares cache, på vissa sidor kan det se ut som att du fortfarande är inloggad.",
+       "cannotlogoutnow-title": "Kan inte logga ut nu",
+       "cannotlogoutnow-text": "Det går inte att logga ut med $1.",
        "welcomeuser": "Välkommen, $1!",
        "welcomecreation-msg": "Ditt konto har skapats.\nDu kan justera dina [[Special:Preferences|{{SITENAME}}-inställningar]] om du vill.",
        "yourname": "Användarnamn:",
        "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",
+       "cannotloginnow-text": "Det går inte att logga in med $1.",
        "yourdomainname": "Din domän",
        "password-change-forbidden": "Du kan inte ändra lösenord på denna wiki.",
        "externaldberror": "Antingen inträffade autentiseringsproblem med en extern databas, eller så får du inte uppdatera ditt externa konto.",
        "resetpass_submit": "Ange lösenord och logga in",
        "changepassword-success": "Ditt lösenord har ändrats!",
        "changepassword-throttled": "Du har gjort för många inloggningsförsök nyligen.\nVänta $1 innan du försöker igen.",
+       "botpasswords": "Botlösenord",
+       "botpasswords-disabled": "Botlösenord är inaktiverade.",
+       "botpasswords-existing": "Befintliga botlösenord",
+       "botpasswords-createnew": "Skapa ett nytt botlösenord",
+       "botpasswords-editexisting": "Redigera ett befintligt botlösenord",
+       "botpasswords-label-appid": "Botnamn:",
+       "botpasswords-label-create": "Skapa",
+       "botpasswords-label-update": "Uppdatera",
+       "botpasswords-label-cancel": "Avbryt",
+       "botpasswords-label-delete": "Radera",
+       "botpasswords-label-resetpassword": "Återställ lösenordet",
+       "botpasswords-bad-appid": "Botnamnet \"$1\" är inte giltigt.",
+       "botpasswords-insert-failed": "Kunde inte lägga till botnamnet \"$1\". Har det redan lagts till?",
+       "botpasswords-update-failed": "Kunde inte uppdatera botnamnet \"$1\". Har det raderats?",
+       "botpasswords-created-title": "Botlösenord skapades",
+       "botpasswords-created-body": "Botlösenordet \"$1\" skapades.",
+       "botpasswords-updated-title": "Botlösenordet uppdaterades",
+       "botpasswords-updated-body": "Botlösenordet \"$1\" uppdaterades.",
+       "botpasswords-deleted-title": "Botlösenord raderades",
+       "botpasswords-deleted-body": "Botlösenordet \"$1\" raderades.",
+       "botpasswords-newpassword": "Det nya lösenordet att logga in för <strong>$1</strong> är <strong>$2</strong>. <em>Spara detta som framtida referens.</em>",
+       "botpasswords-not-exist": "Användaren \"$1\" har inte ett botlösenord som är \"$2\".",
        "resetpass_forbidden": "Lösenord kan inte ändras",
        "resetpass-no-info": "Du måste vara inloggad för att komma åt den här sidan direkt.",
        "resetpass-submit-loggedin": "Ändra lösenord",
        "passwordreset-emailtext-ip": "Någon (förmodligen du, från IP-adressen $1) begärde en återställning av ditt lösenord för {{SITENAME}} ($4). Följande användar{{PLURAL:$3|konto är förknippad|konton är förknippade}} med denna e-postadress:\n\n$2\n\n{{PLURAL:$3|Detta|Dessa}} tillfälliga lösenord kommer att gå ut om {{PLURAL:$5|en dag|$5 dagar}}.\nDu bör logga in och välja ett nytt lösenord nu. Om någon annan gjorde denna begäran, eller om du kommer ihåg ditt ursprungliga lösenord, och inte längre önskar ändra det, kan du ignorera detta meddelande och fortsätta använda ditt gamla lösenord.",
        "passwordreset-emailtext-user": "Användaren $1 på {{SITENAME}} begärde en återställning av ditt lösenord för {{SITENAME}} ($4). Följande användar{{PLURAL:$3|konto är förknippad|konton är förknippade}} med denna e-postadress:\n\n$2\n\n{{PLURAL:$3|Detta|Dessa}} tillfälliga lösenord kommer att gå ut om {{PLURAL:$5|en dag|$5 dagar}}.\nDu bör logga in och välja ett nytt lösenord nu. Om någon annan gjorde denna begäran, eller om du kommer ihåg ditt ursprungliga lösenord, och inte längre önskar ändra det, kan du ignorera detta meddelande och fortsätta använda ditt gamla lösenord.",
        "passwordreset-emailelement": "Användarnamn: \n$1\n\nTillfälligt lösenord: \n$2",
-       "passwordreset-emailsentemail": "Om detta är en registrerad e-postadress för ditt konto kommer en lösenordsåterställning skickas via e-post.",
-       "passwordreset-emailsentusername": "Om det finns en motsvarande e-postadress för ditt konto kommer en lösenordsåterställning skickas via e-post.",
+       "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",
        "changeemail": "Ändra eller ta bort e-postadress",
        "copyrightwarning2": "Observera att alla bidrag till {{SITENAME}} kan komma att redigeras, ändras, eller tas bort av andra deltagare. Om du inte vill se din text förändrad efter andras gottfinnade skall du inte skriva in någon text här.<br />\nDu lovar oss också att du skrev texten själv, eller kopierade från kulturellt allmängods som inte skyddas av upphovsrätt, eller liknande källor - se $1 för detaljer.\n'''LÄGG INTE UT UPPHOVSRÄTTSSKYDDAT MATERIAL HÄR UTAN TILLÅTELSE!'''",
        "editpage-cannot-use-custom-model": "Innehållsmodellen för denna sida kan inte ändras.",
        "longpageerror": "'''FEL: Texten som du försöker spara är {{PLURAL:$1|en kilobyte|$1 kilobyte}}, vilket är mer än det maximalt tillåtna {{PLURAL:$2|en kilobyte|$2 kilobyte}}.'''\nDen kan inte sparas.",
-       "readonlywarning": "<strong>VARNING: Databasen är tillfälligt låst för underhåll. Du kommer inte att kunna spara dina ändringar just nu.\nDet kan vara klokt att kopiera texten till ett textdokument som sparas på din dator tills vidare.</strong>\n\nSystemadministratören som låste databasen gav följande förklaring: $1",
+       "readonlywarning": "<strong>VARNING: Databasen är tillfälligt låst för underhåll. Du kommer inte att kunna spara dina ändringar just nu.</strong>\nDet kan vara klokt att kopiera texten till ett textdokument som sparas på din dator tills vidare.\n\nSystemadministratören som låste databasen gav följande förklaring: $1",
        "protectedpagewarning": "'''Varning: Den här sidan har låsts så att bara användare med administratörsrättigheter kan redigera den.'''\nDen senaste loggposten tillhandahålls nedan som referens:",
        "semiprotectedpagewarning": "'''Observera:''' Denna sida har låsts så att endast registrerade användare kan redigera den.\nDen senaste loggposten tillhandahålls nedan som referens:",
        "cascadeprotectedwarning": "'''Varning:''' Den här sidan har låsts så att bara användare med administratörsrättigheter kan redigera den, eftersom den är inkluderad på följande {{PLURAL:$1|sida|sidor}} som skyddats med kaskaderande skrivskydd:",
        "permissionserrors": "Behörighetsfel",
        "permissionserrorstext": "Du har inte behörighet att göra det du försöker göra, av följande {{PLURAL:$1|anledning|anledningar}}:",
        "permissionserrorstext-withaction": "Du har inte behörighet att $2, av följande {{PLURAL:$1|anledning|anledningar}}:",
-       "contentmodelediterror": "Du kan inte redigera denna sidversion eftersom dess innehållsmodell är <code>$1</code> som skiljer sig från sidans aktuella innehållsmodell <code>$2</code>.",
+       "contentmodelediterror": "Du kan inte redigera den här sidversionen eftersom dess innehållsmodell är <code>$1</code> som skiljer sig från sidans aktuella innehållsmodell <code>$2</code>.",
        "recreate-moveddeleted-warn": "'''Varning: Du återskapar en sida som tidigare raderats.'''\n\nDu bör överväga om det är lämpligt att fortsätta redigera den här sidan.\nRaderings- och sidflyttningsloggen för den här sidan visas här som hjälp:",
        "moveddeleted-notice": "Den här sidan har raderats.\nRaderings- och sidflyttningsloggen för sidan visas nedan som referens.",
        "moveddeleted-notice-recent": "Tyvärr, denna sida raderades nyligen (inom de senaste 24 timmarna).\nLoggen för radering och flyttning av sidan visas nedan som referens.",
        "userrights": "Hantering av användarrättigheter",
        "userrights-lookup-user": "Hantera användargrupper",
        "userrights-user-editname": "Skriv in ett användarnamn:",
-       "editusergroup": "Ändra användargrupper",
+       "editusergroup": "Ändra {{GENDER:$1|användargrupper}}",
        "editinguser": "Ändrar rättigheter för {{GENDER:$1|användaren}} <strong>[[User:$1|$1]]</strong> $2",
        "userrights-editusergroup": "Ändra användargrupper",
-       "saveusergroups": "Spara användargrupper",
+       "saveusergroups": "Spara {{GENDER:$1|användargrupper}}",
        "userrights-groupsmember": "Medlem i:",
        "userrights-groupsmember-auto": "Implicit medlem av:",
        "userrights-groups-help": "Du kan ändra vilka grupper denna användare är medlem i.\n* En ikryssad ruta betyder användaren är medlem i den gruppen.\n* En okryssad ruta betyder att användaren inte är medlem i den gruppen.\n* En asterisk (*) markerar att du inte kan ta bort gruppen när du har lagt till den, eller vice versa.",
        "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-minoredit": "Markera redigeringar som mindre",
        "right-move": "Flytta sidor",
        "right-move-subpages": "Flytta sidor med deras undersidor",
        "right-blockemail": "Blockera användare från att skicka e-post",
        "right-hideuser": "Blockera ett användarnamn och dölj det från allmänheten",
        "right-ipblock-exempt": "Kan redigera från blockerade IP-adresser",
-       "right-proxyunbannable": "Kan redigera från blockerade proxyer",
        "right-unblockself": "Avblockera sig själv",
        "right-protect": "Ändra skyddsnivåer och redigera kaskadskyddade sidor",
        "right-editprotected": "Redigera skyddade sidor som \"{{int:protect-level-sysop}}\"",
        "right-managechangetags": "Skapa och radera [[Special:Tags|taggar]] från databasen",
        "right-applychangetags": "Tillämpa [[Special:Tags|taggar]] tillsammans med ens ändringar",
        "right-changetags": "Lägg till och ta bort godtyckliga [[Special:Tags|märken]] på individuella sidversioner och loggposter.",
+       "grant-generic": "Rättighetsgrupp \"$1\"",
+       "grant-group-page-interaction": "Interagera med sidor",
+       "grant-group-file-interaction": "Interagera med media",
+       "grant-group-watchlist-interaction": "Interagera med din bevakningslista",
+       "grant-group-email": "Skicka e-post",
+       "grant-group-high-volume": "Utför hög volymaktivitet",
+       "grant-group-customization": "Anpassning och inställningar",
+       "grant-group-administration": "Utför administrativa åtgärder",
+       "grant-group-other": "Diverseaktivitet",
+       "grant-blockusers": "Blockera och avblockera användare",
+       "grant-createaccount": "Skapa konton",
+       "grant-createeditmovepage": "Skapa, redigera och flytta sidor",
+       "grant-delete": "Radera sidor, revideringar och loggposter",
+       "grant-editinterface": "Redigera MediaWiki-namnrymden och CSS/Javascript för användaren",
+       "grant-editmycssjs": "Redigera din CSS/JavaScript för användare",
+       "grant-editmyoptions": "Redigera dina användarinställningar",
+       "grant-editmywatchlist": "Redigera din bevakningslista",
+       "grant-editpage": "Redigera befintliga sidor",
+       "grant-editprotected": "Redigera skyddade sidor",
+       "grant-highvolume": "Högvolymsredigering",
+       "grant-oversight": "Dölj användare och undertryck revideringar",
+       "grant-patrol": "Patrullera ändringar på sidor",
+       "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",
+       "grant-uploadeditmovefile": "Ladda upp, byt och flytta filer",
+       "grant-uploadfile": "Ladda upp nya filer",
+       "grant-basic": "Grundläggande rättigheter",
+       "grant-viewdeleted": "Visa raderade filer och sidor",
+       "grant-viewmywatchlist": "Visa din bevakningslista",
        "newuserlogpage": "Logg över nya användare",
        "newuserlogpagetext": "Detta är en logg över nya användarkonton.",
        "rightslog": "Användarrättighetslogg",
        "upload-form-label-select-file": "Välj fil",
        "upload-form-label-infoform-title": "Detaljer",
        "upload-form-label-infoform-name": "Namn",
+       "upload-form-label-infoform-name-tooltip": "En unik beskrivande titel för filen, som kommer att fungera som ett filnamn. Du kan använda klarspråk med mellanslag. Ta inte med filändelsen.",
        "upload-form-label-infoform-description": "Beskrivning",
+       "upload-form-label-infoform-description-tooltip": "Beskriv kortfattat allt anmärkningsvärt om verket.\nFör ett foto, nämn huvudmotiv, tillfälle eller plats.",
        "upload-form-label-usage-title": "Användning",
        "upload-form-label-usage-filename": "Filnamn",
        "foreign-structured-upload-form-label-own-work": "Detta är mitt eget verk",
        "foreign-structured-upload-form-label-own-work-message-shared": "Jag intygar att jag äger upphovsrätten för denna fil och samtycker till att oåterkalleligen släppa filen på Wikimedia Commons under licensen \n[https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] och jag accepterar [https://wikimediafoundation.org/wiki/Terms_of_Use villkoren för användning].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Om du inte äger upphovsrätten för denna fil eller om du önskar att släppa den under en annan licens bör du överväga att använda [https://commons.wikimedia.org/wiki/Special:UploadWizard uppladdningsguiden på Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Du kanske skulle vilja prova att använda [[Special:Upload|uppladdningssidan på {{SITENAME}}]] om webbplatsens policys tillåter att denna fil laddas upp.",
-       "foreign-structured-upload-form-2-label-intro": "Tack för att du donera en bild för att användas på {{SITENAME}}. Du bör endast fortsätta om det uppfyller flera villkor:",
-       "foreign-structured-upload-form-2-label-ownwork": "Det måste vara helt och hållet <strong>din egen skapelse</strong>, inte bara taget från internet",
-       "foreign-structured-upload-form-2-label-noderiv": "Det får inte innehålla <strong>något verk av någon annan</strong>, eller inspirerats av dem",
-       "foreign-structured-upload-form-2-label-useful": "Det bör vara <strong>pedagogisk och användbar</strong> för att undervisa andra",
-       "foreign-structured-upload-form-2-label-ccbysa": "Det måste vara <strong>OK att publicera för evigt</strong> på internet under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Erkännande-DelaLika 4.0]-licensen",
+       "foreign-structured-upload-form-2-label-intro": "Tack för att du donera en bild för att användas på {{SITENAME}}. Du bör endast fortsätta om den uppfyller flera villkor:",
+       "foreign-structured-upload-form-2-label-ownwork": "Den måste vara helt och hållet <strong>din egen skapelse</strong>, inte bara tagen från Internet",
+       "foreign-structured-upload-form-2-label-noderiv": "Den får inte innehålla <strong>något verk av någon annan</strong>, eller inspirerats av dem",
+       "foreign-structured-upload-form-2-label-useful": "Den bör vara <strong>pedagogisk och användbar</strong> för att undervisa andra",
+       "foreign-structured-upload-form-2-label-ccbysa": "Den måste vara <strong>OK att publicera för evigt</strong> på Internet under [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Erkännande-DelaLika 4.0]-licensen",
        "foreign-structured-upload-form-2-label-alternative": "Om inte alla av ovanstående är stämmer in, kan du fortfarande ha möjlighet att ladda upp denna fil med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons Upload Wizard], så länge den är tillgänglig under en fri licens.",
-       "foreign-structured-upload-form-2-label-termsofuse": "Genom att ladda upp filen, att du äger upphovsrätten till denna fil, samt att du samtycker till att oåterkalleligt släppa denna fil till Wikimedia Commons under Creative Commons Erkännande-DelaLika 4.0-licensen, samt att du samtycker till WIkimeidas  [https://wikimediafoundation.org/wiki/Terms_of_Use villkor för användning].",
+       "foreign-structured-upload-form-2-label-termsofuse": "Genom att ladda upp filen bekräftar du att du äger upphovsrätten till denna fil, samt att du samtycker till att oåterkalleligt släppa denna fil till Wikimedia Commons under Creative Commons Erkännande-DelaLika 4.0-licensen, samt att du samtycker till Wikimedias  [https://wikimediafoundation.org/wiki/Terms_of_Use användarvilkor].",
        "foreign-structured-upload-form-3-label-question-website": "Laddade du ner den här bilden från en webbplats eller hittade du den genom en bildsökning?",
-       "foreign-structured-upload-form-3-label-question-ownwork": "Har du skapa denna bild (tagit bilden, skissat, ritat, etc.) själv?",
-       "foreign-structured-upload-form-3-label-question-noderiv": "Innehåller det, eller är det inspirerade av arbete som ägs av någon annan, som t.ex. en logotyp?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Har du skapat denna bild (tagit bilden, skissat, ritat, etc.) själv?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Innehåller den, eller är den inspirerade av arbete som ägs av någon annan, som t.ex. en logotyp?",
        "foreign-structured-upload-form-3-label-yes": "Ja",
        "foreign-structured-upload-form-3-label-no": "Nej",
-       "foreign-structured-upload-form-3-label-alternative": "Tyvärr har detta verktyg i detta fall inte stöd för att ladda upp denna fil. Du kan fortfarande ladda upp den med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons uppladdningsguide] så länge den är tillgänglig under en fri licens.",
+       "foreign-structured-upload-form-3-label-alternative": "Tyvärr har detta verktyg i detta fall inte stöd för att ladda upp den här filen. Du kan fortfarande ladda upp den med hjälp av [https://commons.wikimedia.org/wiki/Special:UploadWizard Commons uppladdningsguide] så länge den är tillgänglig under en fri licens.",
        "foreign-structured-upload-form-4-label-good": "Med detta verktyg kan du ladda upp pedagogiska bilder som du har skapat och fotografier som du har tagit, som inte innehåller verk som någon annan äger.",
        "foreign-structured-upload-form-4-label-bad": "Du kan inte ladda upp bilder som hittats på en sökmotor eller har laddats ned från andra webbplatser.",
        "backend-fail-stream": "Kunde inte strömma filen $1.",
        "listgrouprights-namespaceprotection-header": "Namnrymdsbegränsningar",
        "listgrouprights-namespaceprotection-namespace": "Namnrymd",
        "listgrouprights-namespaceprotection-restrictedto": "Rättighet(er) som låter användare redigera",
+       "listgrants-summary": "Följande är en lista över OAuth-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": "Spårningskategorier",
        "trackingcategories-summary": "Denna sida listar spårningskategorier som automatiskt befolkas av MediaWiki-mjukvaran. Deras namn kan ändras genom att ändra det relevanta systemmeddelandena i {{ns:8}}-namnrymden.",
        "trackingcategories-msg": "Spårningskategori",
        "wlshowhideanons": "anonyma användare",
        "wlshowhidepatr": "patrullerade redigeringar",
        "wlshowhidemine": "mina redigeringar",
+       "wlshowhidecategorization": "kategorisering av sidor",
        "watchlist-options": "Alternativ för bevakningslistan",
        "watching": "Bevakar...",
        "unwatching": "Avbevakar...",
        "export-download": "Ladda ner som fil",
        "export-templates": "Inkludera mallar",
        "export-pagelinks": "Inkludera länkade sidor till ett djup på:",
+       "export-manual": "Lägg till sidor manuellt:",
        "allmessages": "Systemmeddelanden",
        "allmessagesname": "Namn",
        "allmessagesdefault": "Standardtext",
        "javascripttest-pagetext-frameworks": "Välj en av följande testmiljöer: $1",
        "javascripttest-pagetext-skins": "Välj ett utseende att köra tester med:",
        "javascripttest-qunit-intro": "Se [$1 testningsdokumentationen] på mediawiki.org.",
-       "tooltip-pt-userpage": "Din användarsida",
+       "tooltip-pt-userpage": "{{GENDER:|Din användarsida}}",
        "tooltip-pt-anonuserpage": "Användarsida för ip-numret du redigerar från",
-       "tooltip-pt-mytalk": "Din diskussionssida",
+       "tooltip-pt-mytalk": "{{GENDER:|Din}} diskussionssida",
        "tooltip-pt-anontalk": "Diskussion om redigeringar från det här ip-numret",
-       "tooltip-pt-preferences": "Dina inställningar",
+       "tooltip-pt-preferences": "{{GENDER:|Dina}} inställningar",
        "tooltip-pt-watchlist": "Listan över sidor du bevakar för ändringar",
-       "tooltip-pt-mycontris": "Lista över dina bidrag",
+       "tooltip-pt-mycontris": "Lista över {{GENDER:|dina}} bidrag",
        "tooltip-pt-anoncontribs": "En lista över redigeringar från denna IP-adress",
        "tooltip-pt-login": "Du uppmuntras att logga in, men det är inget krav",
        "tooltip-pt-logout": "Logga ut",
        "tooltip-t-recentchangeslinked": "Visa senaste ändringarna av sidor som den här sidan länkar till",
        "tooltip-feed-rss": "RSS-flöde för den här sidan",
        "tooltip-feed-atom": "Atom-flöde för den här sidan",
-       "tooltip-t-contributions": "En lista över bidrag från den här användaren",
-       "tooltip-t-emailuser": "Skicka e-post till den här användaren",
+       "tooltip-t-contributions": "En lista över bidrag från {{GENDER:$1|den här användaren}}",
+       "tooltip-t-emailuser": "Skicka e-post till {{GENDER:$1|den här användaren}}",
        "tooltip-t-info": "Mer information om denna sida",
        "tooltip-t-upload": "Ladda upp filer",
        "tooltip-t-specialpages": "Lista över alla specialsidor",
        "pageinfo-category-files": "Antal filer",
        "markaspatrolleddiff": "Märk som patrullerad",
        "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.",
        "rcpatroldisabled": "Patrullering av Senaste ändringar är avstängd.",
        "newimages-legend": "Filter",
        "newimages-label": "Filnamn (eller en del av det):",
        "newimages-showbots": "Visa uppladdningar av botar",
+       "newimages-hidepatrolled": "Dölj patrullerade uppladdningar",
        "noimages": "Ingenting att se.",
        "ilsubmit": "Sök",
        "bydate": "efter datum",
        "scarytranscludefailed-httpstatus": "[Hämtning av mall för $1 misslyckades: HTTP $2]",
        "scarytranscludetoolong": "[För lång URL]",
        "deletedwhileediting": "'''Varning''': Denna sida raderades efter att du började redigera!",
-       "confirmrecreate": "Användaren [[User:$1|$1]] ([[User talk:$1|diskussion]]) raderade den här sidan efter att du började redigera den med motiveringen:\n: ''$2''\nBekräfta att du verkligen vill återskapa sidan.",
-       "confirmrecreate-noreason": "Användare [[User:$1|$1]] ([[User talk:$1|diskussion]]) raderade den här sidan efter att du började redigera. Bekräfta att du verkligen vill återskapa sidan.",
+       "confirmrecreate": "Användaren [[User:$1|$1]] ([[User talk:$1|diskussion]]) raderade den här sidan efter att du började redigera den med motiveringen:\n: <em>$2</em>\nBekräfta att du verkligen vill återskapa sidan.",
+       "confirmrecreate-noreason": "Användare [[User:$1|$1]] ([[User talk:$1|diskussion]]) {{GENDER:$1|raderade}} den här sidan efter att du började redigera. Bekräfta att du verkligen vill återskapa sidan.",
        "recreate": "Återskapa",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Rensa denna sidas cache?",
        "watchlisttools-edit": "Visa och redigera bevakningslistan",
        "watchlisttools-raw": "Redigera bevakningslistan i råformat",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskussion]])",
+       "timezone-local": "Lokal",
        "duplicate-defaultsort": "'''Varning:''' Standardsorteringsnyckeln \"$2\" tar över från den tidigare standardsorteringsnyckeln \"$1\".",
        "duplicate-displaytitle": "<strong>Varning:</strong> Visningstiteln \"$2\" skriver över den tidigare visningstiteln \"$1\".",
        "invalid-indicator-name": "<p>Fel:</strong> Sidstatus-indikatorernas <code>namn</code>-attributet får inte vara tomt.",
        "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-create-heading": "Skapa en ny tag",
        "tags-create-explanation": "Som standard, kommer nyskapade taggar att bli tillgängliga för användning av användare och botar.",
        "tags-create-tag-name": "Taggnamn:",
        "tags-deactivate-not-allowed": "Det är inte möjligt att inaktivera taggen \"$1\".",
        "tags-deactivate-submit": "Inaktivera",
        "tags-apply-no-permission": "Du har inte behörighet att tillämpa taggar på dina ändringar",
+       "tags-apply-blocked": "Du kan inte använda(?) ändringsmärken när 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",
        "tags-update-no-permission": "Du har inte behörighet att lägga till eller ta bort taggar från individuella sidversioner eller loggposter.",
+       "tags-update-blocked": "Du kan inte lägga till eller ta bort ändringsmärken när du är blockerad.",
        "tags-update-add-not-allowed-one": "Märket \"$1\" kan inte läggas till manuellt.",
        "tags-update-add-not-allowed-multi": "Följande {{PLURAL:$2|märke|märken}} kan inte läggas till manuellt: $1",
        "tags-update-remove-not-allowed-one": "Märket \"$1\" får inte tas bort.",
        "pagelang-language": "Språk",
        "pagelang-use-default": "Använd standardspråk",
        "pagelang-select-lang": "Välj språk",
+       "pagelang-submit": "Skicka",
        "right-pagelang": "Ändra sidans språk",
        "action-pagelang": "ändra sidspråket",
        "log-name-pagelang": "Språkändringslogg",
        "mediastatistics": "Mediastatistik",
        "mediastatistics-summary": "Statistik om uppladdade filtyper. Detta inkluderar bara den senaste versionen av en fil. Äldre eller raderade filversioner exkluderas.",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 byte}} ($2; $3%)",
+       "mediastatistics-bytespertype": "Total filstorlek för detta avsnitt: {{PLURAL:$1|$1 byte}} ($2; $3%).",
+       "mediastatistics-allbytes": "Total filstorlek för alla filer: {{PLURAL:$1|$1 byte}} ($2).",
        "mediastatistics-table-mimetype": "MIME-typ",
        "mediastatistics-table-extensions": "Möjliga tillägg",
        "mediastatistics-table-count": "Antal filer",
        "mediastatistics-header-text": "Text",
        "mediastatistics-header-executable": "Körbara filer",
        "mediastatistics-header-archive": "Komprimerade format",
+       "mediastatistics-header-total": "Alla filer",
        "json-warn-trailing-comma": "$1 avslutande {{PLURAL:$1|kommatecken}} togs bort från JSON",
        "json-error-unknown": "Det var ett problem med JSON. Fel: $1",
        "json-error-depth": "Listans maximala djup har överskridits",
        "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."
+       "api-error-blacklisted": "Välj en annan beskrivande titel.",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "cookiebaserade sessioner",
+       "sessionprovider-nocookies": "Cookies kan vara inaktiverade. Se till att du har cookies aktiverat och försök igen.",
+       "randomrootpage": "Slumprotsida"
 }
index 59f6b3f..da74677 100644 (file)
@@ -43,7 +43,8 @@
                        "Shrikarsan",
                        "Matma Rex",
                        "Anj.balaji",
-                       "Dineshkumar Ponnusamy"
+                       "Dineshkumar Ponnusamy",
+                       "Sharanrajindia"
                ]
        },
        "tog-underline": "இணைப்புகளுக்கு அடிக்கோடிடு",
        "log-name-pagelang": "மொழி பதிவை மாற்றுக",
        "log-description-pagelang": "இது பக்க மொழி மாற்றச் செயல்பாட்டின் பதிவு.",
        "logentry-pagelang-pagelang": "$3 க்கான பக்க மொழியை $4 இலிருந்து $5 க்கு $1 {{GENDER:$2|மாற்றினார்}}.",
+       "mediastatistics-bytespertype": "இந்த பிரிவக்கான மொத்த கோப்பளவு: $1 எண்ணுண்மி",
+       "mediastatistics-allbytes": "அனைத்து கோப்புகளின் மொத்த கோப்பளவு: $1 எண்ணுண்மி",
        "mediastatistics-table-count": "கோப்புகளின் எண்ணிக்கை",
        "mediastatistics-table-totalbytes": "மொத்த அளவு",
        "mediastatistics-header-unknown": "அறியப்படாதது",
index 1f88168..a7055be 100644 (file)
        "right-blockemail": "ఈమెయిలు పంపకుండా సభ్యుని నిరోధించు",
        "right-hideuser": "ప్రజలకు కనబడకుండా చేసి, సభ్యనామాన్ని నిరోధించు",
        "right-ipblock-exempt": "ఐపీ నిరోధాలు, ఆటో నిరోధాలు, శ్రేణి నిరోధాలను తప్పించు",
-       "right-proxyunbannable": "ప్రాక్సీల ఆటోమాటిక్ నిరోధాన్ని తప్పించు",
        "right-unblockself": "స్వీయ అనిరోధం",
        "right-protect": "సంరక్షణ స్థాయిలను మార్చు, కాస్కేడ్-రక్షిత పేజీలలో దిద్దుబాటు చెయ్యి",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" గా సంక్షించబడిన పేజీలను సరిదిద్దు",
        "minimum-size": "కనీస పరిమాణం",
        "maximum-size": "గరిష్ఠ పరిమాణం",
        "pagesize": "(బైట్లు)",
-       "restriction-edit": "మారà±\8dà°\9aà±\81",
-       "restriction-move": "తరలిà°\82à°\9aà±\81",
+       "restriction-edit": "మారà±\8dà°\9aà°¡à°\82",
+       "restriction-move": "తరలిà°\82à°\9aà°¡à°\82",
        "restriction-create": "సృష్టించు",
        "restriction-upload": "ఎక్కించు",
        "restriction-level-sysop": "పూర్తి సంరక్షణ",
        "pageinfo-header-edits": "మార్పుల చరిత్ర",
        "pageinfo-header-restrictions": "పేజీ సంరక్షణ",
        "pageinfo-header-properties": "పేజీ లక్షణాలు",
-       "pageinfo-display-title": "à°¶à±\80à°°à±\8dà°·à°¿à°\95 à°\9aà±\82పిà°\82à°\9aà±\81",
+       "pageinfo-display-title": "à°\9aà±\82పిà°\82à°\9aà±\87 à°¶à±\80à°°à±\8dà°·à°¿à°\95",
        "pageinfo-length": "పేజీ నిడివి (బైట్లలో)",
        "pageinfo-article-id": "పేజీ ఐడీ",
        "pageinfo-language": "పేజీ విషయపు భాష",
        "pageinfo-content-model": "పేజీ కంటెంటు మోడల్",
-       "pageinfo-robot-policy": "à°°à±\8bà°¬à±\8bà°\9fà±\8dà°²à°\9aà±\87 à°\87à°\82à°¡à±\86à°\95à±\8dసవà±\81à°¤à±\8bà°\82ది",
+       "pageinfo-robot-policy": "à°°à±\8bà°¬à±\8bà°\9fà±\8dà°²à°\9aà±\87 à°\87à°\82à°¡à±\86à°\95à±\8dసిà°\82à°\97à±\81",
        "pageinfo-robot-index": "అనుమతించబడింది",
        "pageinfo-robot-noindex": "అనుమతించబడలేదు",
        "pageinfo-watchers": "పేజీ గమనింపుదారుల సంఖ్య",
        "pageinfo-edits": "మొత్తం మార్పుల సంఖ్య",
        "pageinfo-authors": "ప్రత్యేక కర్తల మొత్తం సంఖ్య",
        "pageinfo-recent-edits": "ఇటీవలి మార్పుల సంఖ్య (గత $1 లోపు)",
-       "pageinfo-recent-authors": "à°ªà±\8dà°°à°¤à±\8dà°¯à±\87à°\95 à°\95à°°à±\8dతల à°\87à°\9fà±\80వలి సంఖ్య",
-       "pageinfo-magic-words": "à°\9aమతà±\8dà°\95ార {{PLURAL:$1|పదం|పదాలు}} ($1)",
+       "pageinfo-recent-authors": "à°\87à°\9fà±\80వలి à°°à°\9aయితల సంఖ్య",
+       "pageinfo-magic-words": "మాయా {{PLURAL:$1|పదం|పదాలు}} ($1)",
        "pageinfo-hidden-categories": "దాచిన {{PLURAL:$1|వర్గం|వర్గాలు}} ($1)",
        "pageinfo-templates": "ట్రాన్స్‍క్లూడు చేసిన {{PLURAL:$1|మూస|మూసలు}} ($1)",
        "pageinfo-transclusions": "($1) తో {{PLURAL:$1|పేజీ|పేజీలు}} ట్రాన్స్‍క్లూడు చెయ్యబడ్డాయి",
        "watchlisttools-edit": "వీక్షణ జాబితాను చూడండి లేదా మార్చండి",
        "watchlisttools-raw": "ముడి వీక్షణ జాబితాలో మార్పులు చెయ్యి",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|చర్చ]])",
+       "timezone-local": "స్థానిక",
        "duplicate-defaultsort": "హెచ్చరిక: డిఫాల్టు పేర్చు కీ \"$2\", గత డిఫాల్టు పేర్చు కీ \"$1\" ని అతిక్రమిస్తుంది.",
        "version": "సంచిక",
        "version-extensions": "స్థాపించిన పొడగింతలు",
        "pagelang-language": "భాష",
        "pagelang-use-default": "అప్రమేయ భాషను వాడు",
        "pagelang-select-lang": "భాషను ఎంచుకోండి",
+       "pagelang-submit": "పంపించు",
        "right-pagelang": "పేజీ భాషను మార్చడం",
        "action-pagelang": "పేజీ భాషను మార్చే",
        "log-name-pagelang": "భాష మార్పుల చిట్టా",
        "mediastatistics-header-video": "వీడియోలు",
        "mediastatistics-header-office": "కార్యాలయం",
        "mediastatistics-header-text": "పాఠ్య",
+       "mediastatistics-header-total": "అన్ని ఫైళ్ళు",
        "json-error-state-mismatch": "చెల్లని లేదా సరికాని JSON",
        "json-error-syntax": "వ్యాకరణ దోషం",
        "headline-anchor-title": "ఈ విభాగానికి లంకె",
index 88b73fd..861a294 100644 (file)
        "nstab-template": "Шаблон",
        "nstab-help": "Кӯмак",
        "nstab-category": "Гурӯҳ",
+       "mainpage-nstab": "Саҳифаи аслӣ",
        "nosuchaction": "Чунин амале вуҷуд надорад",
        "nosuchactiontext": "Амали дар URL мушаххасшуда номӯътабар аст.\nШумо шояд хато пайванди URL-ро ворид намудед, ё пайванди нодурустро пайгирӣ кардед.\nШояд ин як хатогие дар нармафзоре бошад, ки аз тарафи {{SITENAME}} истифода мешавад.",
        "nosuchspecialpage": "Чунин саҳифаи вижа вуҷуд надорад",
        "createaccountreason": "Сабаб:",
        "createacct-reason": "Сабаб",
        "createacct-reason-ph": "Барои чӣ ҳисоби дигареро эҷод карда истодаед",
-       "createacct-captcha": "Бозрасии амниятӣ",
-       "createacct-imgcaptcha-ph": "Матни болоро ворид кунед",
        "createacct-submit": "Ҳисоби худро созед",
        "createacct-another-submit": "Ҳисоби дигаре созед",
        "createacct-benefit-heading": "{{SITENAME}} тавассути одамони мисли шумо сохта шудааст.",
        "number_of_watching_users_pageview": "[$1 пайгирикунанда {{PLURAL:$1|корбар|корбарон}}]",
        "rc_categories": "Маҳдудият ба гурӯҳҳо (бо аломати \"|\" ҷудо кунед)",
        "rc_categories_any": "Ҳар кадом",
+       "rc-change-size-new": "$1 {{PLURAL:$1|байт}} пас аз тағйир",
        "newsectionsummary": "/* $1 */ бахши ҷадид",
        "rc-enhanced-expand": "Намоиши ҷузъиёт",
        "rc-enhanced-hide": "Пинҳони ҷузъиёт",
        "suppress": "Назорат",
        "booksources": "Манбаҳои китобҳо",
        "booksources-search-legend": "Ҷустуҷӯи сарчашмаҳои китоб",
+       "booksources-search": "Ҷустуҷӯ",
        "booksources-text": "Дер зер феҳристи пайвандҳо ба сомонаҳое, ки китобҳои нав ва кӯҳна мефурӯшанд, оварда шудааст. Мумкин аст, иттилооти бештарро дар бораи китобҳои ҷустуҷӯ кардаатон дошта бошанд:",
        "specialloguserlabel": "Иҷрокунанда:",
        "speciallogtitlelabel": "Ҳадаф (унвон ё корбар):",
        "wlheader-showupdated": "Саҳифаҳое, ки пас аз охирин сар заданатон ба онҳо тағйир кардаанд '''пурранг''' нишон дода шудаанд",
        "wlnote": "Дар зер {{PLURAL:$1|охирин тағйир|'''$1''' охирин тағйирот}} дар $2 соати охир {{PLURAL:омадааст|омадаанд}}.",
        "wlshowlast": "Намоиши охирин $1 соат $2 рӯзҳо",
+       "watchlistall2": "ҳама",
        "watchlist-options": "Ихтиёроти феҳристи пайгириҳо",
        "watching": "Пайгири...",
        "unwatching": "Тавқифи пайгири...",
        "contributions": "Ҳиссагузориҳои {{GENDER:$1|корбар}}",
        "contributions-title": "Ҳиссагузориҳои корбар барои $1",
        "mycontris": "Ҳиссагузориҳо",
+       "anoncontribs": "Саҳмгузориҳо",
        "contribsub2": "Барои {{GENDER:$3|$1}} ($2)",
        "nocontribs": "Ҳеҷ тағйире бо ин мушаххасот пайдо нашуд.",
        "uctop": "(кунунӣ)",
        "movesubpage": "{{PLURAL:$1|Зерсаҳифа|Зерсаҳифаҳо}}",
        "movereason": "Сабаб:",
        "revertmove": "вогардонӣ",
-       "delete_and_move": "Ҳазф ва кӯчонидан",
        "delete_and_move_text": "==Ниёз ба ҳазф==\n\nМақолаи мақсад \"[[:$1]]\" вуҷуд дорад. Оё мехоҳед онро ҳазф кунед то интиқол мумкин шавад?",
        "delete_and_move_confirm": "Бале, саҳифа ҳазф шавад",
        "delete_and_move_reason": "Ҳазф шуд барои мумкин шудани кӯчонидан",
        "tooltip-ca-nstab-main": "Дидани саҳифаи мӯҳтавиёт",
        "tooltip-ca-nstab-user": "Намоиши саҳифаи корбар",
        "tooltip-ca-nstab-media": "Дидани саҳифаи расона",
-       "tooltip-ca-nstab-special": "Ð\98н Ñ\81аҳиÑ\84аи Ð¼Ð°Ñ\85Ñ\81Ñ\83Ñ\81 Ð¼ÐµÐ±Ð¾Ñ\88ад, Ð¨Ñ\83мо Ð¾Ð½Ñ\80о Ð²Ð¸Ñ\80оиÑ\88 ÐºÐ°Ñ\80да Ð½Ð°Ð¼ÐµÑ\82авонед",
+       "tooltip-ca-nstab-special": "Ð\98н Ñ\82аÑ\80Ò·Ñ\83ма Ñ\88оÑ\8fд Ð½Ð¸Ñ\91з Ð±Ð° Ð±Ð°Ñ\80ӯзÑ\88ави Ð´Ð¾Ñ\88Ñ\82а Ð±Ð¾Ñ\88ад.",
        "tooltip-ca-nstab-project": "Намоиши саҳифаи лоиҳа",
        "tooltip-ca-nstab-image": "Дидани саҳифаи парванда",
        "tooltip-ca-nstab-mediawiki": "Дидани пайғоми системавӣ",
        "spambot_username": "Спамтозакуни МедиаВики",
        "spam_reverting": "Вогардони ба охирин нусхае, ки пайванде ба $1 надорад",
        "spam_blanking": "Ҳамаи нусхаҳои пайвандҳо $1 доштан, дар ҳоли холӣ кардан",
+       "pageinfo-toolboxlink": "Иттилооти саҳифа",
        "pageinfo-contentpage-yes": "Бале",
        "pageinfo-protect-cascading-yes": "Бале",
        "markaspatrolleddiff": "Ба унвони баррасишуда аломат бизан",
        "feedback-message": "Пайём:",
        "feedback-subject": "Мавзӯъ:",
        "feedback-submit": "Ирсол",
+       "searchsuggest-search": "Ҷустуҷӯ",
        "expandtemplates": "Бастдодани шаблонҳо",
        "expand_templates_intro": "Ин саҳифаи вижа матнеро дарёфт карда ва тамоми шаблонҳои ба кор рафта дар онро ба таври бозгаште баст медиҳад. Ҳамчунин тобеҳои таҷзеҳ\n<nowiki>{{</nowiki>#language:...}}, ва мутағйирҳое чун\n<nowiki>{{</nowiki>CURRENTDAY}}&mdash;ро ҳам баст медиҳад – дар воқеъ тақрибан ҳар чиро ки дохили ду акулот бошад.\nИн кор бо садо задани марҳилаи таҷзеҳи марбут дар худи МедиаВики сурат мегирад.",
        "expand_templates_title": "Унвони мавзӯъ, барои {{FULLPAGENAME}} ва ғайра.:",
index bf26a92..cbcbb49 100644 (file)
@@ -29,6 +29,7 @@
        "tog-hideminor": "ซ่อนการแก้ไขเล็กน้อยในหน้าปรับปรุงล่าสุด",
        "tog-hidepatrolled": "ซ่อนการแก้ไขที่ตรวจสอบแล้วในหน้าปรับปรุงล่าสุด",
        "tog-newpageshidepatrolled": "ซ่อนหน้าที่ตรวจสอบแล้วในรายการหน้าใหม่",
+       "tog-hidecategorization": "ซ่อนการจัดหมวดหมู่หน้า",
        "tog-extendwatchlist": "ขยายรายการเฝ้าดูให้แสดงการเปลี่ยนแปลงทั้งหมด ไม่เพียงการเปลี่ยนแปลงล่าสุด",
        "tog-usenewrc": "จัดกลุ่มการเปลี่ยนแปลงแบ่งตามหน้าในรายการปรับปรุงล่าสุดและรายการเฝ้าดู",
        "tog-numberheadings": "กำหนดเลขหัวเรื่องอัตโนมัติ",
@@ -59,6 +60,7 @@
        "tog-watchlistreloadautomatically": "โหลดรายการเฝ้าดูใหม่อัตโนมัติเมื่อใดที่มีการเปลี่ยนตัวกรอง (ต้องการจาวาสคริปต์)",
        "tog-watchlisthideanons": "ซ่อนการแก้ไขโดยผู้ใช้นิรนามจากรายการเฝ้าดู",
        "tog-watchlisthidepatrolled": "ซ่อนการแก้ไขที่ตรวจสอบแล้วจากรายการเฝ้าดู",
+       "tog-watchlisthidecategorization": "ซ่อนการจัดหมวดหมู่หน้า",
        "tog-ccmeonemails": "ส่งสำเนาอีเมลที่ฉันส่งหาผู้อื่นให้ฉัน",
        "tog-diffonly": "ไม่แสดงเนื้อหาหน้าใต้ผลต่าง",
        "tog-showhiddencats": "แสดงหมวดหมู่ที่ซ่อนอยู่",
        "no-null-revision": "ไม่สามารถสร้างรุ่นว่างใหม่ของหน้า \"$1\"",
        "badtitle": "ใช้ชื่อเรื่องนี้ไม่ได้",
        "badtitletext": "ชื่อหน้าที่ขอไม่ถูกต้อง เป็นชื่อว่าง หรือชื่อข้ามภาษาหรือข้ามวิกิที่เชื่อมโยงไม่ถูกต้อง\nอาจมีอักขระที่ไม่สามารถใช้ในชื่อเรื่องได้",
+       "title-invalid-empty": "ชื่อเรื่องหน้าที่ขอว่างหรือมีเฉพาะชื่อเนมสเปซ",
+       "title-invalid-utf8": "ชื่อเรื่องหน้าที่ขอมีลำดับ UTF-8 ที่ไม่สมเหตุสมผล",
+       "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 ไบต์ในการเข้ารหัส UTF-8",
+       "title-invalid-leading-colon": "ชื่อเรื่องหน้าที่ขอขึ้นต้นด้วยโคลอนไม่สมเหตุสมผล",
        "perfcached": "ข้อมูลต่อไปนี้ถูกเก็บในแคชและอาจล้าสมัย มีผลการค้นหาสูงสุด $1 รายการในแคช",
        "perfcachedts": "ข้อมูลต่อไปนี้ถูกเก็บในแคชและถูกปรับล่าสุดเมื่อ $1 มีผลลัพธ์สูงสุด $4 รายการในแคชได้",
        "querypage-no-updates": "ขณะนี้ปิดใช้งานการปรับหน้านี้ \nข้อมูลในที่นี้จะไม่รีเฟรชเป็นปัจจุบัน",
        "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 วัน)",
        "userlogin-remembermypassword": "ให้ฉันอยู่ในระบบต่อ",
        "userlogin-signwithsecure": "ใช้การเชื่อมต่อที่ปลอดภัย",
+       "cannotloginnow-title": "ไม่สามารถล็อกเอาต์ได้ขณะนี้",
+       "cannotloginnow-text": "ไม่สามารถล็อกเอาต์ได้เมื่อกำลังใช้ $1",
        "yourdomainname": "โดเมนของคุณ:",
        "password-change-forbidden": "คุณไม่สามารถเปลี่ยนรหัสผ่านบนวิกินี้",
        "externaldberror": "มีข้อผิดพลาดของฐานข้อมูลการพิสูจน์ตัวจริง หรือคุณไม่ได้รับอนุญาตให้ปรับบัญชีภายนอกของคุณ",
        "resetpass_submit": "ตั้งรหัสผ่านและล็อกอิน",
        "changepassword-success": "เปลี่ยนรหัสผ่านของคุณสำเร็จ!",
        "changepassword-throttled": "ล่าสุดคุณพยายามล็อกอินมากครั้งเกินไป\nกรุณารอ $1 ก่อนลองอีกครั้ง",
+       "botpasswords": "รหัสผ่านบอต",
        "resetpass_forbidden": "ไม่สามารถเปลี่ยนรหัสผ่านได้",
        "resetpass-no-info": "คุณต้องล็อกอินเพื่อเข้าถึงหน้านี้โดยตรง",
        "resetpass-submit-loggedin": "เปลี่ยนรหัสผ่าน",
        "passwordreset-emailtext-ip": "บางคน (ซึ่งอาจเป็นคุณ จากเลขที่อยู่ไอพี $1) ขอตั้งรหัสผ่านของคุณใหม่บน{{SITENAME}} ($4) บัญชีผู้ใช้ดังกล่าวเกี่ยวข้องกับที่อยู่อีเมลนี้:\n\n$2\n\n{{PLURAL:$3|รหัสผ่านชั่วคราวนี้|รหัสผ่านชั่วคราวเหล่านี้}}จะหมดอายุใน $5 วัน\nตอนนี้คุณควรล็อกอินและเลือกรหัสผ่านใหม่ หากบุคคลอื่นขอตั้งรหัสผ่านใหม่นี้ หรือคุณจำรหัสผ่านเดิมของคุณได้แล้ว และคุณไม่ต้องการเปลี่ยนรหัสผ่านอีก คุณอาจละเลยข้อความนี้และใช้รหัสผ่านเดิมของคุณต่อไป",
        "passwordreset-emailtext-user": "ผู้ใช้ $1 บน {{SITENAME}} ขอตั้งรหัสผ่านของคุณใหม่สำหรับ {{SITENAME}} ($4) {{PLURAL:$3||}}บัญชีผู้ใช้ดังกล่าวเกี่ยวข้องกับที่อยู่อีเมลนี้:\n\n$2\n\n{{PLURAL:$3|รหัสผ่านชั่วคราวนี้|รหัสผ่านชั่วคราวเหล่านี้}}จะหมดอายุใน $5 วัน\nตอนนี้คุณควรล็อกอินและเลือกรหัสผ่านใหม่ หากบุคคลอื่นขอตั้งรหัสผ่านใหม่นี้ หรือคุณจำรหัสผ่านเดิมของคุณได้แล้ว และคุณไม่ต้องการเปลี่ยนรหัสผ่านอีก คุณอาจละเลยข้อความนี้และใช้รหัสผ่านเดิมของคุณต่อไป",
        "passwordreset-emailelement": "ชื่อผู้ใช้: \n$1\n\nรหัสผ่านชั่วคราว: \n$2",
-       "passwordreset-emailsentemail": "หาà¸\81à¸\99ีà¹\88à¸\84อà¸\97ีà¹\88อยูà¹\88อีà¹\80มลà¸\97ีà¹\88ลà¸\87à¸\97ะà¹\80à¸\9aียà¸\99สำหรับบัญชีของคุณ เช่นนั้นจะส่งอีเมลตั้งรหัสผ่านใหม่",
-       "passwordreset-emailsentusername": "หาà¸\81à¸\99ีà¹\88à¸\84ือà¸\97ีà¹\88อยูà¹\88อีà¹\80มลà¸\97ีà¹\88ลà¸\87à¸\97ะà¹\80à¸\9aียà¸\99à¹\84วà¹\89à¸\94à¹\89วยà¸\81ัà¸\99 เช่นนั้นจะส่งอีเมลตั้งรหัสผ่านใหม่",
+       "passwordreset-emailsentemail": "หาà¸\81à¸\97ีà¹\88อยูà¹\88อีà¹\80มลà¸\99ีà¹\89สัมà¸\9eัà¸\99à¸\98à¹\8cà¸\81ับบัญชีของคุณ เช่นนั้นจะส่งอีเมลตั้งรหัสผ่านใหม่",
+       "passwordreset-emailsentusername": "หาà¸\81มีà¸\97ีà¹\88อยูà¹\88อีà¹\80มลà¸\97ีà¹\88ลà¸\87à¸\97ะà¹\80à¸\9aียà¸\99à¹\84วà¹\89à¸\94à¹\89วยà¸\81ัà¸\9aà¸\8aืà¹\88อà¸\9cูà¹\89à¹\83à¸\8aà¹\89à¸\99ีà¹\89 เช่นนั้นจะส่งอีเมลตั้งรหัสผ่านใหม่",
        "passwordreset-emailsent-capture": "อีเมลตั้งรหัสผ่านใหม่ถูกส่งไปแล้ว ซึ่งแสดงด้านล่าง",
        "passwordreset-emailerror-capture": "อีเมลตั้งรหัสผ่านใหม่ถูกสร้างขึ้นแล้ว ซึ่งแสดงด้านล่าง แต่ไม่สามารถส่งไปยัง{{GENDER:$2|ผู้ใช้}}: $1",
        "changeemail": "เปลี่ยนหรือลบที่อยู่อีเมล",
        "copyrightwarning": "โปรดระลึกว่างานเขียนทั้งหมดใน {{SITENAME}} ถือว่าเผยแพร่ภายใต้ $2 (ดูรายละเอียดทาง $1)\nหากคุณไม่ต้องการให้งานของคุณถูกแก้ไขและกระจายได้ตามใจ ก็อย่าส่งเข้ามา<br />\nนอกจากนี้ คุณยังสัญญาเราว่าคุณเขียนงานด้วยตนเอง หรือคัดลอกจากสาธารณสมบัติหรือทรัพยากรเสรีที่คล้ายกัน\n<strong>อย่าส่งงานมีลิขสิทธิ์โดยไม่ได้รับอนุญาต!</strong>",
        "copyrightwarning2": "โปรดระลึกว่างานเขียนทั้งหมดใน {{SITENAME}} อาจถูกผู้เขียนอื่นแก้ไข เปลี่ยนแปลงหรือนำออก\nหากคุณไม่ต้องการให้งานของคุณถูกแก้ไข ก็อย่าส่งเข้ามา<br />\nนอกจากนี้ คุณยังสัญญาเราว่าคุณเขียนงานด้วยตนเอง หรือคัดลอกจากสาธารณสมบัติหรือทรัพยากรเสรีที่คล้ายกัน (ดูรายละเอียดที่ $1)\n<strong>อย่าส่งงานมีลิขสิทธิ์โดยไม่ได้รับอนุญาต!</strong>",
        "longpageerror": "<strong>ข้อผิดพลาด: ข้อความที่คุณส่งมีขนาด $1 กิโลไบต์\nซึ่งเกินสูงสุด $2 กิโลไบต์</strong>\nไม่สามารถบันทึกได้",
-       "readonlywarning": "<strong>à¸\84ำà¹\80à¸\95ือà¸\99: à¸\90าà¸\99à¸\82à¹\89อมูลà¸\96ูà¸\81ลà¹\87อà¸\81à¹\80à¸\9eืà¹\88อà¸\9aำรุà¸\87รัà¸\81ษา à¸\84ุà¸\93à¸\88ึà¸\87à¹\84มà¹\88สามารà¸\96à¸\9aัà¸\99à¸\97ึà¸\81à¸\81ารà¹\80à¸\9bลีà¹\88ยà¸\99à¹\81à¸\9bลà¸\87ของคุณได้ในขณะนี้</strong>\nคุณอาจต้องการคัดลอกและวางข้อความของคุณในไฟล์ข้อความ และบันทึกไว้ภายหลัง\n\nผู้ดูแลระบบที่ล็อกฐานข้อมูลให้คำอธิบายดังนี้: $1",
+       "readonlywarning": "<strong>à¸\84ำà¹\80à¸\95ือà¸\99: à¸\90าà¸\99à¸\82à¹\89อมูลà¸\96ูà¸\81ลà¹\87อà¸\81à¹\80à¸\9eืà¹\88อà¸\9aำรุà¸\87รัà¸\81ษา à¸\84ุà¸\93à¸\88ึà¸\87à¹\84มà¹\88สามารà¸\96à¸\9aัà¸\99à¸\97ึà¸\81à¸\81ารà¹\81à¸\81à¹\89à¹\84à¸\82ของคุณได้ในขณะนี้</strong>\nคุณอาจต้องการคัดลอกและวางข้อความของคุณในไฟล์ข้อความ และบันทึกไว้ภายหลัง\n\nผู้ดูแลระบบที่ล็อกฐานข้อมูลให้คำอธิบายดังนี้: $1",
        "protectedpagewarning": "<strong>คำเตือน: หน้านี้ถูกล็อก เพื่อให้เฉพาะผู้ใช้ที่มีสิทธิผู้ดูแลระบบแก้ไขได้เท่านั้น</strong>\nรายการปูมล่าสุดจัดไว้ด้านล่างเพื่อการอ้างอิง:",
        "semiprotectedpagewarning": "<strong>หมายเหตุ:</strong> หน้านี้ถูกล็อก เพื่อให้เฉพาะผู้ใช้ลงทะเบียนสามารถแก้ไขเท่านั้น\nรายการปูมล่าสุดได้จัดไว้ด้านล่างนี้เพื่อการอ้างอิง",
        "cascadeprotectedwarning": "<strong>คำเตือน:</strong> หน้านี้ถูกล็อก และแก้ไขได้เฉพาะผู้ใช้ที่มีสิทธิผู้ดูแลระบบ เนื่องจากหน้านี้รวมอยู่ใน{{PLURAL:$1|หน้า}}ที่ถูกล็อกแบบต่อเรียงต่อไปนี้:",
        "showingresultsinrange": "ด้านล่างแสดงมากสุด {{PLURAL:$1|<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-nonefound": "ไม่มีผลลัพธ์ตรงกับคำค้น",
+       "search-nonefound-thiswiki": "ไม่พบผลลัพธ์ตรงกับคำค้นในเว็บไซต์นี้",
        "powersearch-legend": "ค้นหาขั้นสูง",
        "powersearch-ns": "ค้นหาในเนมสเปซ:",
        "powersearch-togglelabel": "เลือก:",
        "right-blockemail": "บล็อกผู้ใช้มิให้ส่งอีเมล",
        "right-hideuser": "บล็อกชื่อผู้ใช้ ซ่อนไม่ให้สาธารณะเห็น",
        "right-ipblock-exempt": "เลี่ยงการบล็อกเลขที่อยู่ไอพี บล็อกอัตโนมัติ และบล็อกช่วง",
-       "right-proxyunbannable": "เลี่ยงการบล็อกอัตโนมัติของพร็อกซี",
        "right-unblockself": "ปลดบล็อกตนเอง",
        "right-protect": "เปลี่ยนระดับการล็อกและแก้ไขหน้าที่ถูกล็อกแบบต่อเรียง",
        "right-editprotected": "แก้ไขหน้าที่ถูกล็อกในฐานะ \"{{int:protect-level-sysop}}\"",
        "rcshowhidemine": "$1การแก้ไขของฉัน",
        "rcshowhidemine-show": "แสดง",
        "rcshowhidemine-hide": "ซ่อน",
+       "rcshowhidecategorization": "$1การจัดหมวดหมู่หน้า",
        "rcshowhidecategorization-show": "แสดง",
        "rcshowhidecategorization-hide": "ซ่อน",
        "rclinks": "แสดงการปรับปรุงล่าสุด $1 รายการ ในช่วง $2 วันที่ผ่านมา<br />$3",
        "mostrevisions": "หน้าที่มีรุ่นปรับปรุงมากสุด",
        "prefixindex": "หน้าทั้งหมดพร้อมคำขึ้นต้น",
        "prefixindex-namespace": "หน้าทั้งหมดพร้อมคำขึ้นต้น (เนมสเปซ $1)",
+       "prefixindex-submit": "แสดง",
        "prefixindex-strip": "ลบคำขึ้นต้นในรายการออก",
        "shortpages": "หน้าสั้น",
        "longpages": "หน้ายาว",
        "whatlinkshere-hidelinks": "$1 ลิงก์",
        "whatlinkshere-hideimages": "$1ลิงก์ไฟล์",
        "whatlinkshere-filters": "ตัวกรอง",
+       "whatlinkshere-submit": "ไป",
        "autoblockid": "บล็อกอัตโนมัติ #$1",
        "block": "บล็อกผู้ใช้",
        "unblock": "ปลดบล็อกผู้ใช้",
        "tags-actions-header": "ปฏิบัติการ",
        "tags-active-yes": "ใช่",
        "tags-active-no": "ไม่",
+       "tags-source-extension": "นิยามโดยส่วนขยาย",
+       "tags-source-manual": "ใช้ด้วยมือโดยผู้ใช้และบอต",
+       "tags-source-none": "เลิกใช้แล้ว",
        "tags-edit": "แก้ไข",
+       "tags-delete": "ลบ",
+       "tags-activate": "เปิดใช้งาน",
+       "tags-deactivate": "ปิดใช้งาน",
        "tags-hitcount": "$1 การเปลี่ยนแปลง",
        "comparepages": "เปรียบเทียบหน้า",
        "compare-page1": "หน้า 1",
        "htmlform-no": "ไม่",
        "htmlform-yes": "ใช่",
        "htmlform-chosen-placeholder": "เลือกตัวเลือก",
+       "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 ลบหน้า $3",
index 22dd623..ead7f7b 100644 (file)
@@ -81,7 +81,8 @@
                        "Captantrips",
                        "Diyapazon",
                        "Matma Rex",
-                       "HakanIST"
+                       "HakanIST",
+                       "Imabadplayer"
                ]
        },
        "tog-underline": "Bağlantıların altını çiz:",
        "tog-watchlisthidebots": "İzleme listemde bot değişikliklerini gizle",
        "tog-watchlisthideminor": "İzleme listemde küçük değişiklikleri gizle",
        "tog-watchlisthideliu": "İzleme listemde, kayıtlı kullanıcılar tarafından yapılan değişiklikleri gizle",
-       "tog-watchlistreloadautomatically": "Filtre değiştiğinde otomatik izleme (JavaScript gerekir)yeniden",
+       "tog-watchlistreloadautomatically": "Filtre değiştiğinde izleme listesini otomatik yenile (JavaScript gerekir)",
        "tog-watchlisthideanons": "İzleme listemde, anonim kullanıcılar tarafından yapılan değişiklikleri gizle",
        "tog-watchlisthidepatrolled": "İzleme listesinde, devriye görmüş değişiklikleri gizle",
        "tog-watchlisthidecategorization": "Sayfa kategorilendirmesni gizle",
        "policy-url": "Project:İlkeler",
        "portal": "Topluluk portali",
        "portal-url": "Project:Topluluk portali",
-       "privacy": "Gizlilik ilkesi",
-       "privacypage": "Project:Gizlilik ilkesi",
+       "privacy": "Gizlilik politikası",
+       "privacypage": "Project:Gizlilik Politikası",
        "badaccess": "İzin hatası",
        "badaccess-group0": "Bu işlemi yapma yetkiniz yok.",
        "badaccess-groups": "Yapmak istediğiniz işlem, sadece {{PLURAL:$2|şu gruptaki|şu gruplardaki}} kullanıcılar tarafından yapılabilir: $1",
        "virus-scanfailed": "tarama başarısız (kod $1)",
        "virus-unknownscanner": "bilinmeyen antivürüs:",
        "logouttext": "'''Artık oturumunuzu kapattınız.'''\n\nTarayıcınızın önbelleğini temizleyinceye kadar bazı sayfalarda, oturumunuz açıkmış gibi gözükmeye devam edebilir.",
+       "cannotlogoutnow-title": "Şu an oturum kapatılamıyor",
+       "cannotlogoutnow-text": "$1 kullanılırken oturumu kapatmak mümkün değil.",
        "welcomeuser": "Hoş geldin $1!",
        "welcomecreation-msg": "Hesabınız açıldı.\n[[Special:Preferences|{{SITENAME}} tercihlerinizi]] değiştirmeyi unutmayın.",
        "yourname": "Kullanıcı adı:",
        "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",
+       "cannotloginnow-text": "$1 kullanırken giriş yapmak mümkün değil.",
        "yourdomainname": "Alan adınız:",
        "password-change-forbidden": "Bu vikide parolanızı değiştiremezsiniz.",
        "externaldberror": "Ya doğrulama veritabanı hatası var ya da kullanıcı hesabınızı güncellemeye yetkiniz yok.",
        "resetpass_submit": "Şifreyi ayarlayın ve oturum açın",
        "changepassword-success": "Parolanız başarıyla değiştirildi!",
        "changepassword-throttled": "Çok fazla yeni oturum açma girişiminde bulundunuz.\nLütfen tekrar denemeden önce $1 bekleyin.",
+       "botpasswords": "Bot şifreleri",
+       "botpasswords-disabled": "Bot şifreleri devre dışı.",
+       "botpasswords-no-central-id": "Bot şifresini kullanmak için, merkezi bir hesap ile giriş yapmalısınız.",
+       "botpasswords-existing": "Mevcut bot şifreleri",
+       "botpasswords-createnew": "Yeni bir bot şifresi oluştur",
+       "botpasswords-editexisting": "Mevcut bir bot şifresini düzenle",
+       "botpasswords-label-appid": "Bot ismi:",
+       "botpasswords-label-create": "Oluştur",
+       "botpasswords-label-update": "Güncelle",
+       "botpasswords-label-cancel": "İptal",
+       "botpasswords-label-delete": "Sil",
+       "botpasswords-label-resetpassword": "Şifreyi sıfırla",
+       "botpasswords-label-grants-column": "Verilen",
+       "botpasswords-bad-appid": "Bot ismi \"$1\" geçerli değil.",
+       "botpasswords-insert-failed": "Bot adı \"$1\" eklenemedi. Zaten eklenmiş olmalı?",
+       "botpasswords-update-failed": "Bot ismini \"$1\" olarak güncelleme başarısız oldu. Silinmiş olabilir mi?",
+       "botpasswords-created-title": "Bot şifresi oluşturuldu.",
+       "botpasswords-created-body": "Bot şifresi \"$1\" başarıyla oluşturuldu.",
+       "botpasswords-updated-title": "Bot şifresi guncellendi",
+       "botpasswords-updated-body": "Bot şifresi \"$1\" başarıyla güncellendi.",
+       "botpasswords-deleted-title": "Bot şifresi silindi.",
+       "botpasswords-deleted-body": "Bot şifresi $1 silinmiş.",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider kullanılamaz.",
        "resetpass_forbidden": "Parolalar değiştirilememektedir",
        "resetpass-no-info": "Bu sayfaya doğrudan erişmek için oturum açmanız gereklidir.",
        "resetpass-submit-loggedin": "Parolayı değiştir",
        "passwordreset-emailtext-ip": "Birisi, (muhtemelen siz, $1 IP adresinden) {{SITENAME}} ($4) için hesap bilgilerinizin \nhatırlatılmasını istedi. Aşağıdaki kullanıcı {{PLURAL:$3|hesabı|hesapları}} bu e-posta adresiyle ilişkili:\n\n$2\n\n{{PLURAL:$3|Bu geçici şifre|Bu geçici şifreler}} {{PLURAL:$5|bir gün|$5  gün}} geçerlidir.\nBu geçici parola ile giriş yapın ve yeni bir şifre seçin. Şifre değişimini siz istemediyseniz veya şifrenizi hatırladıysanız ve artık şifrenizi değiştirmek istemiyorsanız; bu iletiyi önemsemeyerek eski şifrenizi kullanmaya devam edebilirsiniz.",
        "passwordreset-emailtext-user": "$1 adlı kullanıcı, {{SITENAME}} ($4) için hesap bilgilerinizin hatırlatılmasını istedi. Aşağıdaki kullanıcı {{PLURAL:$3|hesabı|hesapları}} bu e-posta adresiyle ilişkili:\n\n$2\n\n{{PLURAL:$3|Bu geçici şifre|Bu geçici şifreler}} {{PLURAL:$5|bir gün|$5  gün}} geçerlidir.\nBu geçici parola ile giriş yapın ve yeni bir şifre seçin. Bu talep bir başkasına aitse veya şifrenizi hatırladıysanız ve artık şifrenizi değiştirmek istemiyorsanız; bu iletiyi önemsemeyerek eski şifrenizi kullanmaya devam edebilirsiniz.",
        "passwordreset-emailelement": "Kullanıcı adı: \n$1\n\nGeçici şifre: \n$2",
-       "passwordreset-emailsentemail": "Eğer bu hesabınız için kayıtlı bir e-posta adresi ise, bir parola sıfırlama e-postası gönderilecektir.",
+       "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-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",
        "changeemail": "E-posta adresini değiştir veya çıkar",
        "shown-title": "Sayfa başına $1 {{PLURAL:$1|sonuç|sonuç}} göster",
        "viewprevnext": "($1 {{int:pipe-separator}} $2) ($3) gör",
        "searchmenu-exists": "'''Bu vikide \"[[:$1]]\" adında bir sayfa mevcut'''",
-       "searchmenu-new": "<strong>Bu vikide \"[[:$1]]\" sayfasını oluştur!</strong> {{PLURAL:$2|0=|Ayrıca aramınızda bulunan sayfayı görün.|Ayrıca bulunan arama sonuçlarını görün.}}",
+       "searchmenu-new": "<strong>\"[[:$1]]\" sayfasını oluşturabilirsiniz!</strong> {{PLURAL:$2|0=|Ayrıca aramanız sonucunda bulunan sayfayı görüntüleyebilirsiniz.|Ayrıca bulunan arama sonuçlarını görüntüleyebilirsiniz.}}",
        "searchprofile-articles": "İçerik sayfaları",
        "searchprofile-images": "Çokluortam",
        "searchprofile-everything": "Her şey",
        "prefs-watchlist-token": "İzleme listesi anahtarı:",
        "prefs-misc": "Diğer ayarlar",
        "prefs-resetpass": "Parolayı değiştir",
-       "prefs-changeemail": "E-posta'yı değiştir",
+       "prefs-changeemail": "E-posta adresini değiştir veya kaldır",
        "prefs-setemail": "E-posta adresini ayarlayın",
        "prefs-email": "E-posta seçenekleri",
        "prefs-rendering": "Görünüm",
        "right-createpage": "Sayfa oluştur (tartışma sayfası olmayan)",
        "right-createtalk": "Tartışma sayfaları oluştur",
        "right-createaccount": "Yeni kullanıcı hesapları yarat",
+       "right-autocreateaccount": "Otomatik olarak harici bir kullanıcı hesabı ile oturum aç",
        "right-minoredit": "Değişikliklerini küçük olarak kaydet",
        "right-move": "Sayfaları taşı",
        "right-move-subpages": "Sayfaları altsayfalarıyla beraber taşı",
        "right-blockemail": "Bir kullanıcının e-posta göndermesini engelle",
        "right-hideuser": "Bir kullanıcı adını engelle, genelden gizleyerek",
        "right-ipblock-exempt": "IP engellemelerini atla, otomatik engelle ve aralık engellemeleri",
-       "right-proxyunbannable": "Proxylerin otomatik engellemelerini atla",
        "right-unblockself": "Kendi engellemesini kaldır",
        "right-protect": "Koruma düzeylerini değiştir ve kademeli korumalı sayfaları düzenle",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" olarak korunan sayfalarda değişiklik yap",
        "right-managechangetags": "Veritabanında [[Special:Tags|etiket]] oluşturma veya silme",
        "right-applychangetags": "Değişiklikleriyle beraber [[Special:Tags|etiketleri]] uygula",
        "right-changetags": "Tekil sürümler ve günlük kayıtlarına rastgele [[Special:Tags|etiket]] ekleme veya çıkarma",
+       "grant-group-email": "E-posta gönder",
+       "grant-basic": "Basit haklar",
        "newuserlogpage": "Yeni kullanıcı kayıtları",
        "newuserlogpagetext": "En son kaydolan kullanıcı kayıtları.",
        "rightslog": "Kullanıcı hakları kayıtları",
        "recentchanges-label-plusminus": "Sayfa boyutundaki değişikliğin bayt bazında değeri",
        "recentchanges-legend-heading": "'''Gösterge:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (ayrıca [[Special:NewPages|yeni sayfalar listesine]] bakınız)",
+       "recentchanges-submit": "Göster",
        "rcnotefrom": "<strong>$3, $4</strong> tarihinden itibaren yapılan {{PLURAL:$5|değişiklik|değişiklik}} aşağıdadır (<strong>$1</strong> tarhine kadar olanlar gösterilmektedir).",
        "rclistfrom": "$3 $2 tarihinden itibaren yeni değişiklikleri göster",
        "rcshowhideminor": "Küçük değişiklikleri $1",
        "upload-form-label-infoform-description": "Açıklama",
        "upload-form-label-usage-title": "Kullanımı",
        "upload-form-label-usage-filename": "Dosya adı",
+       "foreign-structured-upload-form-label-infoform-categories": "Kategoriler",
+       "foreign-structured-upload-form-label-infoform-date": "Tarih",
+       "foreign-structured-upload-form-3-label-yes": "Evet",
+       "foreign-structured-upload-form-3-label-no": "Hayır",
        "backend-fail-stream": "$1 dosyası okunamadı.",
        "backend-fail-backup": "\"$1\" dosyası yedeklenemedi.",
        "backend-fail-notexists": "$1 dosyası mevcut değil.",
        "linkstoimage-redirect": "$1 (dosya yönlendirme) $2",
        "duplicatesoffile": "Şu {{PLURAL:$1|dosya|$1 dosya}}, bu dosyanın kopyası ([[Special:FileDuplicateSearch/$2|daha fazla ayrıntı]]):",
        "sharedupload": "Bu dosya $1 projesinden olup, diğer projelerde kullanılıyor olabilir.",
-       "sharedupload-desc-there": "Bu dosya $1 deposundan ve diğer projeler tarafından kullanılıyor olabilir. Daha fazla bilgi için lütfen [$2 dosya açıklama sayfasına] bakın.",
-       "sharedupload-desc-here": "Bu dosya $1 deposundan ve diğer projeler tarafından kullanılıyor olabilir.\nAşağıda [$2 dosya açıklama sayfasındaki] açıklama gösteriliyor.",
+       "sharedupload-desc-there": "Bu dosya $1 deposunda bulunmaktadır ve diğer projeler tarafından kullanılıyor olabilir. Daha fazla bilgi için lütfen [$2 dosya açıklama sayfasına] bakın.",
+       "sharedupload-desc-here": "Bu dosya $1 deposunda bulunmaktadır ve diğer projeler tarafından kullanılıyor olabilir.\nAşağıda [$2 dosya açıklama sayfasındaki] açıklama gösteriliyor.",
        "sharedupload-desc-edit": "Bu dosya $1 projesinden olup, diğer projelerde kullanılıyor olabilir.\nDosyanın açıklama sayfasında değişiklik yapmak için ilgili sayfaya [$2 buradan] gidebilirsiniz.",
        "sharedupload-desc-create": "Bu dosya $1 projesinden olup, diğer projelerde kullanılıyor olabilir.\nDosyanın açıklama sayfasında değişiklik yapmak için ilgili sayfaya [$2 buradan] gidebilirsiniz.",
        "filepage-nofile": "Bu isimde bir dosya yok.",
        "uploadnewversion-linktext": "Dosyanın yenisini yükleyin",
        "shared-repo-from": "$1'dan",
        "shared-repo": "ortak bir havuz",
-       "shared-repo-name-wikimediacommons": "Wikimedia Commons'ta",
+       "shared-repo-name-wikimediacommons": "Wikimedia Commons",
        "upload-disallowed-here": "Bu dosyanın üzerine yazamazsınız.",
        "filerevert": "$1 dosyasını eski haline döndür",
        "filerevert-legend": "Dosyayı eski haline döndür",
        "specialloguserlabel": "Kullanıcı:",
        "speciallogtitlelabel": "Hedef (başlık ya da kullanıcı):",
        "log": "Kayıtlar",
+       "logeventslist-submit": "Göster",
        "all-logs-page": "Tüm ortak günlükler",
        "alllogstext": "{{SITENAME}} için mevcut tüm günlüklerin birleşik gösterimi.\nGünlük tipini, kullanıcı adını (büyük-küçük harf duyarlı), ya da etkilenen sayfayı (yine büyük-küçük harf duyarlı) seçerek görünümü daraltabilirsiniz.",
        "logempty": "Kayıtlarda eşleşen bilgi yok.",
        "wlshowlast": "Son $1 saati $2 günü göster",
        "watchlistall2": "Hepsini göster",
        "watchlist-hide": "Gizle",
+       "wlshowtime": "Gösterilecek zaman aralığı:",
        "wlshowhideminor": "küçük değişiklikler",
        "wlshowhidebots": "botlar",
        "wlshowhideliu": "kayıtlı kullanıcılar",
        "wlshowhideanons": "anonim kullanıcılar",
        "wlshowhidepatr": "incelenmiş değişiklikler",
+       "wlshowhidemine": "değişikliklerim",
        "watchlist-options": "İzleme listesi seçenekleri",
        "watching": "İzleniyor...",
        "unwatching": "İzlenmiyor...",
        "javascripttest-pagetext-frameworks": "Lütfen aşağıdaki test çerçevelerinden birini seçin: $1",
        "javascripttest-pagetext-skins": "Testleri koşmak için bir tema seçin:",
        "javascripttest-qunit-intro": "mediawiki.org üzerinden [$1 deneme belgelerine] bakınız.",
-       "tooltip-pt-userpage": "Kullanıcı sayfanız",
+       "tooltip-pt-userpage": "{{GENDER:|Kullanıcı}} sayfanız",
        "tooltip-pt-anonuserpage": "IP adresine ait bir kullanıcı sayfasını düzenliyorsunuz",
-       "tooltip-pt-mytalk": "Mesaj sayfanız",
+       "tooltip-pt-mytalk": "{{GENDER:|Mesaj sayfanız}}",
        "tooltip-pt-anontalk": "Bu IP adresinden yapılmış değişiklikleri tartış",
-       "tooltip-pt-preferences": "Tercihleriniz (ayarlarınız)",
+       "tooltip-pt-preferences": "{{GENDER:|Tercihleriniz}}",
        "tooltip-pt-watchlist": "Değişiklikler için izlemeye aldığınız sayfaların listesi",
-       "tooltip-pt-mycontris": "Katkılarınızın listesi",
+       "tooltip-pt-mycontris": "{{GENDER:|Katkılarınızın}} listesi",
        "tooltip-pt-anoncontribs": "Bu IP adresinden yapılmış değişiklikler listesi",
        "tooltip-pt-login": "Oturum açmanız tavsiye edilmektedir; ancak bu zorunda değildir",
        "tooltip-pt-logout": "Sistemden çık",
        "tooltip-t-recentchangeslinked": "Bu sayfadan bağlantı verilen sayfalardaki son değişiklikler",
        "tooltip-feed-rss": "Bu sayfa için RSS beslemesi",
        "tooltip-feed-atom": "Bu sayfa için atom beslemesi",
-       "tooltip-t-contributions": "Kullanıcının katkılarının listesini gör",
+       "tooltip-t-contributions": "{{GENDER:$1|Bu kullanıcı}} tarafından yapılan katkıların listesi",
        "tooltip-t-emailuser": "Bu kullanıcıya e-posta gönder",
        "tooltip-t-info": "Bu sayfa hakkında daha fazla bilgi",
        "tooltip-t-upload": "Dosya yükle",
        "mediastatistics-header-text": "Metin türü",
        "mediastatistics-header-executable": "Yürütülebilir dosyalar",
        "mediastatistics-header-archive": "Sıkıştırılmış biçimler",
+       "mediastatistics-header-total": "Tüm dosyalar",
        "json-warn-trailing-comma": "$1 takip eden {{PLURAL:$1|virgül|virgüller}} JSON'dan kaldırılmıştır.",
        "json-error-unknown": "JSON ile ilgili bir sorun oluştu. Hata:$1",
        "json-error-depth": "Azami yığın derinliği aşıldı",
        "special-characters-title-endash": "tire",
        "special-characters-title-emdash": "uzun çizgi",
        "special-characters-title-minus": "Eksi işareti",
+       "mw-widgets-dateinput-no-date": "Hiçbir tarih seçilmedi",
        "mw-widgets-titleinput-description-new-page": "sayfa henüz mevcut değil",
-       "mw-widgets-titleinput-description-redirect": "$1'e yönlendirildi"
+       "mw-widgets-titleinput-description-redirect": "$1'e yönlendirildi",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "çerez tabanlı oturumlar",
+       "sessionprovider-nocookies": "Çerezler devre dışı olabilir. Çerkezlerin aktif olduğuna emin olun ve yeniden başlatin.",
+       "randomrootpage": "Rastgele kök sayfası"
 }
index c0eaef3..a523c22 100644 (file)
@@ -32,6 +32,7 @@
        "tog-hideminor": "Соңгы үзгәртүләр исемлегендә кече үзгәртүләр яшерелсен",
        "tog-hidepatrolled": "Тикшерелгән үзгәртүләр яңа үзгәртүләр исемлегеннән яшерелсен.",
        "tog-newpageshidepatrolled": "Тикшерелгән битләр яңа битләр исемлегеннән яшерелсен",
+       "tog-hidecategorization": "Битләрне төркемләшүне ябу",
        "tog-extendwatchlist": "Соңгыларын гына түгел, ә барлык үзгәртүләрне эченә алган, киңәйтелгән күзәтү исемлеге",
        "tog-usenewrc": "Соңгы үзгәртүләрдә һәм күзәтү исемлегендә үзгәрешләрне төркемләргә",
        "tog-numberheadings": "Атамалар автомат рәвештә номерлансын",
        "tog-watchlisthideliu": "Авторизацияне узган кулланучыларның үзгәртүләре күзәтү исемлегеннән яшерелсен",
        "tog-watchlisthideanons": "Аноним кулланучыларның үзгәртүләре күзәтү исемлегеннән яшерелсен",
        "tog-watchlisthidepatrolled": "Тикшерелгән үзгәртүләр күзәтү исемлегеннән яшерелсен",
+       "tog-watchlisthidecategorization": "Битләрне төркемләшүне ябу",
        "tog-ccmeonemails": "Башка кулланучыларга җибәргән хатларымның копияләре миңа да җибәрелсен",
        "tog-diffonly": "Юрама чагыштыру астында бит эчтәлеге күрсәтелмәсен",
        "tog-showhiddencats": "Яшерен төркемнәр күрсәтелсен",
        "tog-norollbackdiff": "Кире кайтару ясагач юрамалар аермасы күрсәтелмәсен",
        "tog-useeditwarning": "Битне сакламыйча китү вакытында мине кисәтергә",
+       "tog-prefershttps": "Системага керргәндә һәрвакыт саклаулы тоташуны кулланырга",
        "underline-always": "Һәрвакыт",
        "underline-never": "Бервакытта да",
        "underline-default": "Браузер көйләнмәләре кулланылсын",
        "october-date": "$1 Октябрь",
        "november-date": "$1 Ноябрь",
        "december-date": "$1 Декабрь",
+       "period-am": "ТК",
+       "period-pm": "ТС",
        "pagecategories": "{{PLURAL:$1|1=Төркем|Төркемнәр}}",
        "category_header": "«$1» төркемендәге битләр",
        "subcategories": "Төркемчәләр",
        "hidden-categories": "{{PLURAL:$1|1=Яшерен төркем|Яшерен төркемнәр}}",
        "hidden-category-category": "Яшерен төркемнәр",
        "category-subcat-count": "{{PLURAL:$2|1=Әлеге төркем бары тик бу астөркемне генә үз өченә ала.|Әлеге төркемдә $2 астөркемдән бары тик $1 {{PLURAL:$1|астөркем}} генә күрсәтелгән.}}",
-       "category-subcat-count-limited": "Бу төркемдә {{PLURAL:$1|$1 төркемчә}} бар.",
+       "category-subcat-count-limited": "Бу төркемдә {{PLURAL:$1|$1 төркемчә}}.",
        "category-article-count": "{{PLURAL:$2|1=Әлеге төркемдә бер генә бит бар.|Әлеге төркемнең $2 {{PLURAL:$2|битеннән}} {{PLURAL:$1|$1 бит}} кенә курсәтелгән.}}",
-       "category-article-count-limited": "Бу төркемдә {{PLURAL:$1|$1 бит}} бар.",
+       "category-article-count-limited": "Бу төркемдә {{PLURAL:$1|$1 бит|1=бары тик бер бит}}.",
        "category-file-count": "{{PLURAL:$2|1=Әлеге төркемдә бер генә файл бар.|Әлеге төркемдә $2 {{PLURAL:$2|файлдан}} {{PLURAL:$1|$1 файл}} гына курсәтелгән.}}",
-       "category-file-count-limited": "Бу төркемдә {{PLURAL:$1|$1 файл}} бар.",
+       "category-file-count-limited": "Бу төркемдә {{PLURAL:$1|$1 файл|1=бары тик бер файл}}.",
        "listingcontinuesabbrev": "дәвамы",
        "index-category": "Индексланган битләр",
        "noindex-category": "Индексланмаган битләр",
        "qbmyoptions": "Битләрем",
        "faq": "ЕБС",
        "faqpage": "Project:ЕБС",
-       "actions": "Ð¥Ó\99Ñ\80Ó\99кÓ\99Ñ\82",
+       "actions": "Ð\93амÓ\99ллÓ\99Ñ\80",
        "namespaces": "Исемнәр мәйданы",
-       "variants": "ТөÑ\80лÓ\99р",
+       "variants": "Ð\92аÑ\80ианÑ\82лар",
        "navigation-heading": "Навигация",
        "errorpagetitle": "Хата",
        "returnto": "$1 битенә кайту.",
        "unprotectthispage": "Бу битнең яклауын үзгәртү",
        "newpage": "Яңа бит",
        "talkpage": "Бит турында фикер алышу",
-       "talkpagelinktext": "Ð\91әхәс",
+       "talkpagelinktext": "бәхәс",
        "specialpage": "Махсус бит",
        "personaltools": "Шәхси кораллар",
        "articlepage": "Мәкаләне карау",
        "pool-timeout": "Кысылуның  вакыты узды",
        "pool-queuefull": "Сорауларны саклау  бите тулы",
        "pool-errorunknown": "Билгесез  хата",
+       "pool-servererror": "Пул санау хезмәте эшләми ($1).",
        "poolcounter-usage-error": "$1: куллану хатасы",
        "aboutsite": "{{SITENAME}} турында",
        "aboutpage": "Project:Тасвирлама",
        "editold": "үзгәртү",
        "viewsourceold": "башлангыч кодны карау",
        "editlink": "үзгәртү",
-       "viewsourcelink": "башлангыч кодны карау",
+       "viewsourcelink": "чыганак кодны карау",
        "editsectionhint": "$1 бүлеген үзгәртү",
        "toc": "Эчтәлек",
        "showtoc": "күрсәтү",
        "red-link-title": "$1 (мондый бит юк)",
        "sort-descending": "Кимү буенча урнаштыру",
        "sort-ascending": "Арту буенча урнаштыру",
-       "nstab-main": "Ð\91иÑ\82",
+       "nstab-main": "Ð\9cÓ\99калÓ\99",
        "nstab-user": "Кулланучы",
        "nstab-media": "Мультимедиа",
        "nstab-special": "Махсус бит",
        "nstab-project": "Проект бите",
        "nstab-image": "Файл",
-       "nstab-mediawiki": "Хәбәр",
+       "nstab-mediawiki": "Хат",
        "nstab-template": "Калып",
        "nstab-help": "Ярдәм",
        "nstab-category": "Төркем",
        "laggedslavemode": "Игътибар: биттә соңгы яңартулар күрсәтелмәгән булырга мөмкин.",
        "readonly": "Мәгълүматлар базасына язу ябылган",
        "enterlockreason": "Ябылу сәбәбен һәм вакытын күрсәтегез.",
-       "readonlytext": "Мәгълүмат базасы хәзерге вакытта яңа битләр ясаудан һәм башка үзгәртүләрдән ябылган. Бу планлаштырылган хезмәт күрсәтү сәбәпле булырга мөмкин.\nЯбучы оператор түбәндәге аңлатманы калдырган:\n$1",
+       "readonlytext": "Мәгълүмат базасы хәзерге вакытта яңа битләр ясаудан һәм башка үзгәртүләрдән ябылган: бу планлаштырылган хезмәт күрсәтү сәбәпле булырга мөмкин.\nЯбучы идарәче түбәндәге аңлатманы калдырган: $1",
        "missing-article": "Мәгълүматлар базасында «$1» $2 битенең соралган тексты табылмады.\n\nБу, гадәттә, искергән сылтама буенча бетерелгән битнең үзгәртү тарихына күчкәндә килеп чыга.\n\nӘгәр хата монда түгел икән, сез программада хата тапкан булырга мөмкинсез.\nЗинһар өчен, URLны күрсәтеп, бу турыда [[Special:ListUsers/sysop|идарәчегә]] хәбәр итегез.",
        "missingarticle-rev": "(юрама № $1)",
        "missingarticle-diff": "(аерма: $1, $2)",
        "perfcachedts": "Бу мәгълүматлар кэштан алынган, ул соңгы тапкыр $1 яңартылды. Кэшта иң күбе {{PLURAL:$4|язма}} саклана",
        "querypage-no-updates": "Хәзер бу битне яңартып булмый. Монда күрсәтелгән мәгълүматлар кабул ителмәячәк.",
        "viewsource": "Карау",
-       "viewsource-title": "$1 Ð±Ð¸Ñ\82енең Ñ\8fÑ\85ма Ñ\82екÑ\81Ñ\82ын карау",
+       "viewsource-title": "$1 Ð±Ð¸Ñ\82енең Ñ\87Ñ\8bганагын карау",
        "actionthrottled": "Тизлек киметелгән",
-       "actionthrottledtext": "Спамга каршы көрәш өчен аз вакыт эчендә бу гамәлне еш куллану тыелган. Зинһар, соңарак кабатлагыз.",
+       "actionthrottledtext": "Спамга каршы көрәш өчен, аз вакыт эчендә бу гамәлне еш куллану тыелган һәм СЕз бирелгән вакытны бетергәнсез инде. Зинһар, соңарак кабатлагыз.",
        "protectedpagetext": "Бу бит үзгәртүләрдән һәм башка төрле гамәлләрдән якланган.",
-       "viewsourcetext": "Сез бу битнең башлангыч текстын карый һәм күчерә аласыз:",
-       "viewyourtext": "Сез '''үз төзәтмәләрегезне''' бу сәхифәдә карый һәм чыгарылма текстны күчермәли аласыз:",
+       "viewsourcetext": "Сез бу битнең башлангыч текстын карый һәм күчерә аласыз.",
+       "viewyourtext": "Сез <strong>үз төзәтмәләрегезне</strong> бу сәхифәдә карый һәм чыгарылма текстны күчермәли аласыз.",
        "protectedinterface": "Бу биттә программа тәэминатының интерфейс хәбәрләре бар. Вандализмга каршы көрәш сәбәпле, бу битне үзгәртү тыела. Әлеге хәбәрнең тәрҗемәсен өстәү яки үзгәртү өчен, зинһар өчен, MediaWiki [//translatewiki.net/ translatewiki.net] тәрҗемәләү сайтын кулланыгыз.",
        "editinginterface": "<strong>Игътибар:</strong> Сез программа тәэминатының интерфейс тексты булган битне үзгәртәсез. Бу башка кулланучыларга да тәэсир итәчәк.",
        "translateinterface": "Бу хәбәрнең текстын үзгәртү өчен яки өстәмәләр кертү өчен MediaWiki җирләштерү сайтын кулланыгыз [//translatewiki.net/ translatewiki.net].",
        "virus-badscanner": "Көйләү хатасы. Билгесез вируслар сканеры: ''$1''",
        "virus-scanfailed": "сканерлау хатасы ($1 коды)",
        "virus-unknownscanner": "билгесез антивирус:",
-       "logouttext": "'''Сез хисап язмагыздан чыктыгыз.'''\n\nСез {{SITENAME}} проектында аноним рәвештә кала яисә шул ук яки башка исем белән яңадан <span class='plainlinks'>[$1 керә]</span> аласыз.\nКайбер битләр Сез кергән кебек күрсәтелергә мөмкин. Моны бетерү өчен браузер кэшын чистартыгыз.",
-       "welcomeuser": "Ð¥Ñ\83Ñ\88 ÐºÐ¸Ð»Ð´егез, $1!",
+       "logouttext": "<strong>Сез хисап язмагыздан чыктыгыз.</strong>\n\nКайбер битләр Сез кергән кебек күрсәтелергә мөмкин. Моны бетерү өчен браузер кэшын чистартыгыз.",
+       "welcomeuser": "РÓ\99Ñ\85им Ð¸Ñ\82егез, $1!",
        "yourname": "Кулланучы исеме:",
        "userlogin-yourname": "Кулланучы исеме",
        "userlogin-yourname-ph": "Хисап язмасының исемен кертегез",
        "yourpasswordagain": "Серсүзне кабат кертү:",
        "createacct-yourpasswordagain": "Серсүзне раслагыз",
        "createacct-yourpasswordagain-ph": "Серсүзне кабаттан кертегез",
-       "remembermypassword": "Хисап язмамны бу браузерда саклансын (иң күп $1 {{PLURAL:$1|көн|көн|көн}}гә кадәр)",
+       "remembermypassword": "Хисап язмам бу браузерда саклансын (иң күбе $1 {{PLURAL:$1|көн}})",
        "userlogin-remembermypassword": "Системада калырга",
        "userlogin-signwithsecure": "Якланган кушылу",
        "yourdomainname": "Сезнең доменыгыз:",
        "logout": "Чыгу",
        "userlogout": "Чыгу",
        "notloggedin": "Сез хисап язмагызга кермәгәнсез",
-       "userlogin-noaccount": "Ð\90ккаÑ\83нÑ\82 юкмы?",
-       "userlogin-joinproject": "Проектка керү",
+       "userlogin-noaccount": "ХиÑ\81ап Ñ\8fзмагÑ\8bз юкмы?",
+       "userlogin-joinproject": "{{SITENAME}} проектына керү",
        "nologin": "Кулланучы исемең юкмы? '''$1'''",
        "nologinlink": "Хисап язмасы төзү",
        "createaccount": "Яңа кулланучыны теркәү",
        "createacct-emailoptional": "Электрон почта юлламагыз (мәҗбүри түгел)",
        "createacct-email-ph": "Электрон почта юлламагызны языгыз",
        "createacct-another-email-ph": "Электрон почта юлламагызны кертегез",
-       "createaccountmail": "электрон почта аша",
+       "createaccountmail": "Очраклы рәвештә ясалган ваҡытлыча серсузне файдаланырга һәм аны минем электрон почтама җибәрергә",
        "createacct-realname": "Чын исем (мәҗбүри түгел)",
        "createaccountreason": "Сәбәп:",
        "createacct-reason": "Сәбәп",
        "login-userblocked": "Бу кулланучы тыелды. Керү тыелган.",
        "wrongpassword": "Язылган серсүз дөрес түгел. Тагын бер тапкыр сынагыз.",
        "wrongpasswordempty": "Серсүз юлы буш булырга тиеш түгел.",
-       "passwordtooshort": "Сезсүз $1 {{PLURAL:$1|символдан}} торырга тиеш.",
+       "passwordtooshort": "Сезсүз кимендә $1 {{PLURAL:$1|символдан}} торырга тиеш.",
        "password-name-match": "Кертелгән серсүз кулланучы исеменнән аерылырга тиеш.",
        "password-login-forbidden": "Бу кулланучы исемен һәм серсүзне куллану тыелган",
        "mailmypassword": "Серсүзне бетерү",
        "passwordremindertitle": "{{SITENAME}} кулланучысына вакытлы серсүз тапшыру",
-       "passwordremindertext": "Кемдер (бәлки, сездер, IP адресы: $1) {{SITENAME}} ($4) өчен яңа серсүз соратты. $2 өчен яңа серсүз: $3. Әгәр бу сез булсагыз, системага керегез һәм серсүзне алмаштырыгыз. Яңа серсүз $5 {{PLURAL:$5|көн}} гамәлдә булачак.\n\nӘгәр сез серсүзне алмаштыруны сорамаган булсагыз яки, оныткан очракта, исегезгә төшергән булсагыз, бу хәбәргә игътибар бирмичә, иске серсүзегезне куллануны дәвам итегез.",
+       "passwordremindertext": "Кемдер (бәлки, сездер, IP адресы: $1)  {{grammar:genitive|{{SITENAME}}}} ($4) өчен яңа серсүз соратты. $2 кулланучысы өчен яңа вакытлыча серсүз: $3. Әгәр бу сез булган булсагыз, системага керегез һәм яңа серсүз сайлагыз. Сезнең вакытлыча серсүз гамәлдә $5 {{PLURAL:$5|көн}} булачак.\n\nӘгәр сез серсүзне алмаштыруны сорамаган булсагыз яки, оныткан очракта, исегезгә төшергән булсагыз, бу хәбәргә игътибар бирмичә, иске серсүзегезне куллануны дәвам итегез.",
        "noemail": "$1 исемле кулланучы өчен электрон почта адресы язылмаган.",
        "noemailcreate": "Сез дөрес e-mail адресы күрсәтергә тиеш",
        "passwordsent": "Яңа серсүз $1 исемле кулланучының электрон почта адресына җибәрелде.\n\nЗинһар, серсүзне алгач, системага яңадан керегез.",
        "blocked-mailpassword": "Сезнең IP адресыгыз белән битләр үзгәртеп һәм серсүзне яңартып булмый.",
-       "eauthentsent": "Ð\90дÑ\80еÑ\81 Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үне Ð´Ó\99лиллÓ\99Ò¯ Ó©Ñ\87ен Ð°Ò£Ð° Ð¼Ð°Ñ\85Ñ\81Ñ\83Ñ\81 Ñ\85аÑ\82 Ò\97ибÓ\99Ñ\80елде. Ð¥Ð°Ñ\82Ñ\82а Ñ\8fзÑ\8bлганнаÑ\80нÑ\8b Ò¯Ñ\82Ó\99вегез Ñ\81оÑ\80ала.",
+       "eauthentsent": "Ð\9aÒ¯Ñ\80Ñ\81Ó\99Ñ\82елгÓ\99н Ñ\8dлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87Ñ\82а Ð°Ð´Ñ\80еÑ\81Ñ\8bна Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үлÓ\99Ñ\80не Ñ\80аÑ\81лаÑ\83 Ó©Ñ\87ен Ñ\85аÑ\82 Ò\97ибÓ\99Ñ\80елде. Ð\9aилÓ\99Ñ\87Ó\99кÑ\82Ó\99дÓ\99 Ñ\85аÑ\82лаÑ\80 ÐºÐ°Ð±Ñ\83л Ð¸Ñ\82Ò¯ Ó©Ñ\87ен, Ñ\80аÑ\81лаÑ\83нÑ\8b Ò¯Ñ\82егез.",
        "throttled-mailpassword": "Серсүзне электрон почтага җибәрү гамәлен сез {{PLURAL:$1|1=соңгы $1 сәгать}} эчендә кулландыгыз инде. Бу гамәлне явызларча куллануны кисәтү максатыннан аны $1 {{PLURAL:$1|сәгать}} аралыгында бер генә тапкыр башкарып була.",
        "mailerror": "Хат җибәрү хатасы: $1",
-       "acct_creation_throttle_hit": "Сезнең IP адресыннан бу тәүлек эчендә {{PLURAL:$1|$1 хисап язмасы}} төзелде инде. Шунлыктан бу гамәл сезнең өчен вакытлыча ябык.",
+       "acct_creation_throttle_hit": "Сезнең IP адресыннан бу тәүлек эчендә {{PLURAL:$1|$1 хисап язмасы}} төзелде инде. Шунлыктан бу IP-адрес буенча сезнең өчен әлеге гамәл вакытлыча ябык.",
        "emailauthenticated": "Сезнең электрон почта адресыгыз $2 $3 расланды.",
-       "emailnotauthenticated": "Электрон почта адресыгыз әле дәлилләнмәгән, шуңа викиның электрон почта белән эшләү гамәлләре сүндерелде.",
+       "emailnotauthenticated": "Электрон почта адресыгыз әле дәлилләнмәгән.\nХатлар әлеге мөмкинлекләргә җибәрелмәячәк.",
        "noemailprefs": "Электрон почта адресыгыз күрсәтелмәгән, шуңа викиның электрон почта белән эшләү гамәлләре сүндерелгән.",
        "emailconfirmlink": "Электрон почта адресыгызны дәлилләгез.",
        "invalidemailaddress": "Электрон почта адресы кабул ителә алмый, чөнки ул дөрес форматка туры килми. Зинһар, дөрес адрес кертегез яки юлны буш калдырыгыз.",
        "cannotchangeemail": "Бу хисап язмасының электрон почта адресы бу викида үзгәртелә алмый",
        "accountcreated": "Хисап язмасы төзелде",
-       "accountcreatedtext": "$1 исемле кулланучы өчен хисап язмасы төзелде.",
+       "accountcreatedtext": "[[{{ns:User}}:$1|$1]] ([[{{ns:User talk}}:$1|бәхәс]]) кулланучысы өчен хисап язмасы төзелде.",
        "createaccount-title": "{{SITENAME}}: теркәлү",
        "createaccount-text": "Кемдер, электрон почта адресыгызны күрсәтеп, {{SITENAME}} ($4) проектында «$3» серсүзе белән «$2» исемле хисап язмасы теркәде. Сез керергә һәм серсүзегезне үзгәртергә тиеш.\n\nХисап язмасы төзү хата булса, бу хатны онытыгыз.",
-       "login-throttled": "Сез Ð°Ñ\80Ñ\82Ñ\8bк ÐºÒ¯Ð¿ Ñ\82апкÑ\8bÑ\80 ÐºÐµÑ\80еÑ\80гÓ\99 Ñ\82Ñ\8bÑ\80Ñ\8bÑ\88Ñ\82Ñ\8bгÑ\8bз.\nЯңадан ÐºÐ°Ð±Ð°Ñ\82лаганÑ\87Ñ\8b Ð±ÐµÑ\80аз көтүегез сорала.",
+       "login-throttled": "Сез Ð°Ñ\80Ñ\82Ñ\8bк ÐºÒ¯Ð¿ Ñ\82апкÑ\8bÑ\80 ÐºÐµÑ\80еÑ\80гÓ\99 Ñ\82Ñ\8bÑ\80Ñ\8bÑ\88Ñ\82Ñ\8bгÑ\8bз.\nÐ\97инһаÑ\80, Ñ\8fңадан ÐºÐ°Ð±Ð°Ñ\82лаганÑ\87Ñ\8b, $1 көтүегез сорала.",
        "login-abort-generic": "Системага уңышсыз керү очрагы",
        "loginlanguagelabel": "Тел: $1",
        "suspicious-userlogout": "Сезнең эшчәнлекне бетерү соравыгыз кире кагылды, чөнки ул ялгыш браузер яисә кэшлаучы прокси аша җибәрелергэ мөмкин.",
        "php-mail-error-unknown": "PHP mail() функциясендә билгесез хата",
        "user-mail-no-addy": "Электрон почта адресыннан башка электрон хат җибәрмәкче булды",
        "changepassword": "Серсүзне үзгәртү",
-       "resetpass_announce": "Сез Ñ\8dлекÑ\82Ñ\80он Ð¿Ð¾Ñ\87Ñ\82а Ð°Ñ\88а Ð²Ð°ÐºÑ\8bÑ\82лÑ\8bÑ\87а Ð±Ð¸Ñ\80елгÓ\99н Ñ\81еÑ\80Ñ\81үз Ñ\8fÑ\80дÓ\99мендÓ\99 ÐºÐµÑ\80дегез. Ð¡Ð¸Ñ\81Ñ\82емага ÐºÐµÑ\80үне Ñ\82өгÓ\99ллÓ\99Ò¯ Ó©Ñ\87ен Ñ\8fңа Ñ\81еÑ\80Ñ\81үз Ñ\82өзегез.",
+       "resetpass_announce": "Системага керүне төгәлләү өчен яңа серсүз төзегез.",
        "resetpass_text": "<!-- Монда текст өстәгез -->",
        "resetpass_header": "Хисап язмасы серсүзен үзгәртү",
        "oldpassword": "Иске серсүз:",
        "newpassword": "Яңа серсүз:",
        "retypenew": "Яңа серсүзне кабатлагыз:",
        "resetpass_submit": "Серсүз куеп керү",
-       "changepassword-success": "Сезнең серсүз уңышлы үзгәртелде! Системага керү башкарыла...",
+       "changepassword-success": "Серсүзегез уңышлы үзгәртелде!",
        "resetpass_forbidden": "Серсүз үзгәртелә алмый",
        "resetpass-no-info": "Бу битне карау өчен сез системага үз хисап язмагыз ярдәмендә керергә тиеш.",
        "resetpass-submit-loggedin": "Серсүзне үзгәртү",
        "passwordreset-emailtext-ip": "Кемдер (бәлки, сездер, $1 IP-адресыннан) {{SITENAME}} ($4) проектында сезнең серсүзне искә төшерүне сорады.\n{{PLURAL:$3|1=Түбәндәге хисап язмасы|Түбәндәге хисап язмалары}} бу электрон әрҗә адресы белән бәйле:\n\n$2\n\n{{PLURAL:$3|1=Бу вакытлы серсүз|Бу вакытлы серсүзләр}} {{PLURAL:$5|$5 көн}} дәвамында эшлиячәкләр.\nСез системага керергә һәм яңа серсүз сайларга тиешсез.\nӘгәр сез серсүз сорамаган булсагыз яки элеккеге серсүзегезне искә төшерсәгез \nһәм аны үзгәртергә теләмәсәгез, бу хатка җавап бирмәгез\nһәм элеккеге серсүзегезне кулланыгыз.",
        "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": "ТүбÓ\99ндÓ\99 Ñ\8fзÑ\8bлган Ñ\85аÑ\82-иÑ\81кÓ\99Ñ\80Ñ\82Ò¯ ÐºÐ¸Ñ\82еÑ\80елгÓ\99н, Ð°Ð½Ñ\8b Ò\97ибÓ\99Ñ\80мÓ\99үнең Ñ\81Ó\99бÓ\99бе:$1",
+       "passwordreset-emailsentemail": "Әгәрдә бу электрон әрҗәгез сезнең хисап язмагыз белән бәйләнгән булса,сезгә серсүзне яңарту өчен хат җибәреләчәк.",
+       "passwordreset-emailsent-capture": "Серсүзне яңарту турындагы мәгълүмәт электрон хат белән сезгә җибәрелде, аның текстын түбәндә карарга мөмкин.",
+       "passwordreset-emailerror-capture": "СеÑ\80Ñ\81үзне Ñ\8fңаÑ\80Ñ\82Ñ\83 Ñ\82Ñ\83Ñ\80Ñ\8bнда Ñ\85Ó\99бÓ\99Ñ\80 Ð¸Ñ\82еÑ\87е Ñ\85аÑ\82 Ñ\8fÑ\81алдÑ\8b, Ð»Ó\99кин Ð°Ð½Ñ\8b  {{GENDER:$2|кÑ\83лланÑ\83Ñ\87Ñ\8bга}} Ñ\82үбÓ\99ндÓ\99ге Ñ\81Ó\99бÓ\99п Ð°Ñ\80каÑ\81Ñ\8bнда Ò\97ибÓ\99Ñ\80е Ð±Ñ\83лмадÑ\8b$1",
        "changeemail": "Электрон әрҗә адресын үзгәртү яисә бетерү",
-       "changeemail-header": "Электрон әрҗә адресын үзгәртү",
+       "changeemail-header": "Электрон әрҗә адресын үзгәртү өчен әлеге форманы тутырыгыз. Әгәрдә сез электрон әрҗәгезне хисап язмагыздан өзәсегез килмәсә, форманы тутырганда яңа адрес урынын буш калдырыгыз.",
+       "changeemail-passwordrequired": "Әлеге үзгәрешләрне раслау өчен, Сезгә кулланылучы серсүзне язарга кирәк.",
        "changeemail-no-info": "Бу сәхифәгә турыдан-туры мөрәҗәгать итәр өчен, сез системага керергә тиешсез",
        "changeemail-oldemail": "Хәзерге электрон әрҗә адресы:",
        "changeemail-newemail": "Яңа электрон почта адресы:",
        "changeemail-none": "(юк)",
        "changeemail-password": "«{{SITENAME}}» проекты өчен серсүзегез:",
        "changeemail-submit": "E-mail адресын үзгәртү",
+       "resettokens": "Токеннарны ташлау",
+       "resettokens-tokens": "Токеннар:",
+       "resettokens-token-label": "$1 (агымдагы мәгънә: $2)",
        "bold_sample": "Калын язылыш",
        "bold_tip": "Калын язылыш",
        "italic_sample": "Курсив язылыш",
        "nowiki_tip": "Вики-форматлауны исәпкә алмау",
        "image_sample": "Мисал.jpg",
        "image_tip": "Куелган файл",
-       "media_tip": "Ð\9cедиа-Ñ\84айлга сылтама",
+       "media_tip": "Файлга сылтама",
        "sig_tip": "Имза һәм вакыт",
        "hr_tip": "Горизонталь сызык (еш кулланмагыз)",
        "summary": "Үзгәртүләр тасвирламасы:",
        "anonpreviewwarning": "''Сез системада теркәлмәдегез.Сезнең тарафтан эшләнгән барлык үзгәртүләр дә сезнең IP-юлламагызны саклауга китерә.''",
        "missingsummary": "'''Искәртү.''' Сез үзгәртүгә кыскача тасвирлау язмадыгыз. Сез «Битне саклау» төймәсенә тагын бер тапкыр бассагыз, үзгәртүләр тасвирламасыз сакланачак.",
        "missingcommenttext": "Аска тасвирлама язуыгыз сорала.",
-       "missingcommentheader": "''Искәртү:''' Сез тасвирламага исем бирмәдегез.\n«{{int:savearticle}}» төймәсенә кабат бассагыз, үзгәртүләр исемсез язылачак.",
+       "missingcommentheader": "<strong>Искәртү:</strong> Сез шәрехнең темасын күрсәтмәгәнсез.\n«{{int:savearticle}}» төймәсенә кабат бассагыз, үзгәртүләр темасыз язылачак.",
        "summary-preview": "Тасвирламаны алдан карау:",
        "subject-preview": "Башисемне болай булачак:",
        "blockedtitle": "Кулланучы тыелды",
        "loginreqlink": "керергә",
        "loginreqpagetext": "Сез башка битләр карау өчен $1 тиеш.",
        "accmailtitle": "Серсүз җибәрелде.",
-       "accmailtext": "[[User talk:$1|$1]] ÐºÑ\83лланÑ\83Ñ\87Ñ\8bÑ\81Ñ\8b Ó©Ñ\87ен Ñ\82өзелгÓ\99н Ñ\81еÑ\80Ñ\81үз $2 Ð°Ð´Ñ\80еÑ\81Ñ\8bна Ò\97ибÓ\99Ñ\80елде.\n\nСайÑ\82ка ÐºÐµÑ\80гÓ\99Ñ\87 сез ''[[Special:ChangePassword|серсүзегезне үзгәртә аласыз]]''.",
+       "accmailtext": "[[User talk:$1|$1]] ÐºÑ\83лланÑ\83Ñ\87Ñ\8bÑ\81Ñ\8b Ó©Ñ\87ен Ñ\82өзелгÓ\99н Ñ\81еÑ\80Ñ\81үз $2 Ð°Ð´Ñ\80еÑ\81Ñ\8bна Ò\97ибÓ\99Ñ\80елде.\n\nÐ\90вÑ\82оÑ\80изаÑ\86иÑ\8f Ñ\83згаÑ\87, Ò¯Ð· Ñ\85иÑ\81ап Ñ\8fзмагÑ\8bзда сез ''[[Special:ChangePassword|серсүзегезне үзгәртә аласыз]]''.",
        "newarticle": "(Яңа)",
        "newarticletext": "Сез әлегә язылмаган биткә кердегез.\nЯңа бит ясау өчен астагы тәрәзәдә мәкалә текстын җыегыз ([$1 ярдәм битен] карый аласыз).\nӘгәр сез бу биткә ялгышлык белән эләккән булсагыз, браузерыгызның '''артка''' төймәсенә басыгыз.",
        "anontalkpagetext": "----''Бу бәхәс бите системада теркәлмәгән яисә үз исеме белән кермәгән кулланучыныкы.\nАны тану өчен IP адресы файдаланыла.\nӘгәр сез аноним кулланучы һәм сезгә юлланмаган хәбәрләр алдым дип саныйсыз икән (бер IP адресы күп кулланучы өчен булырга мөмкин), башка мондый аңлашылмаучанлыклар килеп чыкмасын өчен [[Special:UserLogin|системага керегез]] яисә [[Special:UserLogin/signup|теркәлегез]].''",
        "userpage-userdoesnotexist": "«<nowiki>$1</nowiki>» исемле хисап язмасы юк. Сез чынлап та бу битне ясарга яисә үзгәртергә телисезме?",
        "userpage-userdoesnotexist-view": "\"$1\" исемле хисап язмасы юк.",
        "blocked-notice-logextract": "Бу кулланучы хәзергә тыелды.\nТүбәндә тыю көндәлегенең соңгы язу бирелгән:",
-       "clearyourcache": "'''Искәрмә:''' Сез саклаган үзгәртүләр кулланышка керсен өчен браузерыгызның кешын чистартырга туры киләчәк. \n* '''Firefox/Safari''': Shift төймшсенә баскан килеш җиһазлар тасмасында ''Яңарту (Обновить)'' язуына басыгыз, яисә ''Ctrl-F5'' яки  ''Ctrl-R'' (Mac өчен ''Command-R'') төймәләренә басыгыз\n* '''Google Chrome.'''  ''Ctrl-Shift-R'' (Mac өчен ''Command-Shift-R'' ) төймәләренә басыгыз\n* '''Internet Explorer.''' ''Ctrl''  төймәсенә баскан килеш  ''Яңарту (Обновить)'' язуына, яисә ''Ctrl-F5'' басыгыз\n* '''Konqueror.''' ''Яңарту (Обновить)'' язуына, яисә ''F5'' басыгыз\n* '''Opera.''' Менюдан кеш чистартуны сайлагыз: ''Җиһазлар (Инструменты) → Көйләнмәләр (Настройки)''",
+       "clearyourcache": "<strong>Искәрмә:</strong> Сез саклаган үзгәртүләр кулланышка керсен өчен браузерыгызның кешын чистартырга туры киләчәк. \n* <strong>Firefox/Safari:</strong> Shift төймшсенә баскан килеш җиһазлар тасмасында <em>Яңарту (Обновить)</em> язуына басыгыз, яисә <em>Ctrl-F5</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>",
        "usercssyoucanpreview": "'''Ярдәм:''' \"{{int:showpreview}} төймәсенә басып, яңа CSS-файлны тикшереп була.",
        "userjsyoucanpreview": "'''Ярдәм:''' \"{{int:showpreview}}\" төймәсенә басып, яңа JS-файлны тикшереп була.",
        "usercsspreview": "'''Бу бары тик CSS-файлны алдан карау гына, ул әле сакланмаган!'''",
        "userinvalidcssjstitle": "'''Игътибар:''' \"$1\" бизәү темасы табылмады. Кулланучының .css һәм .js битләре исемнәре бары тик кечкенә (юл) хәрефләрдән генә торырга тиеш икәнен онытмагыз. Мисалга: {{ns:user}}:Foo/vector.css, ә {{ns:user}}:Foo/Vector.css түгел!",
        "updated": "(Яңартылды)",
        "note": "'''Искәрмә:'''",
-       "previewnote": "'''Бу фәкать алдан карау гына, үзгәртүләрегез әле сакланмаган!'''",
+       "previewnote": "<strong>Исегездә тотыгыз, бу алдан карау гына.</strong>\nТәзәтмәләрегез әлегә сакланмаган!",
+       "continue-editing": "Үзгәртүне дәвам итү",
        "previewconflict": "Әлеге алдан карау битендә сакланачак текстның ничек күренәчәге күрсәтелә.",
        "session_fail_preview": "'''Кызганычка, сезнең сессия идентификаторыгыз югалды. Нәтиҗәдә сервер үзгәртүләрегезне кабул итә алмый.\nТагын бер тапкыр кабатлавыгыз сорала.\nБу хата тагын кабатланса, [[Special:UserLogout|чыгыгыз]] һәм яңадан керегез.'''",
        "session_fail_preview_html": "'''Кызганычка, сезнең сессия турында мәгълүматлар югалды. Нәтиҗәдә сервер үзгәртүләрегезне кабул итә алмый.'''\n\n''{{SITENAME}} чиста HTML кулланырга рөхсәт итә, ә бу үз чиратында JavaScript-атакалар оештыру өчен кулланылырга мөмкин. Шул сәбәпле сезнең өчен алдан карау мөмкинлеге ябык.''\n\n'''Әгәр сез үзгәртүне яхшы ният белән башкарасыз икән, тагын бер тапкыр кабатлап карагыз. Хата кабатланса, сайттан [[Special:UserLogout|чыгыгыз]] һәм яңадан керегез.'''",
        "yourdiff": "Аермалар",
        "copyrightwarning": "Бөтен өстәмәләр һәм үзгәртүләр $2 (карагыз: $1) лицензиясе шартларында башкарыла дип санала.\nӘгәр аларның ирекле таратылуын һәм үзгәртелүен теләмәсәгез, монда өстәмәвегез сорала.<br />\nСез өстәмәләрнең авторы булырга яисә мәгълүматның ирекле чыганаклардан алынуын күрсәтергә тиеш.<br />\n'''МАХСУС РӨХСӘТТӘН БАШКА АВТОРЛЫК ХОКУКЫ БУЕНЧА САКЛАНУЧЫ МӘГЪЛҮМАТЛАР УРНАШТЫРМАГЫЗ!'''",
        "copyrightwarning2": "Сезнең үзгәртүләр башка кулланучылар тарафыннан үзгәртелә яисә бетерелә ала.\nӘгәр аларның үзгәртелүен теләмәсәгез, монда өстәмәвегез сорала.<br />\nСез өстәмәләрнең авторы булырга яисә мәгълүматның ирекле чыганаклардан алынуын күрсәтергә тиеш (карагыз: $1).\n'''МАХСУС РӨХСӘТТӘН БАШКА АВТОРЛЫК ХОКУКЫ БУЕНЧА САКЛАНУЧЫ МӘГЪЛҮМАТЛАР УРНАШТЫРМАГЫЗ!'''",
-       "longpageerror": "'''ХАТА: сакланучы текст зурлыгы - $1 килобайт, бу $2 килобайт чигеннән күбрәк. Бит саклана алмый.'''",
-       "readonlywarning": "'''Кисәтү: мәгълүматлар базасында техник эшләр башкарыла, сезнең үзгәртүләр хәзер үк саклана алмый.\nТекст югалмасын өчен аны компьютерыгызга саклап тора аласыз.'''\n\nИдарәче күрсәткән сәбәп: $1",
+       "longpageerror": "<strong>ХАТА: сакланучы текст зурлыгы - $1 {{PLURAL:$1|килобайт}}, бу $2 {{PLURAL:$2|килобайт}} чигеннән күбрәк. Бит саклана алмый.</strong>",
+       "readonlywarning": "<strong>Кисәтү: мәгълүматлар базасында техник эшләр башкарыла, сезнең үзгәртүләр хәзер үк саклана алмый.</strong>\nБез сезгә әлеге текстны, югалмас өчен, берәр файлга сакларга тәкъдим итәбез.\n\nМәгълүматлар базасын япкан идарәче күрсәткән сәбәп: $1",
        "protectedpagewarning": "'''Кисәтү: сез бу битне үзгәртә алмыйсыз, бу хокукка идарәчеләр гына ия.'''\nТүбәндә көндәлекнең  соңгы язуы бирелгән:",
        "semiprotectedpagewarning": "'''Кисәтү:''' бу бит якланган. Аны теркәлгән кулланучылар гына үзгәртә ала.\nАста бу битне күзәтү көндәлеге бирелгән:",
-       "cascadeprotectedwarning": "'''Кисәтү:''' Бу битне идарәчеләр гына үзгәртә ала. Сәбәбе: ул {{PLURAL:$1|каскадлы яклау исемлегенә кертелгән}}:",
+       "cascadeprotectedwarning": "<strong>Кисәтү:</strong> Бу битне идарәчеләр гына үзгәртә ала., чөнки бит {{PLURAL:$1|каскадлы яклау исемлегенә кертелгән}}:",
        "titleprotectedwarning": "'''Кисәтү: Мондый исемле бит якланган, аны үзгәртү өчен [[Special:ListGroupRights|тиешле хокукка]] ия булу зарур.'''\nАста күзәтү көндәлегендәге соңгы язма бирелгән:",
        "templatesused": "Бу биттә кулланылган {{PLURAL:$1|1=калып|калыплар}} :",
-       "templatesusedpreview": "Алдан каралучы биттә кулланылган {{PLURAL:$1|1=үрнәк|үрнәкләр}}:",
-       "templatesusedsection": "Бу бүлектә кулланылган {{PLURAL:$1|1=үрнәк|үрнәкләр}}:",
+       "templatesusedpreview": "Алдан карау мөмкинлегендә кулланылган {{PLURAL:$1|1=калып|калыплар}}:",
+       "templatesusedsection": "Бу бүлектә кулланылган {{PLURAL:$1|1=калып|калыплар}}:",
        "template-protected": "(якланган)",
        "template-semiprotected": "(өлешчә якланган)",
        "hiddencategories": "Бу бит $1 {{PLURAL:$1|яшерен төркемгә|$1 яшерен төркемнәргә}} керә:",
        "edit-gone-missing": "Битне яңартып булмый.\nУл бетерелгән булырга мөмкин.",
        "edit-conflict": "Үзгәртүләр конфликты.",
        "edit-no-change": "Текстта үзгәешләр ясалмау сәбәпле, сезнең үзгәртү кире кагыла.",
+       "postedit-confirmation-created": "Бит төзелде",
+       "postedit-confirmation-saved": "Төзәтмәгез сакланды.",
        "edit-already-exists": "Яңа бит төзеп булмый.\nУл инде бар.",
-       "editwarning-warning": "Башка биткә күчү вакытында бу мәкаләгә керткән үзгәрешләр югалырга мөмкин.\nӘгәрдә сез теркәлгән булсагыз, бу искәрмәне сез «Көйләнмәләрем» өлешендә үзгәртә аласыз.",
+       "editwarning-warning": "Башка биткә күчү вакытында бу мәкаләгә керткән үзгәрешләр югалырга мөмкин.\nӘгәрдә сез теркәлгән булсагыз, бу искәрмәне сез көйләнмәләрегезнең «{{int:prefs-editing}}» бүлегендә үзгәртә аласыз.",
+       "content-model-wikitext": "викитекст",
+       "content-model-text": "гади текст",
+       "content-model-javascript": "JavaScript",
+       "content-json-empty-object": "Буш объект",
+       "content-json-empty-array": "Буш массив",
        "duplicate-args-category": "Калыпны чакыруда кабатлап торган аргументларны кулланган битләр",
        "expensive-parserfunction-warning": "<strong>Игътибар:</strong>  бу биттә хәтерне еш кулланучы функцияләр артык күп.\n\nБиттә {{PLURAL:$2|$2 эш куллану}} рөхсәт ителгән очракта, монда $1 {{PLURAL:$1|эш башкарыла}}.",
        "expensive-parserfunction-category": "Хәтерне еш кулланучы функцияләр күп булган битләр",
        "rev-showdeleted": "күрсәтү",
        "revisiondelete": "Битнең юрамасын бетерү / кайтару",
        "revdelete-nooldid-title": "Ахыргы юрама билгеләнмәгән",
-       "revdelete-nooldid-text": "Ð\91Ñ\83 Ñ\84Ñ\83нкÑ\86иÑ\8fне Ð±Ð°Ñ\88каÑ\80Ñ\83 Ó©Ñ\87ен Ñ\81ез Ð°Ñ\85Ñ\8bÑ\80гÑ\8b Ñ\8eÑ\80аманÑ\8b (Ñ\8fки Ñ\8eÑ\80амалаÑ\80нÑ\8b) Ð±Ð¸Ð»Ð³ÐµÐ»Ó\99мÓ\99дегез.",
+       "revdelete-nooldid-text": "Ð\91Ñ\83 Ñ\84Ñ\83нкÑ\86иÑ\8fне Ð±Ð°Ñ\88каÑ\80Ñ\83 Ó©Ñ\87ен Ñ\81ез Ð°Ñ\85Ñ\8bÑ\80гÑ\8b Ñ\8eÑ\80аманÑ\8b (Ñ\8fки Ñ\8eÑ\80амалаÑ\80нÑ\8b) Ð±Ð¸Ð»Ð³ÐµÐ»Ó\99мÓ\99гÓ\99нÑ\81ез, Ó\99леге Ñ\8eÑ\80ама Ñ\8eк, Ñ\8fиÑ\81Ó\99 Ñ\81ез Ð±Ó©Ñ\82енлÓ\99й Ð±Ñ\83 Ñ\8eÑ\80аманÑ\8b Ñ\8fÑ\88еÑ\80мÓ\99кÑ\87е Ð±Ñ\83лаÑ\81Ñ\8bз.",
        "revdelete-no-file": "Бу файл юк.",
        "revdelete-show-file-confirm": "Сез чыннан да «<nowiki>$1</nowiki>» файлының бетерелгән  $2, $3 версиясен карарга телисезме??",
        "revdelete-show-file-submit": "Әйе",
-       "logdelete-selected": "Ð\96Ñ\83Ñ\80налнÑ\8bÒ£ {{PLURAL:$1|1=Сайланган Ñ\8fзма|Ñ\81айланган Ñ\8fзмалаÑ\80Ñ\8b}} :",
+       "logdelete-selected": "Ð\9aөндÓ\99лекнең {{PLURAL:$1|1=Ñ\81айланган Ñ\8fзмаÑ\81Ñ\8b\81айланган Ñ\8fзмалаÑ\80Ñ\8b}}:",
        "revdelete-legend": "Чикләүләр урнаштыр:",
        "revdelete-hide-text": "Үзгәртү тексты",
        "revdelete-hide-image": "Файл эчендәгеләрне качыр",
        "notextmatches": "Тиңдәш текстлы битләр юк",
        "prevn": "алдагы {{PLURAL:$1|$1}}",
        "nextn": "чираттагы {{PLURAL:$1|$1}}",
+       "prev-page": "алдагы бит",
+       "next-page": "киләсе бит",
        "prevn-title": "Алдагы $1  {{PLURAL:$1|язма}}",
        "nextn-title": "{{PLURAL:$1|Киләсе $1 язма}}",
-       "shown-title": "Сәхифәдә $1 {{PLURAL:$1|1=язма|язма}} күрсәтелсен",
+       "shown-title": "Сәхифәдә $1 {{PLURAL:$1|язма}} күрсәтелсен",
        "viewprevnext": "Күрсәтелүе: ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "<strong>Бу вики-проектта «[[:$1]]» исемле бит бар инде</strong>{{PLURAL:$2|0=|Башка эзләү нәтиҗәләрен дә карап ал.}}",
        "searchmenu-new": "<strong>«Әлеге [[:$1]]» вики-проектта бит ясарга!</strong>\n{{PLURAL:$2|0=|Шулай ук, эзләү ярдәмендә табылган битне карагыз.|Шулай ук, эзләү ярдәмендә табылган битләрне карагыз.}}",
        "searchprofile-images-tooltip": "Файллар эзләү",
        "searchprofile-everything-tooltip": "Барлык битләрдән эзләү",
        "searchprofile-advanced-tooltip": "Бирелгән исемнәр мәйданында эзләү",
-       "search-result-size": "$1 ({{PLURAL:$2|1 сүз|$2 сүз}})",
-       "search-result-category-size": "{{PLURAL:$1|1=1 әгъза|$1 әгъза}} ({{PLURAL:$2|1=1 асттөркем|$2 асттөркем}}, {{PLURAL:$3|1=1 файл|$3 файл}})",
+       "search-result-size": "$1 ({{PLURAL:$2|$2 сүз}})",
+       "search-result-category-size": "$1 {{PLURAL:$1|әгъза}} ($2 {{PLURAL:$2|астөркем}}, $3 {{PLURAL:$3|файл}})",
        "search-redirect": "(юнәлтү $1)",
        "search-section": "($1 бүлеге)",
        "search-category": "($1 категориясе)",
        "prefs-watchlist-token": "Күзәтү исемлеге токены:",
        "prefs-misc": "Башка көйләнмәләр",
        "prefs-resetpass": "Серсүзне үзгәртү",
+       "prefs-changeemail": "Электрон әрҗә адресын үзгәртү яисә бетерү",
        "prefs-email": "E-mail көйләнмәләре",
        "prefs-rendering": "Күренеш",
        "saveprefs": "Саклау",
        "columns": "Баганалар:",
        "searchresultshead": "Эзләү",
        "stub-threshold": "Ясалма сылтамаларның бизәлеше буенча чикләүләр ($1):",
+       "stub-threshold-sample-link": "мисал",
        "stub-threshold-disabled": "Ябылган",
        "recentchangesdays": "Соңгы үзгәртүләрне күрсәтүче көннәр саны:",
        "recentchangesdays-max": "(иң күбе $1 {{PLURAL:$1|көн}})",
        "prefs-dateformat": "Вакытың форматы",
        "prefs-timeoffset": "Вакыт билгеләнеше",
        "prefs-advancedediting": "Гомуми көйләүләр",
+       "prefs-editor": "Мөхәррир",
+       "prefs-preview": "Алдан карау",
        "prefs-advancedrc": "Киңәйтелгән көйләүләр",
        "prefs-advancedrendering": "Киңәйтелгән көйләүләр",
        "prefs-advancedsearchoptions": "Киңәйтелгән көйләүләр",
        "prefs-advancedwatchlist": "Киңәйтелгән көйләүләр",
        "prefs-displayrc": "Күрсәтү көйләнмәләре",
        "prefs-displaywatchlist": "Күрсәтү көйләнмәләре",
+       "prefs-tokenwatchlist": "Токен",
        "prefs-diffs": "Юрамалар аермасы",
        "userrights": "Кулланучы хокуклары белән идарә итү",
        "userrights-lookup-user": "Кулланучы төркемнәре белән идарә итү",
        "right-suppressredirect": "Элекке исемнән юнәлтү ясамыйча исемне алмаштыру",
        "right-upload": "файлларны йөкләү",
        "right-reupload": "Булган файллар өстеннән язарга",
-       "right-writeapi": "язма өчен API куллану",
+       "right-writeapi": "Язма өчен API куллану",
        "right-delete": "битләрне бетерү",
        "right-editinterface": "Кулланучы интерфейсын үзгәртү",
        "newuserlogpage": "Кулланучыларны теркәү көндәлеге",
        "enhancedrc-history": "тарих",
        "recentchanges": "Соңгы үзгәртүләр",
        "recentchanges-legend": "Соңгы үзгәртүләр көйләүләре",
-       "recentchanges-summary": "Ð\91Ñ\83 Ð±Ð¸Ñ\82Ñ\82Ó\99 {{grammar:genitive|{{SITENAME}}}} Ð¿Ñ\80оекÑ\82Ñ\8bнÑ\8bÒ£ Ñ\81оңгÑ\8b Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үлÓ\99Ñ\80е ÐºÒ¯Ñ\80Ñ\81Ó\99Ñ\82елÓ\99.",
+       "recentchanges-summary": "ТөÑ\80ле Ð±Ð¸Ñ\82лÓ\99Ñ\80дÓ\99 Ñ\8dÑ\88лÓ\99нгÓ\99н Ñ\81оңгÑ\8b Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үлÓ\99Ñ\80 Ð¸Ñ\81емлеге.",
        "recentchanges-feed-description": "Бу агымда соңгы үзгәртүләрне күзәтү.",
        "recentchanges-label-newpage": "Бу үзгәртү белән яңа бит төзелгән",
        "recentchanges-label-minor": "Бу кече үзгәртү",
        "recentchanges-label-bot": "Бу үзгәртү бот белән эшләнгән",
        "recentchanges-label-unpatrolled": "Үзгәртүне әлегә тикшермәгәннәр",
        "recentchanges-label-plusminus": "Битнең зурлыгы шуның кадәрле байтка үзгәрде",
-       "recentchanges-legend-heading": "'''Легенда:&nbsp;'''",
-       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|яңа бит]])",
+       "recentchanges-legend-heading": "'''Аңлатма:&nbsp;'''",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шулай ук [[Special:NewPages|яңа битләр исемлеген]] карагыз)",
+       "recentchanges-submit": "Күрсәт",
        "rcnotefrom": "Астарак <strong>$3, $4</strong> өчен {{PLURAL:$5|үзгәртүләр күрсәтелгән}} (<strong>$1</strong> артык түгел).",
        "rclistfrom": "$3 $2 башлап яңа үзгәртүләрне күрсәт",
        "rcshowhideminor": "кече үзгәртүләрне $1",
-       "rcshowhideminor-show": "күÑ\80Ñ\81Ó\99Ñ\82",
-       "rcshowhideminor-hide": "яшер",
+       "rcshowhideminor-show": "Ð\9aÒ¯Ñ\80Ñ\81Ó\99Ñ\82Ò¯",
+       "rcshowhideminor-hide": "Яшер",
        "rcshowhidebots": "ботларны $1",
        "rcshowhidebots-show": "Күрсәт",
-       "rcshowhidebots-hide": "яшер",
+       "rcshowhidebots-hide": "Яшер",
        "rcshowhideliu": "теркәлгән кулланучыларны $1",
-       "rcshowhideliu-show": "күрсәт",
-       "rcshowhideliu-hide": "яшер",
+       "rcshowhideliu-show": "Ð\9aүрсәт",
+       "rcshowhideliu-hide": "Яшер",
        "rcshowhideanons": "кермәгән кулланучыларны $1",
-       "rcshowhideanons-show": "күÑ\80Ñ\81Ó\99Ñ\82",
-       "rcshowhideanons-hide": "яшер",
+       "rcshowhideanons-show": "Ð\9aÒ¯Ñ\80Ñ\81Ó\99Ñ\82Ò¯",
+       "rcshowhideanons-hide": "Яшер",
        "rcshowhidepatr": "тикшерелгән үзгәртүләрне $1",
+       "rcshowhidepatr-show": "Күрсәтү",
        "rcshowhidepatr-hide": "яшер",
        "rcshowhidemine": "минем үзгәртүләремне $1",
-       "rcshowhidemine-show": "күрсәт",
-       "rcshowhidemine-hide": "яшер",
-       "rclinks": "Соңгы $2 көн эчендә соңгы $1 үзгәртүне күрсәт<br />$3",
+       "rcshowhidemine-show": "Күрсәтү",
+       "rcshowhidemine-hide": "Яшер",
+       "rcshowhidecategorization-show": "Күрсәт",
+       "rclinks": "Соңгы $2 көн эчендә ясалган $1 үзгәртүне күрсәт<br />$3",
        "diff": "аерма",
        "hist": "тарих",
-       "hide": "яшер",
-       "show": "күрсәт",
+       "hide": "Яшер",
+       "show": "Ð\9aүрсәт",
        "minoreditletter": "к",
        "newpageletter": "Я",
        "boteditletter": "б",
        "recentchangeslinked-feed": "Бәйләнешле үзгәртүләр",
        "recentchangeslinked-toolbox": "Бәйләнешле үзгәртүләр",
        "recentchangeslinked-title": "\"$1\" битенә бәйләнешле үзгәртүләр",
-       "recentchangeslinked-summary": "Бу күрсәтелгән бит белән сылталган (йә күрсәтелгән төркемгә керткән) битләрнең үзгәртелмәләре исемлеге.\n[[Special:Watchlist|Күзәтү исемлегегезгә]] керә торган битләр '''калын'''.",
+       "recentchangeslinked-summary": "Бу күрсәтелгән бит белән сылталган (йә күрсәтелгән төркемгә керткән) битләрнең үзгәртелмәләре исемлеге.\n[[Special:Watchlist|Күзәтү исемлегегезгә]] керә торган битләр '''калын''' итеп күрсәтелгән.",
        "recentchangeslinked-page": "Битнең исеме:",
        "recentchangeslinked-to": "Моның урынына бу биткә бәйле булган битләрдәге үзгәртүләрне күрсәтү",
-       "upload": "Файлны йөкләү",
+       "upload": "Файл йөкләү",
        "uploadbtn": "Файлны йөкләү",
        "reuploaddesc": "Файлны йөкләүгә кире кату",
        "upload-tryagain": "Яңартылган файлны җибәрү",
        "upload_directory_read_only": "Моңа Сезнең хокукларыгыз юк һәм веб-сервер $1 папкасыны йөкли алмый.",
        "uploaderror": "Файлны йөкләүдә хата",
        "upload-recreate-warning": "'''Игътибар: Мондый исемле файл бетерелгән яки исеме алмаштырылган '''",
-       "uploadtext": "Бу форманы кулланып серверга файллар йөкли аласыз. Элегрәк йөкләнелгән файлларны карау өчен [[Special:FileList|йөкләнелгән файллар исемлегенә]] мәрәҗәгать итегез. Шулай ук ул [[Special:Log/upload|йөкләнмәләр исемлегенә]] һәм [[Special:Log/delete|бетерелгән файллар]] исемлегенә дә языла.\n\nФайлны мәкаләгә йөкләү өчен Сез менә бу үрнәкләрне куллана аласыз:\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Рәсем.jpg]]</nowiki></code>''' файлның тулы юрамасын кую өчен;\n* '''<code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Räsem.png|200px|thumb|left|тасвирламасы]]</nowiki></code>'''  200 пиксельга кадәр киңлектәге  һәм текстның сул ягында, тасвирламасы белән;\n* '''<code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:File.ogg]]</nowiki></code>'''биттә файлны сүрәтләмичә, бары тик сылтамасын гына кую.",
+       "uploadtext": "Бу форманы кулланып серверга файллар йөкли аласыз. \nЭлегрәк йөкләнелгән файлларны карау өчен [[Special:FileList|йөкләнелгән файллар исемлегенә]] мәрәҗәгать итегез. Шулай ук ул [[Special:Log/upload|йөкләнмәләр исемлегенә]] һәм [[Special:Log/delete|бетерелгән файллар]] исемлегенә дә языла.\n\nФайлны мәкаләгә йөкләү өчен Сез менә бу үрнәкләрне куллана аласыз:\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Рәсем.jpg]]</nowiki></code></strong> — файлның тулы юрамасын кую өчен;\n* <strong><code><nowiki>[[</nowiki>{{ns:file}}<nowiki>:Рәсем.png|200px|thumb|left|тасвирламасы]]</nowiki></code></strong> — 200 пиксельга кадәр киңлектәге  һәм текстның сул ягында, тасвирламасы белән;\n* <strong><code><nowiki>[[</nowiki>{{ns:media}}<nowiki>:Файл.ogg]]</nowiki></code></strong> — биттә файлны сүрәтләмичә, бары тик сылтамасын гына кую.",
        "upload-permitted": "{{PLURAL:$2|Рөхсәт ителгән файл төрләре}}: $1.",
        "upload-preferred": "{{PLURAL:$2|Мөмкин булган файл төрләре}}: $1.",
        "upload-prohibited": "{{PLURAL:$2|Тыелган файл төрләре}}: $1.",
        "listfiles_count": "Юрамалар",
        "file-anchor-link": "Файл",
        "filehist": "Файлның тарихы",
-       "filehist-help": "Ð\94аÑ\82ага/Ñ\81Ó\99гаÑ\82Ñ\8cкÓ\99, Ñ\88Ñ\83л Ð²Ð°ÐºÑ\8bÑ\82Ñ\82а Ð±Ð¸Ñ\82нең Ð½Ð¸Ð½Ð´Ð¸ Ð±Ñ\83лганлÑ\8bгÑ\8bн ÐºÒ¯Ñ\80Ò¯ Ó©Ñ\87ен басыгыз.",
+       "filehist-help": "ФайлнÑ\8bÒ£ Ð½Ð¸Ð½Ð´Ð¸ Ð±Ñ\83лганлÑ\8bгÑ\8bн ÐºÒ¯Ñ\80Ò¯ Ó©Ñ\87ен Ð´Ð°Ñ\82ага/Ñ\81Ó\99гаÑ\82Ñ\8cкÓ\99 басыгыз.",
        "filehist-deleteall": "Барысын да юк ит",
        "filehist-deleteone": "бетерү",
        "filehist-revert": "кайтару",
        "filehist-current": "хәзерге",
        "filehist-datetime": "Дата/вакыт",
        "filehist-thumb": "Миниатюра",
-       "filehist-thumbtext": "$1 көнне булган версиянең эскизы",
+       "filehist-thumbtext": "$1 көнне булган юрама эскизы",
        "filehist-nothumb": "Миниатюрасы юк",
        "filehist-user": "Кулланучы",
        "filehist-dimensions": "Зурлык",
        "linkstoimage": "{{PLURAL:$1|Киләсе $1 бит|Киләсе $1 битләр|}} әлеге файлга сылтама ясый:",
        "nolinkstoimage": "Бу файлга сылтаган битләр юк.",
        "duplicatesoffile": "{{PLURAL:$1|Әлеге $1 файл }} астагы файлның күчерелмәсе булып тора ([[Special:FileDuplicateSearch/$2|тулырак]]):",
-       "sharedupload": "Бу файл $1'дан һәм башка проектларда кулланырга мөмкин.",
-       "sharedupload-desc-here": "Бу файл $1'дан һәм башка проектларда кулланырга мөмкин. Файл турында [$2 мәгълүмат ] аста бирелгән.",
+       "sharedupload": "Бу файл $1 проектыннан һәм башка проектларда кулланырга мөмкин",
+       "sharedupload-desc-here": "Бу файл $1 проектыннан һәм башка проектларда кулланырга мөмкин. \nФайл турында [$2 тулырак мәгълүмат] түбәндәрәк күрсәтелгән.",
        "filepage-nofile": "Мондый исемле файл юк.",
        "filepage-nofile-link": "Мондый исемле файл  юк. Сез аны [$1 йөкли аласыз].",
        "uploadnewversion-linktext": "Бу файлның яңа юрамасын йөкләү",
        "mostimages": "Иң кулланган сүрәтләр",
        "mostrevisions": "Күп үзгәртүләр белән битләр",
        "prefixindex": "Барлык алкушымча белән битләр",
+       "prefixindex-submit": "Күрсәт",
        "shortpages": "Кыска битләр",
        "longpages": "Озын битләр",
        "deadendpages": "Тупик битләре",
        "listusers": "Кулланучылар исемлеге",
        "usercreated": "$3 $1 көнне $2 вакытта {{GENDER:$3|теркәлде}}",
        "newpages": "Яңа битләр",
+       "newpages-submit": "Күрсәт",
        "newpages-username": "Кулланучы:",
        "ancientpages": "Иң иске битләр",
        "move": "Күчерү",
        "specialloguserlabel": "Башкаручы:",
        "speciallogtitlelabel": "Максат (атама яисә {{ns:user}}:кулланучы исеме):",
        "log": "Көндәлекләр",
+       "logeventslist-submit": "Күрсәт",
        "all-logs-page": "Барлык көндәлекләр",
        "alllogstext": "{{SITENAME}} сәхифәсенең гомуми көндәлекләре исемлеге.\nСез нәтиҗәләрне көндәлек төре, кулланучы исеме (хәреф зурлыгын истә тотыгыз) яки куззаллаган бит (шулай ук хәреф зурлыгын истә тотыгыз) буенча тәртипкә салырга мөмкин.",
        "logempty": "Кирәкле язмалар көндәлектә юк.",
        "allpagesprefix": "Алкушымчалы битләрне күрсәтү:",
        "allpages-hide-redirects": "Юнәлтүләрне яшер",
        "categories": "Төркемнәр",
+       "categories-submit": "Күрсәт",
        "categoriespagetext": "{{PLURAL:$1|1=Әлеге төркем үз өченә|Әлеге төркемнәр  үз өченә}}   битләрне һәм медиа-файлларны ала.\nАста [[Special:UnusedCategories|кулланылмаган төркемнәр]] кәрсәтелгән.\nШулай ук  [[Special:WantedCategories|кирәкле төркемнәр исемлегендә]] карагыз.",
        "special-categories-sort-count": "исәп буенча тәртипләү",
        "special-categories-sort-abc": "әлифба буенча тәртипләү",
        "activeusers-hidesysops": "Идарәчеләрне яшер",
        "activeusers-noresult": "Кулланучылар табылмады.",
        "listgrouprights": "Кулланучы төркемнәренең хокуклары",
+       "listgrouprights-key": "Легенда:\n* <span class=\"listgrouprights-granted\">Бирелгән хокуклар</span>\n* <span class=\"listgrouprights-revoked\">Алынган хокуклар</span>",
        "listgrouprights-group": "Төркем",
        "listgrouprights-rights": "Хокуклар",
        "listgrouprights-helppage": "Help:Төркемнәрнең хокуклары",
        "emailtarget": "Кулланучы-хатны алучының исемен языгыз",
        "emailusername": "Кулланучы исеме:",
        "emailusernamesubmit": "Җибәрү",
+       "email-legend": "{{SITENAME}} проектының башка кулланучысына хат җибәрергә",
        "emailfrom": "Кемнән:",
        "emailto": "Кемгә:",
        "emailsubject": "Тема:",
        "emailmessage": "Хәбәр:",
        "emailsend": "Җибәрү",
-       "emailccme": "Миңа хәбәрнең күчермәсене җибәрелсен.",
+       "emailccme": "Миңа хатның күчерелмәсе җибәрелсен.",
        "emailccsubject": "$1 өчен хәбәрегезнең күчермәсе: $2",
        "emailsent": "Хат җибәрелгән",
        "emailsenttext": "E-mail хатыгыз җиберелде.",
        "unwatchthispage": "Күзәтүне туктат",
        "notanarticle": "Мәкалә түгел",
        "watchlist-details": "Күзәтү исемлегегездә, бәхәс битләрен санамыйча, {{PLURAL:$1|$1 бит}} бар.",
+       "wlheader-enotif": "Электрон почта аша белдерүләр ачык.",
+       "wlheader-showupdated": "Сезнең соңгы төзәтмәләрдән соң үзгәргән битләр <strong>калын</strong> шрифт белән күрсәтелгән.",
+       "wlnote": "Түбәндә $3 $4 вакыт аралыгының {{PLURAL:$2|соңгы сәгатендә|соңгы <strong>$2</strong> сәгатендә}} ясалган {{PLURAL:$1|ахыргы төзәтмә|ахыргы <strong>$1</strong> төзәтмә}} күрсәтелгән.",
        "wlshowlast": "$1 сәгать $2 көн өчендә күрсәтү",
        "watchlistall2": "барлык",
+       "watchlist-submit": "Күрсәт",
+       "wlshowtime": "Күрсәтелүче вакыт аралыгы:",
+       "wlshowhideminor": "кече үзгәртүләр",
+       "wlshowhideanons": "аноним кулланучыларныкын",
        "watchlist-options": "Күзәтү исемлеге көйләүләре",
        "watching": "Күзәтү исемлегемә өстәүе…",
        "unwatching": "Күзәтү исемлегемнән чыгаруы…",
+       "enotif_reset": "Барлык битләрне каралган дип билгеләү",
        "enotif_impersonal_salutation": "{{SITENAME}} кулланучы",
        "enotif_lastvisited": "Соңгы керүегездән соң булган барлык үзгәртүләрне күрер өчен, бу сылтама аша узыгыз: $1",
        "enotif_body": "Хөрмәтле $WATCHINGUSERNAME,\n\n\n$PAGEINTRO $NEWPAGE\n\nҮзгәртүнең кыска эчтәлеге: $PAGESUMMARY $PAGEMINOREDIT\n\nҮзгәртүчегә язу:\nэл. почта $PAGEEDITOR_EMAIL\nвики $PAGEEDITOR_WIKI\n\nБу биткә кермәсәгез, аның башка үзгәртүләре турында хат җибәрелмәячәк. Шулай ук сез күзәтү исемлегегездә булган битләр өчен хәбәр бирү флагын алып куя аласыз.\n\n             {{grammar:genitive|{{SITENAME}}}} хәбәр бирү системасы\n\n--\nХәбәр итүләр көйләүләрен үзгәртү:\n{{canonicalurl:{{#special:Preferences}}}}\n\nКүзәтү исемлеге көйләүләрен үзгәртү:\n$HELPPAGE\n\nБитне сезнең күзәтү исемлегездән бетерү:\n$UNWATCHURL\n\nЭлемтә һәм ярдәм:\n$HELPPAGE",
        "delete-confirm": "«$1» бетерү",
        "delete-legend": "Бетерү",
        "historywarning": "<strong>Игътибар:</strong> Сез бетерергә теләгән биттә үзгәртү тарихы бар, ул $1 {{PLURAL:$1|юрамадан тора}}:",
+       "historyaction-submit": "Күрсәт",
        "confirmdeletetext": "Сез бу битнең (яки рәсемнең) тулысынча бетерелүен сорадыгыз.\nЗинһар, моны чыннан да эшләргә теләгәнегезне, моның нәтиҗәләрен аңлаганыгызны һәм [[{{MediaWiki:Policy-url}}]] бүлегендәге кагыйдәләр буенча эшләгәнегезне раслагыз.",
        "actioncomplete": "Гамәл башкарган",
        "actionfailed": "Эш башкарылмаган",
        "sp-contributions-talk": "бәхәс",
        "sp-contributions-search": "Кертемне эзләү",
        "sp-contributions-username": "Кулланучының IP адресы яки исеме:",
-       "sp-contributions-toponly": "Соңгы версия булган үзгәртүләрне генә күрсәтелсен",
+       "sp-contributions-toponly": "Соңгы юрамадагы үзгәртүләр генә күрсәтелсен",
+       "sp-contributions-newonly": "Битләр ясау үзгәртмәләрен генә күрсәтү",
        "sp-contributions-submit": "Эзләү",
        "whatlinkshere": "Бирегә нәрсә сылтый",
        "whatlinkshere-title": "$1 битенә сылтый торган битләр",
        "whatlinkshere-prev": "{{PLURAL:$1|1=алдагы}} $1",
        "whatlinkshere-next": "{{PLURAL:$1|1=киләсе}} $1",
        "whatlinkshere-links": "← сылтамалар",
-       "whatlinkshere-hideredirs": "юнәлтүләрне $1",
-       "whatlinkshere-hidetrans": "кертүләрне $1",
-       "whatlinkshere-hidelinks": "сылтамаларны $1",
+       "whatlinkshere-hideredirs": "Юнәлтүләрне $1",
+       "whatlinkshere-hidetrans": "Ð\9aертүләрне $1",
+       "whatlinkshere-hidelinks": "Сылтамаларны $1",
        "whatlinkshere-hideimages": "$1 файл сылтамалары",
        "whatlinkshere-filters": "Фильтрлар",
        "blockip": "{{GENDER:$1|Кулланучыны}} тыю",
        "unblocklink": "тыюдан азат итү",
        "change-blocklink": "тыюны үзгәртү",
        "contribslink": "кертем",
+       "emaillink": "хат язу",
        "blocklogpage": "Тыю көндәлеге",
        "blocklogentry": "[[$1]] $2 вакытка тыелды $3",
        "unblocklogentry": "$1 кулланучысының тыелу вакыты бетте",
        "move-page": "$1 — исемен алмаштыру",
        "move-page-legend": "Битне күчерү",
        "movepagetext": "Астагы форманы куллану битнең исемен алыштырып, аның барлык тарихын яңа исемле биткә күчерер.\nИске исемле бит яңа исемле биткә юнәлтү булып калыр.\nСез иске исемгә юнәлтүләрне автоматик рәвештә яңа исемгә күчерә аласыз.\nӘгәр моны эшләмәсәгез, [[Special:DoubleRedirects|икеле]] һәм [[Special:BrokenRedirects|өзелгән юнәлтүләрне]] тикшерегез.\nСез барлык сылтамаларның кирәкле җиргә сылтавына җаваплы.\n\nКүздә тотыгыз: әгәр яңа исем урынында бит булса инде, һәм ул буш яки юнәлтү түгел исә, бит <strong>күчерелмәячәк</strong>.\nБу шуны аңлата: сез ялгышып күчерсәгез, битне кайтара аласыз, әмма инде булган битне бетерә алмыйсыз.\n\n<strong>Игътибар!</strong>\nПопуляр битләрне күчерү зур һәм көтелмәгән нәтиҗәләргә китерә ала.\nДәвам иткәнче, барлык нәтиҗәләрне аңлавыгызны тагын бер кат уйлагыз.",
-       "movepagetalktext": "Ð\91Ñ\83 Ð±Ð¸Ñ\82нең Ð±Ó\99Ñ\85Ó\99Ñ\81 Ð±Ð¸Ñ\82е Ð´Ó\99 ÐºÒ¯Ñ\87еÑ\80елÓ\99Ñ\87Ó\99к, '''бÑ\83 Ð¾Ñ\87Ñ\80аклаÑ\80дан Ñ\82Ñ\8bÑ\88''':\n*Ð\90ндÑ\8bй Ð¸Ñ\81емле Ð±Ñ\83Ñ\88 Ð±Ñ\83лмаган Ð±Ó\99Ñ\85Ó\99Ñ\81 Ð±Ð¸Ñ\82е Ð±Ð°Ñ\80 Ð¸Ð½Ð´Ðµ, Ñ\8fиÑ\81Ó\99\n*Сез Ð°Ñ\81Ñ\82агÑ\8b Ñ\84лажокнÑ\8b ÐºÑ\83ймаганÑ\81Ñ\8bз.\n\nÐ\91Ñ\83 Ð¾Ñ\87Ñ\80аклаÑ\80да Ñ\81езгÓ\99 Ð±Ð¸Ñ\82лÓ\99Ñ\80не Ò¯Ð· ÐºÑ\83лÑ\8bгÑ\8bз Ð±ÐµÐ»Ó\99н ÐºÒ¯Ñ\87еÑ\80еÑ\80гÓ\99 Ñ\8fки ÐºÑ\83Ñ\88аÑ\80га Ñ\82Ñ\83Ñ\80Ñ\8b ÐºÐ¸Ð»ÐµÑ\80.",
+       "movepagetalktext": "Ð\91Ñ\83 Ð¿Ñ\83нкÑ\82нÑ\8b Ñ\81айлаган Ð¾Ñ\87Ñ\80акÑ\82а, Ð°Ð½Ñ\8bÒ£ Ð±Ó\99Ñ\85Ó\99Ñ\81 Ð±Ð¸Ñ\82е Ð´Ó\99 Ð°Ð²Ñ\82омаÑ\82ик Ñ\80Ó\99веÑ\88Ñ\82Ó\99 ÐºÒ¯Ñ\87еÑ\80елÓ\99Ñ\87Ó\99к, Ó\99гÓ\99Ñ\80дÓ\99 Ñ\88Ñ\83ндÑ\8bй Ð¸Ñ\81емле Ð±Ð°Ñ\88ка Ñ\82Ñ\83лÑ\8b Ð±Ó\99Ñ\85Ó\99Ñ\81 Ð±Ð¸Ñ\82е Ð±Ñ\83лмаÑ\81а.\n\nÐ\91Ñ\83 Ð¾Ñ\87Ñ\80аклаÑ\80да Ñ\81езгÓ\99 Ð±Ð¸Ñ\82лÓ\99Ñ\80не Ò¯Ð·ÐµÐ³ÐµÐ·Ð³Ó\99 ÐºÒ¯Ñ\87еÑ\80еÑ\80гÓ\99 Ñ\82Ñ\83Ñ\80Ñ\8b ÐºÐ¸Ð»Ó\99Ñ\87Ó\99к.",
        "movenotallowed": "Сездә мәкаләләрне күчерү хокуклары юк.",
        "newtitle": "Яңа исем:",
        "move-watch": "Бу битне күзәтү",
        "import-revision-count": "$1 {{PLURAL:$1|юрама}}",
        "importnopages": "Импортлау өчен битләр юк.",
        "importlogpage": "Кертү көндәлеге",
-       "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-watch": "Бу битне сезнең күзәтү исемлегезгә өстәү",
        "tooltip-ca-unwatch": "Бу битне сезнең күзәтү исемлегездә бетерү",
        "tooltip-search": "{{SITENAME}} эчендә эзләү",
-       "tooltip-search-go": "Нәк шундый исеме белән биткә күчәрү",
+       "tooltip-search-go": "Нәкъ шундый исеме белән биткә күчәрү",
        "tooltip-search-fulltext": "Бу текст белән битләрне табу",
-       "tooltip-p-logo": "Баш бит",
-       "tooltip-n-mainpage": "Ð\91аÑ\88 Ð±Ð¸Ñ\82не ÐºÐµÑ\80еп Ñ\87Ñ\8bгÑ\83",
+       "tooltip-p-logo": "Баш биткә күчү",
+       "tooltip-n-mainpage": "Ð\91аÑ\88 Ð±Ð¸Ñ\82кÓ\99 ÐºÒ¯Ñ\87Ò¯",
        "tooltip-n-mainpage-description": "Баш биткә күчү",
        "tooltip-n-portal": "Проект турында, сез нәрсә итә аласыз һәм нәрсә кайда була дип турында.",
        "tooltip-n-currentevents": "Агымдагы вакыйгалар турында мәгълүматны табу",
        "tooltip-t-recentchangeslinked": "Бу биттән сылтаган битләрдә ахыргы үзгәртүләр",
        "tooltip-feed-rss": "Бу бит өчен RSS трансляциясе",
        "tooltip-feed-atom": "Бу бит өчен Atom трансляциясе",
-       "tooltip-t-contributions": "Кулланучы кертеменең исемлегене карау",
+       "tooltip-t-contributions": "{{GENDER:$1|Бу кулланучының}} кертем исемлеге",
        "tooltip-t-emailuser": "Бу кулланучыга хат җибәрү",
        "tooltip-t-upload": "Файлларны йөкләү",
        "tooltip-t-specialpages": "Барлык махсус битләр исемлеге",
        "tooltip-ca-nstab-category": "Төркем битен карау",
        "tooltip-minoredit": "Бу үзгәртүне кече дип билгелү",
        "tooltip-save": "Үзгәртүләрегезне саклау",
-       "tooltip-preview": "Сезнең Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үлÓ\99Ñ\80егезнең Ð°Ð»Ð´Ð°Ð½ ÐºÐ°Ñ\80авÑ\8b, Ñ\81аклаÑ\83дан ÐºÐ°Ð´Ó\99Ñ\80 Ð¼Ð¾Ð½Ñ\8b ÐºÑ\83лланÑ\8bгÑ\8bз Ó\99ле!",
+       "tooltip-preview": "Ð\90лдан ÐºÐ°Ñ\80аÑ\83, Ñ\81аклаÑ\83 Ð°Ð»Ð´Ñ\8bннан Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82үлÓ\99Ñ\80егезнең ÐºÐ°Ñ\80ап Ñ\87Ñ\8bгÑ\8bгÑ\8bз!",
        "tooltip-diff": "Сезнең үзгәртүләрегезне күрсәтү.",
        "tooltip-compareselectedversions": "Бу битнең сайланган ике юрамасы арасында аерманы карау",
        "tooltip-watch": "Бу битне күзәтү исемлегемә өстәү",
        "newimages": "Яңа сүрәтләр җыелмасы",
        "newimages-legend": "Фильтр",
        "ilsubmit": "Эзләү",
-       "hours": "{{PLURAL:$1|$1 cәгать|$1 cәгать}}",
+       "hours": "{{PLURAL:$1|$1 cәгать}}",
+       "days": "{{PLURAL:$1|$1 көн}}",
        "ago": "$1 элек",
        "hours-ago": "$1 cәгать элек",
        "minutes-ago": "$1 минут элек",
        "exif-software": "Программалы тәэмин ителеш",
        "exif-artist": "Автор",
        "exif-copyright": "Автор хокуклары хуҗасы",
-       "exif-exifversion": "Exif версиясе",
+       "exif-exifversion": "Exif юрамасы",
        "exif-flashpixversion": "FlashPix юрамасын тәэмин итү",
        "exif-colorspace": "Төсләр тирәлеге",
        "exif-componentsconfiguration": "Төсләр төзелешенең конфигурациясе",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Бу битнең кэшы чистартылсынмы?",
        "confirm-purge-bottom": "Кэшны чистартудан соң аның соңгы юрамасы күрсәтеләчәк.",
+       "pipe-separator": "&#32;|&#32;",
        "imgmultipageprev": "← алдагы бит",
        "imgmultipagenext": "алдагы бит →",
        "imgmultigo": "Күчү!",
        "specialpages-group-users": "Кулланучылар һәм аларның хокуклары",
        "specialpages-group-highuse": "Еш кулланылучы битләр",
        "specialpages-group-pages": "Битләр исемлеге",
-       "specialpages-group-pagetools": "Бит өчен җиһазлар",
+       "specialpages-group-pagetools": "Бит өчен кораллар",
        "specialpages-group-wiki": "Мәгълүмат һәм җиһазлар",
        "specialpages-group-redirects": "Күчерелүче махсус битләр",
        "specialpages-group-spam": "Спамга каршы кораллар",
        "intentionallyblankpage": "Бу бит махсус буш калдырылган",
        "external_image_whitelist": "#Бу юлны ничек бар, шулаө калдырыгыз<pre>\n#Монда даими фразаларның фрагментларын куегыз (// арасында торган өлешен)\n#алар тышкы сурәтләрнең URL белән бәйләнерләр.\n#Туры килгәннәре сурәт буларак, туры килмәгәннәре сурәткә сылтама буларак күрсәтеләчәкләр.\n# # билгесе белән башланучы юллар шәрехнамә дип саналалар.\n#Юллар регистрга игътибар бирмиләр.\n\n#Даими фразаларның фрагментларын бу кыр өстендә куегыз. Бу кырны ничек бар, шулай калдырыгыз.</pre>",
        "tags": "Гамәлдә булучы үзгәртүләр билгеләре",
-       "tag-filter": "[[Special:Tags|Tag]] фильтры:",
+       "tag-filter": "[[Special:Tags|Билгеләр]] фильтры:",
        "tag-filter-submit": "Фильтрлау",
        "tag-list-wrapper": "([[Special:Tags|{{PLURAL:$1|1=Билге|Билгеләр}}]]: $2)",
        "tags-title": "Теглар",
        "revdelete-uname-unhid": "кулланучының исеме ачылган",
        "revdelete-restricted": "чикләүләр идарәчеләргә дә кулланыла",
        "revdelete-unrestricted": "чикләүләр идарәчеләр өчен бетерелгән",
-       "logentry-move-move": "$1 $3 сәхифәсен $4 {{GENDER:$2|итеп күчерде}}",
+       "logentry-move-move": "$1  $3 битенең исемен {{GENDER:$2| үзгәртте}}. Яңа исеме: $4",
        "logentry-move-move-noredirect": "$1 юнәлтү калдырмыйча $3 сәхифәсен $4 итеп күчерде",
        "logentry-move-move_redir": "$1 юнәлтү аша $3 сәхифәсен $4 итеп күчерде",
        "logentry-move-move_redir-noredirect": "$1 юнәлтү аша, юнәлтү калдырмыйча $3 сәхифәсен $4 итеп күчерде",
index e10c91e..445856f 100644 (file)
        "nstab-template": "قېلىپ",
        "nstab-help": "ياردەم بەت",
        "nstab-category": "تۈر",
+       "mainpage-nstab": "باش بەت",
        "nosuchaction": "بۇنداق مەشغۇلات يوق",
        "nosuchactiontext": "بۇ مەشغۇلات بېكىتكەن URL ئىناۋەتسىز.\n\nURL نى خاتا كىرگۈزۈپ قالدىڭىز ياكى خاتا ئۇلانمىغا ئەگەشتىڭىز.\n\n {{SITENAME}} بېكەت يۇمشاق دېتالىنىڭ خاتالىقى بولۇشى مۇمكىن.",
        "nosuchspecialpage": "بۇنىڭغا ئوخشاش ئالاھىدە بەت يوق",
        "createaccountreason": "سەۋەب:",
        "createacct-reason": "سەۋەبى",
        "createacct-reason-ph": "نېمىشقا باشقا ھېسابات قۇرماقچى بولدىڭىز",
-       "createacct-captcha": "بىخەتەرلىك تەكشۈرۈشى",
-       "createacct-imgcaptcha-ph": "ئۈستىدە كۆرگىنىڭىزنى كىرگۈزۈڭ",
        "createacct-submit": "ھېساباتىڭىزنى قۇرۇڭ",
        "createacct-another-submit": "باشقا ھېسابات قۇرىمەن",
        "createacct-benefit-heading": "{{SITENAME}} سىزگە ئوخشاش كىشىلەر تەرىپىدىن قۇرۇلغان.",
        "loginlanguagelabel": "تىل: $1",
        "suspicious-userlogout": "تىزىمدىن چىقىش ئىلتىماسىڭىز رەت قىلىندى، چۈنكى ئۇ بەلكىم بۇزۇلغان توركۆرگۈ ياكى غەملەك ۋاكالەتچىسى يوللىغان بولۇشى مۇمكىن.",
        "createacct-another-realname-tip": "ھەقىقىي ئىسمىڭىز ئىختىيارى.\nئەگەر تەمىنلەشنى تاللىسىڭىز، ئۇ سىزنىڭ تۆھپىڭىزنىڭ ئىمزاسى بولىدۇ.",
+       "pt-login": "تىزىمغا كىرىڭ",
+       "pt-createaccount": "ھېسابات قۇر",
        "php-mail-error-unknown": "PHP نىڭ mail() فونكسىيەسىدىكى يوچۇن خاتالىق",
        "user-mail-no-addy": "ئېلخەت ئادرېسسىز خەت يوللاشنى سىنىدى.",
        "user-mail-no-body": "بوش ياكى مەزمۇنى قىسقا مۇۋاپىق بولمىغان تورخەت ئەۋەتىشنى سىنىدى.",
        "passwordreset-emailtext-ip": "باشقىلار (بەلكىم سىز، IP ئادرېسى $1) {{SITENAME}} ($4) دىكى پارولنى قايتا بېكىتىشنى ئىلتىماس قىلدى. تۆۋەندىكى ئىشلەتكۈچىنىڭ {{PLURAL:$3|ھېسابات|ھېسابات}}ى مۇشۇ ئېلخەتكە باغلانغان:\n\n$2\n\n{{PLURAL:$3|بۇ ۋاقىتلىق پارول|بۇ ۋاقىتلىق پارول}} {{PLURAL:$5|بىر كۈن|$5 كۈن}}دە ۋاقتى ئۆتىدۇ. ئەگەر بۇ مەشغۇلاتنى سىز ئىلتىماس قىلغان بولسىڭىز، دەرھال تىزىمغا كىرىپ يېڭى پارولدىن بىرنى تاللاڭ.\nسىز بەلگىلىگەن يېڭى پارول  {{PLURAL:$5|كۈن|$5 كۈن}}دە ۋاقتى توشىدۇ. ئەگەر باشقىلار ئىلتىماس قىلغان بولسا ياكى ئۆزىڭىز بەلگىلىگەن پارول ئېسىڭىزگە كېلىپ ئۇنى ئۆزگەرتمىسىڭىز، \nبۇ ئۇچۇرغا پەرۋا قىلماي ئۆزىڭىزنىڭ كونا پارولىنى ئىشلىتىۋېرىڭ.",
        "passwordreset-emailtext-user": "{{SITENAME}} دىكى ئىشلەتكۈچى $1 بېكەت {{SITENAME}} ($4) دىكى پارولىڭىزنى قايتا بېكىتىشنى ئىلتىماس قىلدى .\nتۆۋەندىكى ئىشلەتكۈچىنىڭ {{PLURAL:$3|ھېسابات|ھېسابات}}($4)ى مۇشۇ ئېلخەتكە باغلانغان:\n\n$2\n\n{{PLURAL:$3|بۇ ۋاقىتلىق پارول|بۇ ۋاقىتلىق پارول}} {{PLURAL:$5|بىر كۈن|$5 كۈن}}دە ۋاقتى ئۆتىدۇ. ئەگەر بۇ مەشغۇلاتنى سىز ئىلتىماس قىلغان بولسىڭىز، دەرھال تىزىمغا كىرىپ يېڭى پارولدىن بىرنى تاللاڭ.\nسىز بەلگىلىگەن يېڭى پارول {{PLURAL:$5|كۈن|$5 كۈن}}دە ۋاقتى توشىدۇ. ئەگەر باشقىلار ئىلتىماس قىلغان بولسا ياكى ئۆزىڭىز بەلگىلىگەن پارول ئېسىڭىزگە كېلىپ ئۇنى ئۆزگەرتمىسىڭىز، \nبۇ ئۇچۇرغا پەرۋا قىلماي ئۆزىڭىزنىڭ كونا پارولىنى ئىشلىتىۋېرىڭ.",
        "passwordreset-emailelement": "ئىشلەتكۈچى نامى: \n$1\n\nۋاقىتلىق پارول: \n$2",
-       "passwordreset-emailsent": "پارولنى قايتا بېكىتىش ئېلخېتى يوللاندى.",
+       "passwordreset-emailsentemail": "پارولنى قايتا بېكىتىش ئېلخېتى يوللاندى.",
        "passwordreset-emailsent-capture": "پارولنى قايتا بېكىتىش ئېلخېتى يوللاندى، تۆۋەندە كۆرسىتىلىدۇ.",
        "passwordreset-emailerror-capture": "ھاسىل قىلىنغان پارولنى قايتا بېكىتىش ئېلخېتى تۆۋەندە كۆرسىتىلگەندەك ئەمما ئۇنى {{GENDER:$2|ئىشلەتكۈچى}}گە يوللىيالمىدى: $1",
        "changeemail": "ئېلخەت ئادرېس ئۆزگەرت",
-       "changeemail-text": "بۇ جەدۋەل تاماملانسا ئېلخەت ئادرېسىڭىزنى ئۆزگەرتىدۇ. سىز ئىم كىرگۈزۈپ بۇ ئۆزگەرتىشنى جەزملەيسىز.",
+       "changeemail-header": "ھېساباتنىڭ ئېلخەت ئادرېسىنى ئۆزگەرت",
        "changeemail-no-info": "سىز تىزىمغا كىرگەندىن كېيىن بىۋاسىتە بۇ بەتكە كىرىشىڭىز لازىم.",
        "changeemail-oldemail": "نۆۋەتتىكى ئېلخەت ئادرېسى:",
        "changeemail-newemail": "يېڭى ئېلخەت ئادرېسى:",
        "prefs-tokenwatchlist": "ئاچقۇچ",
        "prefs-diffs": "پەرقلەر",
        "prefs-help-prefershttps": "بۇ سەپلەك، سىز قايتا تىزىمغا كىرگەندە ئىشلەيدۇ.",
-       "email-address-validity-valid": "ئېلخەت ئادرېسى ئىناۋەتلىك",
-       "email-address-validity-invalid": "ئىناۋەتلىك ئېلخەت ئادرېسىدىن بىرنى كىرگۈزۈڭ",
        "userrights": "ئىشلەتكۈچى ھوقۇقى باشقۇرۇش",
        "userrights-lookup-user": "ئىشلەتكۈچى گۇرۇپپىسى باشقۇرۇش",
        "userrights-user-editname": "ئىشلەتكۈچى ئاتى كىرگۈزۈڭ:",
        "wlheader-showupdated": "سىز ئالدىنقى قېتىم كۆرگەندىن كېيىن ئۆزگەرتىلگەن بەتلەر '''توم''' كۆرۈنىدۇ",
        "wlnote": "تۆۋەندىكىسى يېقىنقى {{PLURAL:$2|سائەت}} ئىچىدىكى ئاخىرقى '{{PLURAL:$1| قېتىملىق}}  ئۆزگەرتىش، $3 $4 گىچە.",
        "wlshowlast": "يېقىنقى $1 سائەت $2 كۈن  نىڭ ئۆزگەرتىشىنى كۆرسەت",
+       "watchlistall2": "ھەممىسى",
        "watchlist-options": "كۆزەت تىزىملىك تاللانما",
        "watching": "كۆزەت قىلىۋاتىدۇ…",
        "unwatching": "كۆزەت قىلمايۋاتىدۇ…",
        "movenosubpage": "بۇ بەتنىڭ تارماق بېتى يوق",
        "movereason": "سەۋەب:",
        "revertmove": "قايتۇر",
-       "delete_and_move": "ئۆچۈرۈپ يۆتكە",
        "delete_and_move_text": "== ئۆچۈرۈش زۆرۈر ==\nنىشان بەت \"[[:$1]]\" مەۋجۇد.\nيۆتكەشكە قولاي بولۇشى ئۈچۈن بۇ بەتنى ئۆچۈرەمسىز؟",
        "delete_and_move_confirm": "ھەئە، بۇ بەتنى ئۆچۈر",
        "delete_and_move_reason": " \"[[$1]]\" يۆتكەشكە قولاي بولۇشى ئۈچۈن ئۆچۈرۈۋېتىلدى",
        "tooltip-pt-mycontris": "تۆھپە تىزىملىكىڭىز",
        "tooltip-pt-login": "تىزىمغا كىرىشىڭىزنى تەۋسىيە قىلىمىز ئەمما مەجبۇرىي ئەمەس",
        "tooltip-pt-logout": "تىزىمدىن چىق",
+       "tooltip-pt-createaccount": "ھېساباتتىن بىرنى قۇرۇپ تىزىمغا كىرىشىڭىزنى تەۋسىيە قىلىمىز، ئەمما بۇ مەجبۇرىي ئەمەس.",
        "tooltip-ca-talk": "بەت مەزمۇنى ھەققىدىكى مۇنازىرە",
        "tooltip-ca-edit": "بۇ بەتنى تەھرىرلىيەلەيسىز.\nساقلاشتىن ئىلگىرى ئالدىن كۆزەت كۇنۇپكىسىنى ئىشلىتىڭ",
        "tooltip-ca-addsection": "يېڭى بىر مۇنازىرە باشلاڭ",
index f1b0541..bfdb585 100644 (file)
@@ -63,7 +63,8 @@
                        "Капитан Джон Шепард",
                        "Translatemyname",
                        "Dars",
-                       "Mix Gerder"
+                       "Mix Gerder",
+                       "E.belykh"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
@@ -90,8 +91,8 @@
        "tog-enotifminoredits": "Надсилати мені електронного листа навіть при незначних редагуваннях сторінок та файлів",
        "tog-enotifrevealaddr": "Показувати мою поштову адресу в повідомленнях",
        "tog-shownumberswatching": "Показувати число користувачів, які додали сторінку до свого списку спостереження",
-       "tog-oldsig": "Ð\86Ñ\81нÑ\83Ñ\8eÑ\87ий підпис:",
-       "tog-fancysig": "Сприймати підпис як вікі-текст (без автоматичного посилання)",
+       "tog-oldsig": "Ð\9fоÑ\82оÑ\87ний підпис:",
+       "tog-fancysig": "Сприймати підпис як вікітекст (без автоматичного посилання)",
        "tog-uselivepreview": "Використовувати швидкий попередній перегляд",
        "tog-forceeditsummary": "Попереджати, коли не зазначений короткий опис редагування",
        "tog-watchlisthideown": "Приховати мої редагування у списку спостереження",
        "october-date": "$1 жовтня",
        "november-date": "$1 листопада",
        "december-date": "$1 грудня",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|1=Категорія|Категорії}}",
        "category_header": "Сторінки в категорії «$1»",
        "subcategories": "Підкатегорії",
        "databaseerror-query": "Запит: $1",
        "databaseerror-function": "Функція: $1",
        "databaseerror-error": "Помилка: $1",
-       "transaction-duration-limit-exceeded": "Ð\94лÑ\8f Ñ\82ого, Ñ\89об Ñ\83никнÑ\83Ñ\82и Ð»Ð°Ð³Ñ\83 Ð¿Ñ\80и Ñ\80еплÑ\96каÑ\86Ñ\96Ñ\97, Ñ\86Ñ\8f Ñ\82Ñ\80анзакÑ\86Ñ\96Ñ\8f Ð±Ñ\83ла Ð¿ÐµÑ\80еÑ\80вана, Ð¾Ñ\81кÑ\96лÑ\8cки Ñ\82Ñ\80ивалÑ\96Ñ\81Ñ\82Ñ\8c Ð·Ð°Ð¿Ð¸Ñ\81Ñ\83 ($1) Ð¿ÐµÑ\80евиÑ\89ила Ð»Ñ\96мÑ\96в Ð² $2 Ñ\81ек.\nЯкщо ви змінюєте декілька елементів за один раз, постарайтесь замість цього зробити декілька невеликих операцій.",
+       "transaction-duration-limit-exceeded": "Щоб Ñ\83никнÑ\83Ñ\82и Ð²ÐµÐ»Ð¸ÐºÐ¾Ð³Ð¾ Ð»Ð°Ð³Ñ\83 Ð¿Ñ\80и Ñ\80еплÑ\96каÑ\86Ñ\96Ñ\97, Ñ\86Ñ\8f Ñ\82Ñ\80анзакÑ\86Ñ\96Ñ\8f Ð±Ñ\83ла Ð¿ÐµÑ\80еÑ\80вана, Ð¾Ñ\81кÑ\96лÑ\8cки Ñ\82Ñ\80ивалÑ\96Ñ\81Ñ\82Ñ\8c Ð·Ð°Ð¿Ð¸Ñ\81Ñ\83 ($1) Ð¿ÐµÑ\80евиÑ\89ила Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ\8f Ð² $2 {{PLURAL:$2|Ñ\81екÑ\83ндÑ\83\81екÑ\83нд|Ñ\81екÑ\83нди}}.\n\nЯкщо ви змінюєте декілька елементів за один раз, постарайтесь замість цього зробити декілька невеликих операцій.",
        "laggedslavemode": "Увага: сторінка може не містити останніх редагувань.",
        "readonly": "Запис до бази даних заблокований",
        "enterlockreason": "Зазначте причину і приблизний термін блокування",
        "virus-scanfailed": "помилка сканування (код $1)",
        "virus-unknownscanner": "невідомий антивірус:",
        "logouttext": "'''Ви вийшли з системи.'''\n\nДеякі сторінки можуть відображатися, ніби ви ще в системі, аж поки ви не оновите кеш браузера.",
+       "cannotlogoutnow-title": "Неможливо вийти прямо зараз",
+       "cannotlogoutnow-text": "Неможливо вийти із системи під час використання $1.",
        "welcomeuser": "Вітаємо, $1!",
        "welcomecreation-msg": "Ваш обліковий запис створено.\nТепер маєте змогу за бажанням змінювати ваші [[Special:Preferences|налаштування у {{GRAMMAR:genitive|{{SITENAME}}}}]].",
        "yourname": "Ім'я користувача:",
        "remembermypassword": "Запам'ятати мій обліковий запис на цьому комп'ютері (на строк не більше $1 {{PLURAL:$1|1=дня|днів}})",
        "userlogin-remembermypassword": "Запам'ятати мене",
        "userlogin-signwithsecure": "Захищене з'єднання",
+       "cannotloginnow-title": "Неможливо увійти прямо зараз",
+       "cannotloginnow-text": "Неможливо увійти під-час використання $1.",
        "yourdomainname": "Ваш домен:",
        "password-change-forbidden": "Ви не можна змінити пароль на цій вікі.",
        "externaldberror": "Сталася помилка при автентифікації за допомогою зовнішньої бази даних, або у вас недостатньо прав для внесення змін до свого зовнішнього облікового запису.",
        "throttled-mailpassword": "Листа для оновлення пароля вже було надіслано електронною поштою протягом {{PLURAL:$1|1=останньої години|останніх $1 годин}}.\nДля попередження зловживань дозволено надсилати тільки одного листа оновлення пароля за {{PLURAL:$1|годину|$1 години|$1 годин}}.",
        "mailerror": "Помилка надсилання пошти: $1",
        "acct_creation_throttle_hit": "Відвідувачі з вашої IP-адреси вже створили $1 {{PLURAL:$1|обліковий запис|облікових записи|облікових записів}} за останню добу, що є максимумом для цього відрізка часу.\nТаким чином, користувачі з цієї IP-адреси не можуть на цей момент створювати нових облікових записів.",
-       "emailauthenticated": "Вашу адресу електронної пошти було підтверджено на  $2  о  $3.",
+       "emailauthenticated": "Вашу адресу електронної пошти було підтверджено $2  о  $3.",
        "emailnotauthenticated": "Адресу вашої електронної пошти ще не підтверджено. Надсилання листів неможливе у жодній з наступних опцій.",
        "noemailprefs": "Вкажіть адресу електронної пошти, щоб уможливити наступні поштові функції вікі.",
        "emailconfirmlink": "Підтвердіть адресу вашої електронної пошти",
        "resetpass_submit": "Встановити пароль і ввійти",
        "changepassword-success": "Ваш пароль успішно змінено!",
        "changepassword-throttled": "Ви нещодавно зробили надто багато спроб ввійти до системи.\nБудь ласка, зачекайте $1 перед повторною спробою.",
+       "botpasswords": "Паролі ботів",
+       "botpasswords-summary": "<em>Паролі бота</em> дозволяють отримати доступ до облікового запису користувача через API без використання логіну і пароля головного облікового запису. Права користувача при вході з паролем бота можуть бути обмеженні.\n\nЯкщо ви не знаєте, навіщо воно вам, імовірно, краще цього не робіть. Ніхто ніколи не повинен просити вас, щоб ви створили чи повідомили цей пароль.",
+       "botpasswords-disabled": "Паролі бота відключені.",
+       "botpasswords-no-central-id": "Для використання паролів бота ви повинні увійти в централізований обліковий запис.",
+       "botpasswords-existing": "Існуючі паролі бота",
+       "botpasswords-createnew": "Створити новий пароль бота",
+       "botpasswords-editexisting": "Редагувати існуючий пароль бота",
+       "botpasswords-label-appid": "Назва бота:",
+       "botpasswords-label-create": "Створити",
+       "botpasswords-label-update": "Оновити",
+       "botpasswords-label-cancel": "Скасувати",
+       "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": "Пароль бота створено",
+       "botpasswords-created-body": "Пароль бота «$1» був успішно створений.",
+       "botpasswords-updated-title": "Пароль бота оновлено",
+       "botpasswords-updated-body": "Пароль бота «$1» був успішно оновлений.",
+       "botpasswords-deleted-title": "Пароль бота видалено",
+       "botpasswords-deleted-body": "Пароль бота «$1» було видалено",
+       "botpasswords-newpassword": "Новий пароль для входу під <strong>$1</strong> — <strong>$2</strong>. <em>Запишіть його для подальшого використання.</em>",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider не доступний.",
        "resetpass_forbidden": "Пароль не можна змінити",
        "resetpass-no-info": "Щоб звертатися безпосередньо до цієї сторінки, вам слід увійти до системи.",
        "resetpass-submit-loggedin": "Змінити пароль",
        "passwordreset-emailtext-ip": "Хтось (імовірно ви, з IP-адреси $1) попросив нагадати деталі вашого облікового запису для {{SITENAME}} ($4). З вашою електронною скринькою пов'язан{{PLURAL:$3|1=ий такий запис|і такі записи}}:\n\n$2\n\n{{PLURAL:$3|1=Цей тимчасовий пароль стане недійсним|Ці тимчасові паролі стануть недійсними}} через $5 {{PLURAL:$5|день|дні|днів}}.\nВи маєте ввійти в систему і вибрати новий пароль. Якщо ж цей запит зробив хтось інший або ви згадали свій старий пароль і не бажаєте його змінювати, можете ігнорувати це повідомлення та продовжувати використовувати старий пароль.",
        "passwordreset-emailtext-user": "Користувач $1 з {{SITENAME}} попросив нагадати деталі вашого облікового запису для {{SITENAME}} ($4). З вашою електронною скринькою пов'язан{{PLURAL:$3|1=ий такий запис|і такі записи}}:\n\n$2\n\n{{PLURAL:$3|1=Цей тимчасовий пароль|Ці тимчасові паролі}} стануть нечинні через {{PLURAL:$5|день|$5 дні|$5 днів}}.\nВи маєте ввійти в систему і вибрати новий пароль. Якщо ж цей запит зробив хтось інший, або ви згадали свій старий пароль і не бажаєте його змінювати, можете просто ігнорувати це повідомлення та продовжувати використовувати старий пароль.",
        "passwordreset-emailelement": "Ім'я користувача: \n$1\n\nТимчасовий пароль: \n$2",
-       "passwordreset-emailsentemail": "Якщо це адреса електронної пошти, на яку зареєстрований ваш обліковий запис, то буде надісланий лист для відновлення пароля.",
-       "passwordreset-emailsentusername": "ЯкÑ\89о Ñ\86е Ð²Ñ\96дповÑ\96дна Ð°Ð´Ñ\80еÑ\81а ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и, Ð±Ñ\83де Ð²Ñ\96дпÑ\80авлено лист для відновлення пароля.",
+       "passwordreset-emailsentemail": "Якщо ця електронна адреса асоційована з вашим обліковим записом, то лист для відновлення пароля буде відправлено на неї.",
+       "passwordreset-emailsentusername": "ЯкÑ\89о Ñ\96Ñ\81нÑ\83Ñ\94 ÐµÐ»ÐµÐºÑ\82Ñ\80онна Ð°Ð´Ñ\80еÑ\81а, Ñ\8fка Ð°Ñ\81оÑ\86Ñ\96йована Ð· Ñ\86им Ð¾Ð±Ð»Ñ\96ковим Ð·Ð°Ð¿Ð¸Ñ\81ом, Ð½Ð° Ð½ÐµÑ\97 Ð±Ñ\83де Ð½Ð°Ð´Ñ\96Ñ\81лано лист для відновлення пароля.",
        "passwordreset-emailsent-capture": "Електронний лист скидання пароля було надіслано, як показано нижче.",
        "passwordreset-emailerror-capture": "Електронний лист для відновлення пароля мав бути надісланий, як показано нижче, але його надсилання {{GENDER:$2|користувачеві|користувачці}} $1 не вдалося.",
        "changeemail": "Змінити або вилучити адресу електронної пошти",
        "prefs-custom-js": "Власний JS",
        "prefs-common-css-js": "CSS/JS спільні для всіх тем оформлення:",
        "prefs-reset-intro": "Ця сторінка може бути використана для зміни ваших налаштувань на стандартні.\nПісля виконання цієї дії ви не зможете відкотити зміни.",
-       "prefs-emailconfirm-label": "Ð\9fÑ\96дÑ\82веÑ\80дженнÑ\8f ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и:",
+       "prefs-emailconfirm-label": "Ð\9fÑ\96дÑ\82веÑ\80дженнÑ\8f Ð°Ð´Ñ\80еÑ\81и:",
        "youremail": "Адреса електронної пошти:",
        "username": "{{GENDER:$1|Ім'я користувача|Ім'я користувачки}}:",
        "prefs-memberingroups": "{{GENDER:$2|Член}} {{PLURAL:$1|1=групи|груп}}:",
        "yourvariant": "Варіант мови вмісту:",
        "prefs-help-variant": "Бажана мова сторінок та інтерфейсу цього вікіпроекту.",
        "yournick": "Підпис:",
-       "prefs-help-signature": "Репліки на сторінках обговорення слід підписувати символами \"<nowiki>~~~~</nowiki>\", які будуть перетворені у ваш підпис і час.",
+       "prefs-help-signature": "Репліки на сторінках обговорення слід підписувати символами «<nowiki>~~~~</nowiki>», які будуть перетворені у Ваш підпис і час.",
        "badsig": "Неправильний підпис.\nПеревірте коректність HTML-тегів.",
        "badsiglength": "Ваш підпис дуже довгий.\nПовинно бути не більше $1 {{PLURAL:$1|символу|символів|символів}}.",
        "yourgender": "Стать:",
        "gender-unknown": "Згадуючи Вас, програмне забезпечення використовуватиме по змозі гендерно нейтральні слова",
        "gender-male": "Чоловіча",
        "gender-female": "Жіноча",
-       "prefs-help-gender": "Задання цього параметру — необов'язкове. Застосовується рушієм у тих звертаннях до користувача, які залежать від статі.\nЦя інформація загальнодоступна.",
+       "prefs-help-gender": "Задання цього параметра — необов'язкове. Застосовується рушієм у тих звертаннях до користувача, які залежать від статі.\nЦя інформація загальнодоступна.",
        "email": "Електронна пошта",
        "prefs-help-realname": "Справжнє ім'я вказувати необов'язково.\nЯкщо ви його зазначите, то саме з ним може бути пов'язаний увесь ваш доробок.",
-       "prefs-help-email": "Ð\90дÑ\80еÑ\81а ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ð½Ðµ Ñ\94 Ð¾Ð±Ð¾Ð²'Ñ\8fзковоÑ\8e, Ð°Ð»Ðµ Ð½ÐµÐ¾Ð±Ñ\85Ñ\96дна Ð´Ð»Ñ\8f Ñ\81киданнÑ\8f Ð¿Ð°Ñ\80олÑ\8f, Ñ\8fкÑ\89о Ð²и його забудете.",
-       "prefs-help-email-others": "Також Ð²Ð¾Ð½Ð° Ð´Ð¾Ð·Ð²Ð¾Ð»Ð¸Ñ\82Ñ\8c Ñ\96нÑ\88им ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87ам Ð·Ð²'Ñ\8fзаÑ\82иÑ\81Ñ\8f Ð· Ð²Ð°Ð¼Ð¸ Ñ\87еÑ\80ез Ð²Ð°Ñ\88Ñ\83 Ð¾Ñ\81обиÑ\81Ñ\82Ñ\83 Ñ\81Ñ\82оÑ\80Ñ\96нкÑ\83 Ð±ÐµÐ· Ð½ÐµÐ¾Ð±Ñ\85Ñ\96дноÑ\81Ñ\82Ñ\96 Ñ\80озкÑ\80иÑ\82Ñ\82Ñ\8f Ð²Ð°Ñ\88оÑ\97 ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð°Ð´Ñ\80еÑ\81и.",
+       "prefs-help-email": "Ð\90дÑ\80еÑ\81а ÐµÐ»ÐµÐºÑ\82Ñ\80онноÑ\97 Ð¿Ð¾Ñ\88Ñ\82и Ð½Ðµ Ñ\94 Ð¾Ð±Ð¾Ð²'Ñ\8fзковоÑ\8e, Ð°Ð»Ðµ Ð½ÐµÐ¾Ð±Ñ\85Ñ\96дна Ð´Ð»Ñ\8f Ñ\81киданнÑ\8f Ð¿Ð°Ñ\80олÑ\8f, Ñ\8fкÑ\89о Ð\92и його забудете.",
+       "prefs-help-email-others": "Також Ð²Ð¾Ð½Ð° Ð´Ð¾Ð·Ð²Ð¾Ð»Ð¸Ñ\82Ñ\8c Ñ\96нÑ\88им ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87ам Ð·Ð²'Ñ\8fзаÑ\82иÑ\81Ñ\8f Ð· Ð\92ами Ñ\87еÑ\80ез Ð¿Ð¾Ñ\81иланнÑ\8f Ð½Ð° Ð\92аÑ\88Ñ\96й Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\96 ÐºÐ¾Ñ\80иÑ\81Ñ\82Ñ\83ваÑ\87а Ñ\87и Ð½Ð° Ñ\81Ñ\82оÑ\80Ñ\96нÑ\86Ñ\96 Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ\80еннÑ\8f. Ð\9fÑ\80и Ñ\86Ñ\8cомÑ\83 Ð\92аÑ\88а ÐµÐ»ÐµÐºÑ\82Ñ\80онна Ð°Ð´Ñ\80еÑ\81а Ð·Ð°Ð»Ð¸Ñ\88иÑ\82Ñ\8cÑ\81Ñ\8f Ð½ÐµÑ\80озкÑ\80иÑ\82оÑ\8e.",
        "prefs-help-email-required": "Потрібно зазначити адресу електронної пошти.",
        "prefs-info": "Основні відомості",
        "prefs-i18n": "Інтернаціоналізація",
        "right-blockemail": "блокування користувача від надсилання електронної пошти",
        "right-hideuser": "блокування імені користувача і приховування його",
        "right-ipblock-exempt": "уникнення блокування за IP-адресою, автоблокування і блокування діапазонів",
-       "right-proxyunbannable": "уникнення автоматичного блокування проксі-серверів",
        "right-unblockself": "розблоковування себе",
        "right-protect": "зміна рівнів захисту та редагування каскадно захищених сторінок",
        "right-editprotected": "редагування сторінок з рівнем захисту «{{int:protect-level-sysop}}»",
        "right-managechangetags": "створення та вилучення [[Special:Tags|міток]] з бази даних",
        "right-applychangetags": "додавання [[Special:Tags|міток]] разом зі змінами",
        "right-changetags": "додавання або вилучення будь-яких [[Special:Tags|міток]] для певних версій сторінок або записів журналів",
+       "grant-generic": "Пучок прав \"$1\"",
+       "grant-group-page-interaction": "Взаємодіяти з сторінками",
+       "grant-group-file-interaction": "Взаємодіяти з медіа",
+       "grant-group-watchlist-interaction": "Взаємодіяти з вашим списком спостереження",
+       "grant-group-email": "Надіслати листа",
+       "grant-group-high-volume": "Виконати великий обсяг діяльності",
+       "grant-group-customization": "Налаштування і переваги",
+       "grant-group-administration": "Виконати адміністративні дії",
+       "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-editprotected": "Редагувати захищені сторінки",
+       "grant-highvolume": "Редагування великих обсягів",
+       "grant-oversight": "Приховати користувачів і подавити версії",
+       "grant-patrol": "Патрулювати зміни на сторінках",
+       "grant-protect": "Захистити і зняти захист сторінок",
+       "grant-rollback": "Відкат змін на сторінках",
+       "grant-sendemail": "Відправляти пошту іншим користувачам",
+       "grant-uploadeditmovefile": "Завантажити, замінити та перемістити файли",
+       "grant-uploadfile": "Завантажити нові файли",
+       "grant-viewdeleted": "Перегляд видалених файлів і сторінок",
+       "grant-viewmywatchlist": "Перегляд списку спостереження",
        "newuserlogpage": "Журнал нових користувачів",
        "newuserlogpagetext": "Список нещодавно зареєстрованих користувачів.",
        "rightslog": "Журнал прав користувача",
        "upload-form-label-select-file": "Обрати файл",
        "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": "Назва файлу",
        "foreign-structured-upload-form-label-own-work": "Це моя власна робота",
        "listgrouprights-namespaceprotection-header": "Обмеження простору назв",
        "listgrouprights-namespaceprotection-namespace": "Простір назв",
        "listgrouprights-namespaceprotection-restrictedto": "Права, що дозволяють користувачу редагувати",
+       "listgrants-summary": "Нижче наведено список OAuth грантів, з їхнім пов'язаним доступом до прав користувача. Користувачі можуть дозволити програмам використовувати свій обліковий запис, але з обмеженими правами на основі грантів користувача наданих до заяви. Програма діє від імені користувача, однак насправді не може використовувати права, які користувач не має.\nТам може бути [[{{MediaWiki:Listgrouprights-helppage}}|додаткова інформація]] про індивідуальні права.",
+       "listgrants-grant": "Грант",
+       "listgrants-rights": "Права",
        "trackingcategories": "Відстежувані категорії",
        "trackingcategories-summary": "На цій сторінці перераховані відстежують категорії, які заповнюються автоматично програмним забезпеченням MediaWiki. Їх можна перейменувати, змінивши відповідні системні повідомлення в просторі назв {{ns:8}}.",
        "trackingcategories-msg": "Відстежувана категорія",
        "wlshowhideanons": "анонімних користувачів",
        "wlshowhidepatr": "перевірені редагування",
        "wlshowhidemine": "мої редагування",
+       "wlshowhidecategorization": "категоризацію сторінок",
        "watchlist-options": "Налаштування списку спостереження",
        "watching": "Додавання до списку спостереження…",
        "unwatching": "Вилучення зі списку спостереження…",
        "unblock": "Розблокувати користувача",
        "blockip": "Заблокувати {{GENDER:$1|користувача|користувачку}}",
        "blockip-legend": "Блокування користувача",
-       "blockiptext": "Використовуйте форму нижче, щоб заблокувати можливість редагування зазначеній IP-адресі або користувачу.\nЦе слід робити лише для запобігання порушенням і у відповідності до [[{{MediaWiki:Policy-url}}|правил]].\nОбов'язково заповніть причину нижче, бажано дати інформативну вичерпну інформацію (наприклад, послатися на конкретні правила, дати посилання на редагування користувача, які призвели до блокування). Можна конкретизувати причину блокування на сторінці обговорення користувача.\n* Якщо ви блокуєте обліковий запис бота, переконайтеся, що ви вимкнули автоблокування (для запобігання автоматичного блокування облікових записів власника бота або інших ботів).\n* Зверніть увагу, що IP-адреси у більшості випадків не варто блокувати на більший за декілька днів термін, щоб під блокування не підпали інші користувачі з таким самим IP. Винятки — частий довготривалий вандалізм.",
+       "blockiptext": "Використовуйте форму нижче, щоб заблокувати можливість редагування зазначеній IP-адресі або користувачу.\nЦе слід робити лише для запобігання порушенням і у відповідності до [[{{MediaWiki:Policy-url}}|правил]].\nОбов'язково заповніть причину нижче, бажано дати інформативну вичерпну інформацію (наприклад, послатися на конкретні правила, дати посилання на редагування користувача, які призвели до блокування). Можна конкретизувати причину блокування на сторінці обговорення користувача.\nВи можете заблокувати діапазони IP-адрес, використовуючи [https://uk.wikipedia.org/wiki/CIDR]-синтаксис. Максимально допустимий діапазон — /$1 для протоколу IPv4 та /$2 для протоколу IPv6.",
        "ipaddressorusername": "IP-адреса або ім'я користувача:",
        "ipbexpiry": "Термін:",
        "ipbreason": "Причина:",
        "export-download": "Зберегти як файл",
        "export-templates": "Включити шаблони",
        "export-pagelinks": "Включити пов'язані сторінки з глибиною:",
+       "export-manual": "Додати сторінки вручну:",
        "allmessages": "Системні повідомлення",
        "allmessagesname": "Назва",
        "allmessagesdefault": "Стандартний текст",
        "pageinfo-category-files": "Кількість файлів",
        "markaspatrolleddiff": "Позначити як перевірену",
        "markaspatrolledtext": "Позначити цю сторінку як перевірену",
+       "markaspatrolledtext-file": "Позначити цю версію файлу як відпатрульовану",
        "markedaspatrolled": "Позначена як перевірена",
        "markedaspatrolledtext": "Обрана версія [[:$1]] була позначена як відпатрульована.",
        "rcpatroldisabled": "Патрулювання останніх змін заборонене",
        "newimages-legend": "Фільтр",
        "newimages-label": "Назва файлу (або її частина):",
        "newimages-showbots": "Показати завантаження ботами",
+       "newimages-hidepatrolled": "Приховати відпатрульовані завантаження",
        "noimages": "Файли відсутні.",
        "ilsubmit": "Шукати",
        "bydate": "за датою",
        "hebrew-calendar-m12-gen": "Елула",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|обговорення]])",
        "timezone-utc": "UTC",
+       "timezone-local": "Місцеві",
        "duplicate-defaultsort": "Увага. Ключ сортування «$2» перекриває попередній ключ сортування «$1».",
        "duplicate-displaytitle": "<strong>Увага:</strong> Відображений заголовок \"$2\" заміщує раніше відображений заголовок \"$1\".",
        "invalid-indicator-name": "<strong>Помилка:</strong> Сторінка індикатора стану <code>name</code> атрибута не може бути пуста.",
        "tags-deactivate": "вимкнути",
        "tags-hitcount": "$1 {{PLURAL:$1|зміна|зміни|змін}}",
        "tags-manage-no-permission": "У Вас нема дозволу керувати змінами міток.",
+       "tags-manage-blocked": "Не можна змінювати мітки під час блокування.",
        "tags-create-heading": "Створити нову мітку",
        "tags-create-explanation": "За замовчуванням, новостворені мітки будуть доступні для використання користувачами і ботами.",
        "tags-create-tag-name": "Назва мітки:",
        "tags-deactivate-not-allowed": "Неможливо вимкнути мітку «$1».",
        "tags-deactivate-submit": "Вимкнути",
        "tags-apply-no-permission": "Ви не маєте права міняти мітки вашого редагування.",
+       "tags-apply-blocked": "Ви не можете змінювати мітки редагувань, будучи заблокованим.",
        "tags-apply-not-allowed-one": "Мітку «$1» не можна додавати вручну.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Таку мітку|Такі мітки}} не можна додавати вручну: $1",
        "tags-update-no-permission": "Ви не маєте права додавати або вилучати мітки окремих версій чи журнальних записів.",
+       "tags-update-blocked": "Ви не можете додати чи видалити мітки редагувань, будучи заблокованим.",
        "tags-update-add-not-allowed-one": "Мітку \"$1\" не можна додавати вручну.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Таку мітку|Такі мітки}} не можна додавати вручну: $1",
        "tags-update-remove-not-allowed-one": "Мітку «$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": "Перевищено дозволену глибину стека",
        "mw-widgets-dateinput-placeholder-month": "РРРР-ММ",
        "mw-widgets-titleinput-description-new-page": "сторінка ще не існує",
        "mw-widgets-titleinput-description-redirect": "перенаправлення на $1",
-       "api-error-blacklisted": "Будь ласка, виберіть іншу, більш зрозумілу назву."
+       "api-error-blacklisted": "Будь ласка, виберіть іншу, більш зрозумілу назву.",
+       "randomrootpage": "Випадкова коренева сторінка"
 }
index 539fa36..17cfff1 100644 (file)
        "disclaimerpage": "Project:عام اعلان",
        "edithelp": "معاونت براۓ ترمیم",
        "helppage-top-gethelp": "مدد",
-       "mainpage": "سرÙ\88رÙ\82",
-       "mainpage-description": "صÙ\81Ø­Û\81 Ø§Ù\88Ù\84/سرÙ\88رÙ\82",
+       "mainpage": "صÙ\81Ø­Û\82 Ø§Ù\88Ù\84",
+       "mainpage-description": "صÙ\81Ø­Û\82 Ø§Ù\88Ù\84",
        "policy-url": "Project:حکمتِ عملی",
        "portal": "دیوان عام",
        "portal-url": "Project:دیوان عام",
        "nohistory": "اِس صفحہ کیلئے کوئی تدوینی تاریخچہ موجود نہیں ہے.",
        "currentrev": "حـالیـہ تـجدید",
        "currentrev-asof": "حالیہ نظرثانی بمطابق $1",
-       "revisionasof": "تـجدید بـمطابق $1",
+       "revisionasof": "تجدید بمطابق $1",
        "revision-info": "نظرثانی بتاریخ $1 از {{GENDER:$6|$2}}$7",
        "previousrevision": "←پرانی تدوین",
        "nextrevision": "→اگلا اعادہ",
        "recentchanges-legend": "اِختیاراتِ حالیہ تبدیلیاں",
        "recentchanges-summary": "اس صفحے پر ویکی میں ہونے والی تازہ تریں تبدیلیوں کا مشاہدہ کیجیۓ۔",
        "recentchanges-feed-description": "اس خورد میں ویکی پر ہونے والی تازہ تریں تبدیلیوں کا مشاہدہ کیجیۓ۔",
-       "recentchanges-label-newpage": "اِس ترمیم نے نیا صفحہ تخلیق کردیا",
+       "recentchanges-label-newpage": "یہ ترمیم ایک نئے صفحے کی تخلیق ہے",
        "recentchanges-label-minor": "یہ ایک معمولی ترمیم ہے",
-       "recentchanges-label-bot": "یہ ایک روبالہ سے سرانجام شدہ ترمیم ہے",
+       "recentchanges-label-bot": "اس ترمیم کو ایک روبہ نے انجام دیا ہے",
        "recentchanges-label-unpatrolled": "اس ترمیم کی اب تک مراجعت نہیں کی گئی",
        "recentchanges-label-plusminus": "صفحہ کا حجم تبدیل شدہ بلحاظ بائٹ مقدار",
        "recentchanges-legend-heading": "'''اختیارات'''",
+       "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (نیز [[Special:NewPages|جدید صفحات کی فہرست]]) ملاحظہ فرمائیں",
        "recentchanges-submit": "دکھائیں",
        "rcnotefrom": "ذیل میں <strong>$3, $4</strong> سے کی گئی {{PLURAL:$5|تبدیلی|تبدیلیاں}} <strong>$1</strong> تک دکھائی جا رہی ہیں۔",
        "rclistfrom": "$3 $2 سےنئی تبدیلیاں دکھانا شروع کریں",
        "imagelinks": "ملف کا استعمال",
        "linkstoimage": "اِس ملف کے ساتھ درج ذیل {{PLURAL:$1|صفحہ مربوط ہے|$1 صفحات مربوط ہیں}}",
        "nolinkstoimage": "ایسے کوئی صفحات نہیں جو اس ملف (فائل) سے رابطہ رکھتے ہوں۔",
+       "sharedupload-desc-here": "یہ ملف $1 سے ہے اور دوسرے منصوبوں میں استعمال ہوسکتا ہے۔\nاِس کے [$2 ملفاتی صفحۂ وضاحت] سے تفصیل درج ذیل ہے۔",
        "upload-disallowed-here": "آپ اوپر چھڑا کر اس ملف کو نہیں لکھ سکتے۔",
        "filedelete-comment": "وجہ:",
        "filedelete-submit": "حذف کریں",
        "statistics-edits-average": "فی صفحہ اوسط ترامیم",
        "statistics-users": "مندرج [[خاص:فہرست صارفین، صارف فہرست|صارفین]]",
        "statistics-users-active": "متحرک صارفین",
+       "pageswithprop-submit": "ٹھیک",
        "doubleredirects": "دوہرے متبادل ربط",
        "brokenredirects": "نامکمل متبادل ربط",
        "brokenredirects-edit": "ترمیم کریں",
        "mostlinkedcategories": "سب سے زیادہ ربط والے زمرہ جات",
        "mostcategories": "سب سے زیادہ زمرہ جات والے مضامین",
        "mostimages": "سب سے زیادہ استعمال کردہ تصاویر",
+       "mostinterwikis": "کثیر اندرونی ربط والے صفحات",
        "mostrevisions": "زیادہ تجدید نظر کیے جانے والے صفحات",
        "prefixindex": "تمام صفحات بمع سابقہ",
+       "prefixindex-namespace": "($1 فضائے نام) کے سابقہ کے ساتھ تمام صفحات",
        "prefixindex-submit": "دکھائیں",
+       "prefixindex-strip": "فضائے نام کے سابقہ کے بغیر نتائج",
        "shortpages": "چھوٹے صفحات",
        "longpages": "طویل ترین صفحات",
        "deadendpages": "مردہ صفحات",
        "protectedpages": "محفوظ شدہ صفحات",
+       "protectedpages-noredirect": "رجوع مکررات چھپائیں",
        "protectedpages-timestamp": "وقت کی مہر",
        "protectedpages-page": "صفحہ",
+       "protectedpages-expiry": "مدت محفوظ شدگی",
+       "protectedpages-performer": "محفوظ کنندہ",
+       "protectedpages-params": "معیار حفاظت",
        "protectedpages-reason": "وجہ",
+       "protectedpages-submit": "صفحات دکھائیں",
        "protectedpages-unknown-timestamp": "نامعلوم",
        "protectedpages-unknown-performer": "نامعلوم صارف",
+       "protectedtitles": "مسدود عنوانات",
+       "protectedtitles-summary": "یہ ان صفحات کی فہرست ہے جن کو تخلیق نہیں کیا جا سکتا۔ یہ عنوانات محفوظ شدہ ہیں، جن کو تخلیق نہیں کیا جا سکتا۔ دیکھیے [[{{#خاص:محفوظ صفحات}}|{{int:protectedpages}}]].",
+       "protectedtitles-submit": "دکھائیں",
        "listusers": "فہرست ارکان",
+       "usereditcount": "$1 {{PLURAL:$1|ترمیم|ترامیم}}",
        "usercreated": "{{GENDER:$3|تخلیق شدہ}}  بتاریخ $1 بوقت $2",
        "newpages": "جدید صفحات",
        "newpages-submit": "دکھائیں",
        "protect-othertime": "دیگر وقت:",
        "protect-othertime-op": "دیگر وقت",
        "protect-otherreason-op": "دیگر وجہ",
-       "protect-expiry-options": "1 گھنٹہ:1 گھنٹہ،1 دن:1 دن،1 ہفتہ:1 ہفتہ،2 ہفتے:2 ہفتے،1 مہینا:1 مہینا،3 مہینے:3 مہینے،6 مہینے:6 مہینے،1 سال:1 سال،لامحدود:لامحدود",
+       "protect-expiry-options": "1 hour:1 hour,1 day:1 day,1 week:1 week,2 weeks:2 weeks,1 month:1 month,3 months:3 months,6 months:6 months,1 year:1 year,infinite:infinite",
        "restriction-type": "اجازت:",
+       "pagesize": "(بائیٹ)",
+       "restriction-edit": "تحریر و ترمیم",
+       "restriction-move": "منتقل",
+       "restriction-create": "تخلیق",
+       "restriction-upload": "زبراثقال",
+       "restriction-level-sysop": "مکمل محفوظ",
+       "restriction-level-autoconfirmed": "نیم محفوظ",
+       "restriction-level-all": "کوئی بھی سطح",
        "undelete": "ضائع کردہ صفحات دیکھیں",
        "undeletepage": "معائنہ خذف شدہ صفحات",
        "undeletepagetitle": "'''ذیل میں [[:$1|$1]] کے حذف شدہ ترامیم درج ہیں۔'''",
        "undeleteviewlink": "دکھاؤ",
        "undeleteinvert": "انتخاب بالعکس",
        "undeletecomment": "وجہ:",
+       "undeletedrevisions": "{{PLURAL:$1|1 نظر ثانی|$1 نظر ثانیاں}} بحال",
+       "undeletedrevisions-files": "{{PLURAL:$1|1 نظر ثانی|$1 نظر ثانیاں}} اور {{PLURAL:$2|1 ملف|$2 املاف}} بحال",
+       "undeletedfiles": "{{PLURAL:$1|1 ملف|$1 املاف}} بحال",
+       "undelete-header": "حالیہ حذف شدہ صفحات کے لیے [[Special:Log/delete|نوشتۂ حذف شدگی]] دیکھیں۔",
+       "undelete-search-title": "حذف شدہ صفحات میں تلاش کریں",
+       "undelete-search-box": "حذف شدہ صفحات میں تلاش کریں",
+       "undelete-search-prefix": "اظہار صفحات بآغاز از:",
        "undelete-search-submit": "تلاش",
+       "undelete-no-results": "حذف شدہ صفحات میں ایسا کوئی صفحہ نہیں ملا",
        "undelete-show-file-submit": "ہاں",
        "namespace": "فضائے نام:",
        "invert": "انتخاب بالعکس",
+       "tooltip-invert": "منتخب شدہ فضائے نام (اور مُلحقہ فضائے نام) میں شامل صفحات کی تبدیلیوں کو چُھپانے کیلئے اِس خانہ کو ٹِک کریں۔",
        "namespace_association": "متعلقہ فضا",
        "blanknamespace": "(مرکز)",
        "contributions": "{{GENDER:$1|صارف}} شراکتیں",
        "contributions-title": "مساہماتِ صارف برائے $1",
        "mycontris": "شراکت",
        "anoncontribs": "شراکتیں",
-       "contribsub2": "براۓ $1 ($2)",
+       "contribsub2": "برائے {{GENDER:$3|$1}} ($2)",
        "uctop": "(موجودہ)",
        "month": "مہینہ (اور اُس سے قبل):",
        "year": "سال (اور اُس سے قبل):",
        "whatlinkshere-hidelinks": "روابط $1",
        "whatlinkshere-hideimages": "روابطِ تصاویر $1",
        "whatlinkshere-filters": "فلٹرذ",
+       "whatlinkshere-submit": "ٹھیک",
        "blockip": "داخلہ ممنوع برائے صارف",
        "blockip-legend": "ممنوع کردہ صارفین",
        "ipbreason": "وجہ:",
        "ipblocklist": "ممنوع صارفین",
        "blocklist-reason": "وجہ",
        "ipblocklist-submit": "تلاش",
+       "infiniteblock": "مستقل",
        "blocklink": "پابندی لگائیں",
        "unblocklink": "پابندی ختم",
        "change-blocklink": "پابندی میں تبدیلی",
        "movepagebtn": "مـنـتـقـل",
        "pagemovedsub": "انتقال کامیاب",
        "movepage-moved": "'''\"$1\" منتقل کردیا گیا بطرف \"$2\"'''",
+       "movepage-moved-redirect": "رجوع مکرر تخلیق کر دیا گیا۔",
        "articleexists": "اس عنوان سے کوئی صفحہ پہلے ہی موجود ہے، یا آپکا منتخب کردہ نام مستعمل نہیں۔ براۓ مہربانی دوسرا نام منتخب کیجیۓ۔",
        "movelogpage": "نوشتۂ منتقلی",
        "movereason": "وجہ:",
        "allmessages-filter-all": "تمام",
        "allmessages-filter-modified": "تبدیل شدہ",
        "allmessages-language": "زبان:",
+       "allmessages-filter-submit": "ٹھیک",
        "allmessages-filter-translate": "ترجمہ",
        "thumbnail-more": "چوڑا کریں",
        "import": "درآمد صفحات",
        "tooltip-diff": "دیکھئے کہ اپنے متن میں کیا تبدیلیاں کیں",
        "tooltip-compareselectedversions": "اِس صفحہ کی دو منتخب نظرثانیوں میں فرق دیکھئے",
        "tooltip-watch": "اِس صفحہ کو اپنی زیرِنظرفہرست میں شامل کریں",
+       "tooltip-rollback": "پچھلے صارف کی کی گئی اِس صفحے پر استرجع شدہ ترامیم کو ایک کلِک میں واپس کریں",
        "tooltip-undo": "''استرجع'' اس ترمیم کو پچھلی ترمیم کے جانب واپس کردیگا اور نمائشی انداز میں خانہ ترمیم کھول دے گا۔ آپ مختصراً سبب بیان کرنے کے بھی مجاز ہونگے۔",
        "tooltip-summary": "مختصر خلاصہ درج کریں",
        "anonymous": "{{SITENAME}} گمنام صارف",
        "weeks": "{{PLURAL:$1|$1ہفتہ| $1  ہفتے}}",
        "bad_image_list": "شکلبند درج ذیل ہے:\n\nصرف فہرستی عناصر (* سے شروع ہونے والی لکیری) شامل کی جاتی ہیں۔\nکسی لکیر میں پہلا ربط کوئی خراب ملف کا ہونا چاہئے۔\nاُسی لکیر میں باقی آنے والے ربط کو مستثنیٰ قرار دیا جاتا ہے، مثلاً صفحات جہاں ملف لکیر کے وسط میں آسکتا ہے۔",
        "metadata": "میٹا ڈیٹا",
+       "metadata-help": "اِس ملف میں اِضافی معلومات شامل ہیں، جو کہ شاید اُس رقمی کیمرے یا سکینر سے آئے ہیں جس کے ذریعے یہ ملف بنائی گئی تھی۔\nاگر ملف اپنی اصل حالت میں نہیں رہی ہے تو کچھ تفاصیل ترمیم شدہ ملف کی مکمل طور پر عکاسی نہیں کرپائیں گے۔",
        "metadata-collapse": "طویل تفاصیل چھپاؤ",
        "exif-orientation": "پیشکش",
        "exif-xresolution": "چھوڑاوی دکھاوت",
        "monthsall": "تمام",
        "deletedwhileediting": "انتباہ: آپ کے ترمیم شروع کرنے کے بعد یہ صفحہ حذف کیا جا چکا ہے!",
        "confirm_purge_button": "جی!",
+       "semicolon-separator": "؛&#32;",
+       "imgmultipageprev": "← پچھلا",
+       "imgmultipagenext": "اگلا →",
+       "imgmultigo": "جائیں!",
+       "imgmultigoto": "$1 صفحہ پر جائیں",
        "table_pager_next": "اگلا صفحہ",
        "table_pager_prev": "پچھلا صفحہ",
        "table_pager_first": "پہلا صفحہ",
index 06da196..84cc4b2 100644 (file)
@@ -9,7 +9,8 @@
                        "Игорь Бродский",
                        "아라",
                        "Macofe",
-                       "Sebranik"
+                       "Sebranik",
+                       "Ghiutun"
                ]
        },
        "tog-underline": "Jonoštada kosketused:",
        "shown-title": "Ozutada $1 {{PLURAL:$1|rezul'tat|rezul'tatad}} lehtpoleks",
        "viewprevnext": "Kacta ($1 {{int:pipe-separator}} $2) ($3)",
        "searchmenu-exists": "'''Neciš Wikiš om jo lehtpol' ningoižen nimenke: \"[[:$1]]\"'''",
-       "searchmenu-new": "<strong>Säta lehtpol' \"[[:$1]]\" neciš Wikiš!<strong> {{PLURAL:$2|0=|Kackat mugažo löutud lehtpol'he.|Kackat mugažo ecindan rezul'tatoihe.}}",
+       "searchmenu-new": "<strong>Säta lehtpol' \"[[:$1]]\" neciš Wikiš!</strong> {{PLURAL:$2|0=|Kackat mugažo löutud lehtpol'he.|Kackat mugažo ecindan rezul'tatoihe.}}",
        "searchprofile-articles": "Südäimištlehtpoled",
        "searchprofile-images": "Mul'timedii",
        "searchprofile-everything": "Kaikjal",
index 37d5cec..a342432 100644 (file)
@@ -68,6 +68,7 @@
        "tog-watchlisthidebots": "Ẩn các sửa đổi của robot khỏi danh sách theo dõi",
        "tog-watchlisthideminor": "Ẩn các sửa đổi nhỏ khỏi danh sách theo dõi",
        "tog-watchlisthideliu": "Ẩn sửa đổi của thành viên đã đăng nhập khỏi danh sách theo dõi",
+       "tog-watchlistreloadautomatically": "Tự động tải lại danh sách theo dõi khi nào bộ lọc được thay đổi (cần JavaScript)",
        "tog-watchlisthideanons": "Ẩn sửa đổi của người dùng vô danh khỏi danh sách theo dõi",
        "tog-watchlisthidepatrolled": "Ẩn sửa đổi đã tuần tra trong danh sách theo dõi",
        "tog-watchlisthidecategorization": "Ẩn việc xếp thể loại",
        "databaseerror-query": "Truy vấn: $1",
        "databaseerror-function": "Hàm: $1",
        "databaseerror-error": "Lỗi: $1",
+       "transaction-duration-limit-exceeded": "Để tránh việc tăng độ trễ sao chép quá mức, giao dịch này bị hủy bỏ vì thời gian ghi ($1) vượt quá giới hạn là $2 giây.\nNếu bạn muốn thay đổi nhiều mục cùng lúc, hãy thử thực hiện nhiều tác vụ nhỏ hơn thay thế.",
        "laggedslavemode": "Cảnh báo: Trang có thể chưa được cập nhật.",
        "readonly": "Cơ sở dữ liệu bị khóa",
        "enterlockreason": "Nêu lý do khóa, cùng với thời hạn khóa",
-       "readonlytext": "Cơ sở dữ liệu hiện đã bị khóa không nhận trang mới và các điều chỉnh khác, có lẽ để bảo trì cơ sở dữ liệu định kỳ, một thời gian ngắn nữa nó sẽ trở lại bình thường.\n\nBảo quản viên khóa nó đã đưa ra lời giải thích sau: $1",
+       "readonlytext": "Cơ sở dữ liệu hiện đã bị khóa không nhận trang mới và các điều chỉnh khác, có lẽ để bảo trì cơ sở dữ liệu định kỳ, một thời gian ngắn nữa nó sẽ trở lại bình thường.\n\nQuản trị viên hệ thống khi khóa nó đã đưa ra lời giải thích sau: $1",
        "missing-article": "Cơ sở dữ liệu không tìm thấy văn bản của trang lẽ ra phải có, trang      Normal   0               false   false   false      EN-US   X-NONE   X-NONE                                                     MicrosoftInternetExplorer4                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     “$1” $2.\n\nĐiều này thường xảy ra do nhấn vào liên kết khác biệt phiên bản đã quá lâu hoặc liên kết lịch sử của một trang đã bị xóa.\n\nNếu không phải lý do trên, có thể bạn đã gặp phải một lỗi của phần mềm.\nXin hãy báo nó cho một [[Special:ListUsers/sysop|bảo quản viên]], trong đó ghi lại địa chỉ URL.",
        "missingarticle-rev": "(số phiên bản: $1)",
        "missingarticle-diff": "(Khác: $1, $2)",
        "title-invalid-interwiki": "Tiêu đề trang yêu cầu có chứa một liên kết liên wiki mà không thể được sử dụng làm tiêu đề.",
        "title-invalid-talk-namespace": "Tiêu đề trang đã yêu cầu chỉ đến trang thảo luận không thể tồn tại.",
        "title-invalid-characters": "Tiêu đề trang đã yêu cầu chứa ký tự không hợp lệ: “$1”.",
-       "title-invalid-relative": "Tiêu đề có đường dẫn tương đối. Tiêu đề trang tương đối (./, ../) là không hợp lệ , bởi chúng thường sẽ không thể đến được khi được xử lý bởi trình duyệt của người dùng.",
+       "title-invalid-relative": "Tiêu đề có đường dẫn tương đối. Tiêu đề trang tương đối (./, ../) là không hợp lệ, bởi chúng thường sẽ không thể đến được khi được xử lý bởi trình duyệt của người dùng.",
        "title-invalid-magic-tilde": "Tiêu đề trang đã yêu cầu chứa dãy dấu ngã không hợp lệ (<nowiki>~~~</nowiki>).",
        "title-invalid-too-long": "Tiêu đề trang đã yêu cầu quá dài. Tiêu đề phải ngắn hơn $1 byte theo mã hóa UTF-8.",
        "title-invalid-leading-colon": "Tiêu đề trang đã yêu cầu chứa dấu hai chấm ở đầu là không hợp lệ.",
        "mypreferencesprotected": "Bạn không có quyền thay đổi tùy chọn của bạn.",
        "ns-specialprotected": "Không thể sửa chữa các trang trong không gian tên {{ns:special}}.",
        "titleprotected": "Tựa đề này đã bị [[User:$1|$1]] khóa không cho tạo ra.\nLý do được cung cấp là ''$2''.",
-       "filereadonlyerror": "Không thể sửa đổi tập tin “$1” vì kho tập tin “$2” đang ở chế độ chỉ-đọc.\n\nBảo quản viên khóa nó đưa lý do là: “$3”.",
+       "filereadonlyerror": "Không thể sửa đổi tập tin “$1” vì kho tập tin “$2” đang ở chế độ chỉ-đọc.\n\nQuản trị viên hệ thống khi khóa nó đưa lý do là: “$3”.",
        "invalidtitle-knownnamespace": "Tựa trang không hợp lệ có không gian tên “$2” và văn bản “$3”",
        "invalidtitle-unknownnamespace": "Tựa trang không hợp lệ có không gian tên số $1 không rõ và văn bản “$2”",
        "exception-nologin": "Chưa đăng nhập",
        "wrongpasswordempty": "Bạn chưa gõ vào mật khẩu. Xin thử lần nữa.",
        "passwordtooshort": "Mật khẩu phải có ít nhất {{PLURAL:$1|1 ký tự|$1 ký tự}}.",
        "passwordtoolong": "Mật khẩu không thể dài hơn {{PLURAL:$1|1 ký tự|$1 ký tự}}.",
+       "passwordtoopopular": "Không thể sử dụng một mật khẩu thường gặp. Xin hãy chọn một mật khẩu riêng biệt hơn.",
        "password-name-match": "Mật khẩu của bạn phải khác với tên người dùng của bạn.",
        "password-login-forbidden": "Tên đăng nhập và mật khẩu này đã bị cấm không được sử dụng.",
        "mailmypassword": "Tái tạo mật khẩu",
        "passwordreset-emailtext-user": "Thành viên $1 tại {{SITENAME}} đã yêu cầu tái tạo mật khẩu tại {{SITENAME}} \n($4). {{PLURAL:$3|Tài khoản|Các tài khoản}} dưới đây gắn liền với địa chỉ thư điện tử này:\n\n$2\n\n{{PLURAL:$3|Mật khẩu|Các mật khẩu}} tạm này sẽ hết hạn trong vòng {{PLURAL:$5|một ngày|$5 ngày}}. Bạn nên đăng nhập\nngay bây giờ để chọn mật khẩu mới. Nếu bạn không phải là người yêu cầu hoặc đã nhớ lại mật khẩu hiện hành, và bạn không còn\nmuốn thay đổi nó, xin vui lòng bỏ qua thông điệp này và tiếp tục sử dụng\nmật khẩu cũ.",
        "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ử để 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ử tương ứng đã được đăng ký, chúng tôi sẽ gửi thông tin để đặt lại mật khẩu qua thư điện tử.",
        "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",
        "changeemail": "Đổi hoặc gỡ địa chỉ thư điện tử",
        "copyrightwarning2": "Xin chú ý rằng tất cả các đóng góp của bạn tại {{SITENAME}} có thể được sửa đổi, thay thế, hoặc xóa bỏ bởi các thành viên khác. Nếu bạn không muốn trang của bạn bị sửa đổi không thương tiếc, đừng đăng trang ở đây.<br />\nBạn phải đảm bảo với chúng tôi rằng chính bạn là người viết nên, hoặc chép nó từ một nguồn thuộc phạm vi công cộng hoặc tự do tương đương (xem $1 để biết thêm chi tiết).\n'''ĐỪNG ĐĂNG TÁC PHẨM CÓ BẢN QUYỀN MÀ CHƯA XIN PHÉP!'''",
        "editpage-cannot-use-custom-model": "Không thể thay đổi kiểu nội dung của trang này.",
        "longpageerror": "'''Lỗi: Văn bạn mà bạn muốn lưu dài $1 kilôbyte, dài hơn độ dài tối đa cho phép $2 kilôbyte.'''\nKhông thể lưu trang.",
-       "readonlywarning": "'''CẢNH BÁO: Cơ sở dữ liệu đã bị khóa để bảo dưỡng, do đó bạn không thể lưu các sửa đổi của mình. Bạn nên cắt-dán đoạn bạn vừa sửa vào một tập tin và lưu nó lại để sửa đổi sau này.'''\n\nBảo quản viên khi khóa dữ liệu đã đưa ra lý do: $1",
+       "readonlywarning": "<strong>CẢNH BÁO: Cơ sở dữ liệu đã bị khóa để bảo dưỡng, do đó bạn không thể lưu các sửa đổi của mình. Bạn nên cắt-dán đoạn bạn vừa sửa vào một tập tin và lưu nó lại để sửa đổi sau này.</strong>\n\nQuản trị viên hệ thống khi khóa dữ liệu đã đưa ra lý do: $1",
        "protectedpagewarning": "'''Cảnh báo: Trang này đã bị khóa và chỉ có các thành viên có quyền quản lý mới có thể sửa được.'''\nThông tin mới nhất trong nhật trình được ghi dưới đây để tiện theo dõi:",
        "semiprotectedpagewarning": "'''Lưu ý:''' Trang này đã bị khóa nên chỉ có các thành viên có tài khoản mới có thể sửa đổi được.\nThông tin mới nhất trong nhật trình được ghi dưới đây để tiện theo dõi:",
        "cascadeprotectedwarning": "'''Cảnh báo:''' Trang này đã bị khóa, chỉ có thành viên có quyền quản lý mới có thể sửa đổi được, vì nó được nhúng vào {{PLURAL:$1|trang|những trang}} bị khóa theo tầng sau:",
        "right-blockemail": "Cấm người dùng gửi thư điện tử",
        "right-hideuser": "Cấm thành viên, rồi ẩn nó đi",
        "right-ipblock-exempt": "Bỏ qua cấm IP, tự động cấm và cấm dải IP",
-       "right-proxyunbannable": "Bỏ qua cấm proxy tự động",
        "right-unblockself": "Tự bỏ cấm",
        "right-protect": "Thay đổi mức khóa và sửa trang khóa theo tầng",
        "right-editprotected": "Sửa trang khóa ở mức “{{int:protect-level-sysop}}”",
        "right-managechangetags": "Tạo và xóa [[Special:Tags|thẻ]] từ cơ sở dữ liệu",
        "right-applychangetags": "Áp dụng [[Special:Tags|thẻ]], cùng với những thay đổi của một người",
        "right-changetags": "Thêm và loại bỏ tùy ý các [[Special:Tags|thẻ]] vào các phiên bản riêng và các mục nhật trình",
+       "grant-generic": "Gói quyền “$1”",
+       "grant-group-page-interaction": "Tương tác với trang",
+       "grant-group-file-interaction": "Tương tác với tập tin",
+       "grant-group-watchlist-interaction": "Tương tác với danh sách theo dõi của bạn",
+       "grant-group-email": "Gửi thư điện tử",
+       "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-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-createeditmovepage": "Tạo, sửa đổi, và di chuyển trang",
+       "grant-delete": "Xóa trang, phiên bản, và mục nhật trình",
+       "grant-editinterface": "Sửa không gian tên MediaWiki và CSS/JavaScript cá nhân",
+       "grant-editmycssjs": "Sửa đổi CSS/JavaScript cá nhân của bạn",
+       "grant-editmyoptions": "Sửa đổi tùy chọn cá nhân của bạn",
+       "grant-editmywatchlist": "Sửa danh sách theo dõi của bạn",
+       "grant-editpage": "Sửa đổi các trang đã tồn tại",
+       "grant-editprotected": "Sửa đội các trang bị khóa",
+       "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-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",
+       "grant-uploadeditmovefile": "Tải lên, thay thế, và di chuyển tập tin",
+       "grant-uploadfile": "Tải lên tập tin mới",
+       "grant-viewdeleted": "Xem các trang và tập tin đã xóa",
+       "grant-viewmywatchlist": "Xem danh sách theo dõi của bạn",
        "newuserlogpage": "Nhật trình mở tài khoản",
        "newuserlogpagetext": "Đây là danh sách những tài khoản thành viên mở lên gần đây.",
        "rightslog": "Nhật trình cấp quyền thành viên",
        "recentchanges-legend-heading": "'''Chú giải:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (xem thêm [[Special:NewPages|danh sách các trang mới]])",
        "recentchanges-legend-plusminus": "(''±123'')",
+       "recentchanges-submit": "Xem",
        "rcnotefrom": "Dưới đây là {{PLURAL:$5|thay đổi duy nhất|các thay đổi}} từ <strong>$3 $4</strong> (hiển thị tối đa <strong>$1</strong> thay đổi).",
        "rclistfrom": "Xem các thay đổi từ $2 $3 trở về sau",
        "rcshowhideminor": "$1 sửa đổi nhỏ",
        "rcshowhidemine": "$1 sửa đổi của tôi",
        "rcshowhidemine-show": "Hiện",
        "rcshowhidemine-hide": "Ẩn",
-       "rcshowhidecategorization": "$1 việc xếp thể loại",
+       "rcshowhidecategorization": "$1 tác vụ xếp thể loại",
        "rcshowhidecategorization-show": "Hiện",
        "rcshowhidecategorization-hide": "Ẩn",
        "rclinks": "Xem $1 sửa đổi gần đây nhất trong $2 ngày qua; $3.",
        "foreign-structured-upload-form-label-own-work-message-shared": "Tôi khẳng định rằng tôi giữ quyền tác giả của tập tin này và đồng ý phát hành, một cách không thể hủy bỏ, tập tin này cho Wikimedia Commons theo giấy phép [https://creativecommons.org/licenses/by-sa/4.0/deed.vi Creative Commons Ghi công–Chia sẻ tương tự 4.0], và tôi chấp nhận các [https://wikimediafoundation.org/wiki/Terms_of_Use/vi?uselang=vi Điều khoản Sử dụng].",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "Nếu bạn không giữ quyền tác giả của tập tin hoặc muốn phát hành nó theo một giấy phép khác, xin nghĩ đến việc sử dụng [https://commons.wikimedia.org/wiki/Special:UploadWizard?uselang=vi Trình thuật sĩ tải lên Commons].",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "Bạn cũng có thể muốn thử [[Special:Upload|trang tải lên tại {{SITENAME}}]] nếu trang đó cho phép tải lên tập tin này theo các quy định của họ.",
+       "foreign-structured-upload-form-2-label-intro": "Cảm ơn bạn đóng góp hình ảnh để sử dụng tại {{SITENAME}}. Hãy chỉ tiếp tục nếu hình này thỏa mãn vài điều khoản:",
+       "foreign-structured-upload-form-2-label-ownwork": "Nó phải hoàn toàn <strong>do bạn tạo ra</strong>, chứ không chỉ lấy từ Internet",
+       "foreign-structured-upload-form-2-label-noderiv": "Nó <strong>không được chứa tác phẩm của người khác</strong> hoặc phỏng theo tác phẩm của người khác",
+       "foreign-structured-upload-form-2-label-useful": "Nó phải <strong>có tính giáo dục và có ích</strong> trong việc chia sẻ kiến thức",
+       "foreign-structured-upload-form-2-label-ccbysa": "Nó phải <strong>được phép xuất bản mãi mãi</strong> trên Internet theo giấy phép [https://creativecommons.org/licenses/by-sa/4.0/deed.vi Creative Commons Ghi công–Chia sẻ tương tự 4.0]",
+       "foreign-structured-upload-form-2-label-alternative": "Nếu bạn đã trả lời “Không” bên trên, có thể là [https://commons.wikimedia.org/wiki/Special:UploadWizard?setlang=vi Trình thuật sĩ tải lên tại Commons] vẫn cho phép tải nó lên, miễn là nó được phát hành theo một giấy phép mở.",
+       "foreign-structured-upload-form-2-label-termsofuse": "Với việc tải lên tập tin này, bạn xác nhận rằng bạn giữ quyền tác giả của tập tin này và đồng ý phát hành tập tin này, một cách không thể hủy bỏ, dưới giấy phép Creative Commons Ghi công–Chia sẻ tương tự 4.0, và bạn chấp nhận [https://wikimediafoundation.org/wiki/Terms_of_Use/vi?uselang=vi các Điều khoản Sử dụng].",
+       "foreign-structured-upload-form-3-label-question-website": "Bạn có tải về hình này từ trang Web nào hoặc lấy nó từ máy tìm kiếm hình ảnh nào?",
+       "foreign-structured-upload-form-3-label-question-ownwork": "Bạn có tự mình tạo hình này (chụp hình, vẽ bức ảnh, v.v.)?",
+       "foreign-structured-upload-form-3-label-question-noderiv": "Nó có chứa hoặc phỏng theo tác phẩm nào do người khác sở hữu, chẳng hạn một biểu trưng?",
+       "foreign-structured-upload-form-3-label-yes": "Có",
+       "foreign-structured-upload-form-3-label-no": "Không",
+       "foreign-structured-upload-form-3-label-alternative": "Đáng tiếc, trong trường hợp này, công cụ này không hỗ trợ việc tải lên tập tin này. Có thể là [https://commons.wikimedia.org/wiki/Special:UploadWizard?setlang=vi Trình thuật sĩ tải lên tại Commons] vẫn cho phép tải nó lên, miễn là nó được phát hành theo một giấy phép mở.",
+       "foreign-structured-upload-form-4-label-good": "Với công cụ này, bạn có thể tải lên các trang minh học có tính giáo dục do bạn tạo ra và các hình ảnh do bạn chụp lấy, miễn là nó không chứa tác phẩm của người khác.",
+       "foreign-structured-upload-form-4-label-bad": "Bạn không được phép tải lên hình ảnh được lấy từ máy tìm kiếm hoặc tải về từ trang Web khác.",
        "backend-fail-stream": "Không thể gửi luồng tập tin $1.",
        "backend-fail-backup": "Không thể sao lưu tập tin $1.",
        "backend-fail-notexists": "Tập tin $1 không tồn tại.",
        "mostrevisions": "Trang được sửa đổi nhiều lần nhất",
        "prefixindex": "Tất cả các trang trùng với tiền tố",
        "prefixindex-namespace": "Tất cả các trang trùng với tiền tố (không gian tên $1)",
+       "prefixindex-submit": "Xem",
        "prefixindex-strip": "Ẩn tiền tố trong danh sách",
        "shortpages": "Trang ngắn nhất",
        "longpages": "Trang dài nhất",
        "protectedpages-performer": "Người dùng khóa",
        "protectedpages-params": "Chế độ khóa",
        "protectedpages-reason": "Lý do",
+       "protectedpages-submit": "Xem các trang",
        "protectedpages-unknown-timestamp": "Không rõ",
        "protectedpages-unknown-performer": "Người dùng không rõ",
        "protectedtitles": "Tên trang bị khóa",
        "protectedtitles-summary": "Danh sách này liệt kê các tên trang bị khóa không được tạo ra. Xem danh sách các trang tồn tại bị khóa tại [[{{#special:ProtectedPages}}|{{int:protectedpages}}]].",
        "protectedtitlesempty": "Không có tựa trang nào bị khóa với các thông số như vậy.",
+       "protectedtitles-submit": "Xem các tên trang",
        "listusers": "Danh sách thành viên",
        "listusers-editsonly": "Chỉ hiện thành viên có tham gia sửa đổi",
        "listusers-creationsort": "Xếp theo ngày khởi tạo",
        "usereditcount": "$1 sửa đổi",
        "usercreated": "{{GENDER:$3}}mở $1 lúc $2",
        "newpages": "Trang mới",
+       "newpages-submit": "Xem",
        "newpages-username": "Tên người dùng:",
        "ancientpages": "Trang cũ nhất",
        "move": "Di chuyển",
        "specialloguserlabel": "Người thực hiện:",
        "speciallogtitlelabel": "Mục tiêu (tiêu đề hoặc {{ns:user}}:Tên-người-dùng đối với người dùng):",
        "log": "Nhật trình",
+       "logeventslist-submit": "Xem",
        "all-logs-page": "Tất cả các nhật trình công khai",
        "alllogstext": "Hiển thị tất cả các nhật trình đang có của {{SITENAME}} chung với nhau.\nBạn có thể thu hẹp kết quả bằng cách chọn loại nhật trình, tên thành viên (phân biệt chữ hoa-chữ thường), hoặc các trang bị ảnh hưởng (cũng phân biệt chữ hoa-chữ thường).",
        "logempty": "Không có mục nào khớp với từ khóa.",
        "cachedspecial-viewing-cached-ts": "Bạn đang xem phiên bản vùng nhớ đệm của trang này có thể không đúng thời hoàn toàn.",
        "cachedspecial-refresh-now": "Xem phiên bản mới nhất.",
        "categories": "Thể loại",
+       "categories-submit": "Xem",
        "categoriespagetext": "{{PLURAL:$1|Thể loại|Các thể loại}} dưới đây có trang hoặc tập tin phương tiện.\nNhững [[Special:UnusedCategories|thể loại trống]] không được hiển thị tại đây.\nXem thêm [[Special:WantedCategories|thể loại cần thiết]].",
        "categoriesfrom": "Hiển thị thể loại bằng đầu từ:",
        "special-categories-sort-count": "xếp theo số trang",
        "activeusers-hidebots": "Ẩn robot",
        "activeusers-hidesysops": "Ẩn bảo quản viên",
        "activeusers-noresult": "Không thấy thành viên.",
+       "activeusers-submit": "Xem người dùng tích cực",
        "listgrouprights": "Nhóm thành viên",
        "listgrouprights-summary": "Dưới đây là danh sách nhóm thành viên được định nghĩa tại wiki này, với mức độ truy cập của từng nhóm.\nCó [[{{MediaWiki:Listgrouprights-helppage}}|thông tin thêm]] về từng nhóm riêng biệt.",
        "listgrouprights-key": "Chú giải:\n* <span class=\"listgrouprights-granted\">Quyền được trao</span>\n* <span class=\"listgrouprights-revoked\">Quyền bị tước</span>",
        "listgrouprights-namespaceprotection-header": "Hạn chế không gian tên",
        "listgrouprights-namespaceprotection-namespace": "Không gian tên",
        "listgrouprights-namespaceprotection-restrictedto": "Quyền cho phép người dùng sửa đổi",
+       "listgrants-grant": "Cấp phép",
+       "listgrants-rights": "Quyền lợi",
        "trackingcategories": "Thể loại phần mềm",
        "trackingcategories-summary": "Đây là danh sách các thể loại được phần mềm MediaWiki tự động xếp trang vào. Các tên thể loại được định rõ trong các thông điệp thuộc không gian tên {{ns:8}}.",
        "trackingcategories-msg": "Thể loại phần mềm",
        "wlnote": "Dưới đây là {{PLURAL:$1|thay đổi duy nhất|<strong>$1</strong> thay đổi gần nhất}} trong {{PLURAL:$2|giờ|<strong>$2</strong> giờ}} qua, tính tới $3 lúc $4.",
        "wlshowlast": "Hiển thị $1 giờ $2 ngày gần đây",
        "watchlistall2": "tất cả",
+       "watchlist-hide": "Ẩn",
+       "watchlist-submit": "Xem",
        "wlshowtime": "Thời gian để hiển thị:",
        "wlshowhideminor": "sửa đổi nhỏ",
        "wlshowhidebots": "bot",
        "wlshowhideanons": "người dùng vô danh",
        "wlshowhidepatr": "sửa đổi đã tuần tra",
        "wlshowhidemine": "sửa đổi của tôi",
+       "wlshowhidecategorization": "tác vụ xếp thể loại",
        "watchlist-options": "Tùy chọn về danh sách theo dõi",
        "watching": "Đang theo dõi…",
        "unwatching": "Đang ngừng theo dõi…",
        "delete-confirm": "Xóa “$1”",
        "delete-legend": "Xóa",
        "historywarning": "<strong>Cảnh báo:</strong> Trang bạn sắp xóa đã có lịch sử $1 phiên bản:",
+       "historyaction-submit": "Xem",
        "confirmdeletetext": "Bạn sắp xóa hẳn một trang cùng với tất cả lịch sử của nó.\nXin xác nhận việc bạn định làm, và hiểu rõ những hệ lụy của nó, và bạn thực hiện nó theo đúng đúng [[{{MediaWiki:Policy-url}}|quy định]].",
        "actioncomplete": "Đã thực hiện xong",
        "actionfailed": "Tác động bị thất bại",
        "whatlinkshere-hidelinks": "$1 liên kết",
        "whatlinkshere-hideimages": "$1 liên kết tập tin",
        "whatlinkshere-filters": "Bộ lọc",
+       "whatlinkshere-submit": "Xem",
        "autoblockid": "Cấm tự động #$1",
        "block": "Cấm người dùng",
        "unblock": "Bỏ cấm người dùng",
        "tooltip-pt-preferences": "Tùy chọn cá nhân của tôi",
        "tooltip-pt-watchlist": "Thay đổi của các trang tôi theo dõi",
        "tooltip-pt-mycontris": "Danh sách các đóng góp của tôi",
+       "tooltip-pt-anoncontribs": "Danh sách các sửa đổi được thực hiện qua địa chỉ  IP này",
        "tooltip-pt-login": "Đăng nhập sẽ có lợi hơn, tuy nhiên không bắt buộc.",
        "tooltip-pt-logout": "Đăng xuất",
        "tooltip-pt-createaccount": "Khuyến khích bạn mở tài khoản và đăng nhập; tuy nhiên, không phải bắt buộc phải có tài khoản",
        "exif-compression-34712": "JPEG 2000",
        "exif-copyrighted-true": "Dưới bản quyền",
        "exif-copyrighted-false": "Cờ bản quyền không được đặt",
+       "exif-photometricinterpretation-1": "Trắng đen (đen là 0)",
        "exif-photometricinterpretation-2": "RGB",
        "exif-unknowndate": "Không biết ngày",
        "exif-orientation-1": "Thường",
        "tags-deactivate": "vô hiệu",
        "tags-hitcount": "$1 thay đổi",
        "tags-manage-no-permission": "Bạn không có quyền hạn để quản lý các thẻ thay đổi.",
+       "tags-manage-blocked": "Bạn không thể quản lý thẻ đánh dấu trong lúc bị cấm.",
        "tags-create-heading": "Tạo một thẻ mới",
        "tags-create-explanation": "Theo mặc định, các thẻ mới được tạo ra sẽ được hợp lệ hóa để người dùng và các bot sử dụng.",
        "tags-create-tag-name": "Tên thẻ:",
        "tags-deactivate-not-allowed": "Không thể vô hiệu hóa thẻ “$1”.",
        "tags-deactivate-submit": "Vô hiệu",
        "tags-apply-no-permission": "Bạn không có quyền áp dụng các thẻ thay đổi cùng các thay đổi của bạn.",
+       "tags-apply-blocked": "Bạn không thể thay đổi các thẻ đánh dấu cùng với các thay đổi của bạn trong lúc bị cấm.",
        "tags-apply-not-allowed-one": "Thẻ “$1” không được phép được áp dụng thủ công.",
        "tags-apply-not-allowed-multi": "{{PLURAL:$2|Thẻ|Các thẻ}} sau không được phép áp dụng thủ công: $1",
        "tags-update-no-permission": "Bạn không có quyền thêm hoặc loại bỏ các thẻ thay đổi từ phiên bản riêng hoặc mục nhật trình.",
+       "tags-update-blocked": "Bạn không thể gắn hoặc gỡ thẻ đánh dấu trong lúc bị cấm.",
        "tags-update-add-not-allowed-one": "Thẻ “$1” không được phép thêm vào thủ công.",
        "tags-update-add-not-allowed-multi": "{{PLURAL:$2|Thẻ|Các thẻ}} sau không được phép thêm thủ công: $1",
        "tags-update-remove-not-allowed-one": "Thẻ đánh dấu “$1” không được phép loại bỏ.",
        "tags-edit-title": "Sửa đổi thẻ",
        "tags-edit-manage-link": "Quản lý thẻ",
        "tags-edit-revision-selected": "{{PLURAL:$1|Phiên bản|Các phiên bản}} [[:$2]] được chọn:",
-       "tags-edit-logentry-selected": "{{PLURAL:$1|Nhật trình đã chọn|Các nhật trình đã chọn}}:",
+       "tags-edit-logentry-selected": "{{PLURAL:$1|Sự kiện|Các sự kiện}} nhật trình đã chọn:",
        "tags-edit-revision-legend": "Thêm và loại bỏ các thẻ từ {{PLURAL:$1|phiên bản này|tất cả $1 phiên bản}}",
        "tags-edit-logentry-legend": "Thêm và loại bỏ thẻ đánh dấu từ {{PLURAL:$1|mục nhật trình này|tất cả $1 mục nhật trình}}",
        "tags-edit-existing-tags": "Thẻ đã có:",
        "logentry-suppress-block": "$1 {{GENDER:$2}}đã cấm {{GENDER:$4}}$3 hết hạn $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2}}đã cấu hình lại vụ cấm {{GENDER:$4}}$3 hết hạn $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2}}đã nhập $3 bằng cách tải lên tập tin",
+       "logentry-import-upload-details": "$1 {{GENDER:$2}}đã nhập $3 bằng cách tải lên tập tin ($4 phiên bản)",
        "logentry-import-interwiki": "$1 {{GENDER:$2}}đã nhập $3 từ một wiki khác",
+       "logentry-import-interwiki-details": "$1 {{GENDER:$2}}đã nhập $3 từ $5 ($4 phiên bản)",
        "logentry-merge-merge": "$1 {{GENDER:$2}}đã hợp nhất $3 vào $4 (các phiên bản cho tới $5)",
        "logentry-move-move": "$1 {{GENDER:$2}}đã đổi $3 thành $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2}}đã đổi $3 thành $4 (đã tắt đổi hướng)",
        "logentry-managetags-delete": "$1 {{GENDER:$2}}đã xóa thẻ “$4” (và gỡ nó khỏi $5 phiên bản hoặc mục nhật trình)",
        "logentry-managetags-activate": "$1 {{GENDER:$2}}đã kích hoạt thẻ “$4” để sử dụng bởi người dùng và các bot",
        "logentry-managetags-deactivate": "$1 {{GENDER:$2}}đã vô hiệu thẻ “$4” để sử dụng bởi người dùng và các bot",
-       "log-name-tag": "Nhật trình đánh dấu",
+       "log-name-tag": "Nhật trình thẻ đánh dấu",
        "log-description-tag": "Trang này hiện khi người dùng vừa thêm hoặc loại bỏ [[Special:Tags|thẻ đánh dấu]] từ các phiên bản hoặc các mục nhật trình riêng biệt. Nhật trình không liệt kê các hoạt động đánh dấu khi chúng là một phần của hoạt động sửa đổi, xóa, hoặc các hoạt động tương tự.",
        "logentry-tag-update-add-revision": "$1 {{GENDER:$2}}đã thêm {{PLURAL:$7|thẻ|các thẻ}} $6 vào phiên bản $4 của trang $3",
        "logentry-tag-update-add-logentry": "$1 {{GENDER:$2}}đã thêm {{PLURAL:$7|thẻ|các thẻ}} $6 vào mục nhật trình $5 của trang $3",
        "pagelang-language": "Ngôn ngữ",
        "pagelang-use-default": "Sử dụng ngôn ngữ mặc định",
        "pagelang-select-lang": "Chọn ngôn ngữ",
+       "pagelang-submit": "Áp dụng",
        "right-pagelang": "Thay đổi ngôn ngữ của trang",
        "action-pagelang": "thay đổi ngôn ngữ của trang",
        "log-name-pagelang": "Nhật trình thay đổi ngôn ngữ",
        "mediastatistics": "Thống kê phương tiện",
        "mediastatistics-summary": "Thống kê về các kiểu tập tin đã tải lên. Bảng này chỉ liệt kê phiên bản mới nhất của các tập tin. Các phiên bản cũ hoặc các phiên bản bị xóa được bỏ qua.",
        "mediastatistics-nbytes": "$1 byte ($2; $3%)",
+       "mediastatistics-bytespertype": "Tổng kích thước tập tin của phần này: $1 byte.",
+       "mediastatistics-allbytes": "Tổng kích thước của tất cả các tập tin: $1 byte.",
        "mediastatistics-table-mimetype": "Kiểu MIME",
        "mediastatistics-table-extensions": "Phần mở rộng có thể",
        "mediastatistics-table-count": "Số tập tin",
        "mediastatistics-header-text": "Văn bản",
        "mediastatistics-header-executable": "Tập tin khả thi",
        "mediastatistics-header-archive": "Định dạng nén",
+       "mediastatistics-header-total": "Tất cả tập tin",
        "json-warn-trailing-comma": "$1 dấu phẩy lủng lẳng được xóa khỏi JSON",
        "json-error-unknown": "JSON có vấn đề. Lỗi: $1",
        "json-error-depth": "Đã vượt quá độ sâu ngăn xếp tối đa",
        "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 đủ."
+       "api-error-blacklisted": "Xin vui lòng chọn một tên khác miêu tả đầy đủ.",
+       "randomrootpage": "Trang gốc ngẫu nhiên"
 }
index f597601..13edba7 100644 (file)
        "october-date": "Oktubre $1",
        "november-date": "Nobyembre $1",
        "december-date": "Disyembre $1",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|Kaarangay|Mga kaarangay}}",
        "category_header": "Mga pakli ha kaarangay nga \"$1\"",
        "subcategories": "Mga ubos-nga-kaarangay",
        "morenotlisted": "Diri kompleto ini nga listahan.",
        "mypage": "Pakli",
        "mytalk": "Mga akon paghingay",
-       "anontalk": "Paghingay para hini nga IP",
+       "anontalk": "Hiruhimangraw",
        "navigation": "Paglayag",
        "and": "&#32;ngan",
        "qbfind": "Bilnga",
        "viewsourcetext": "Puydi ka kumita ngan kumopya han gintikangan han pakli.",
        "viewyourtext": "Puydi nim makit-an ngan makopya an tinikangan <strong>imo mga pagliwat</strong> dinhi nga pakli:",
        "protectedinterface": "Ini nga pakli in nahatag hin teksto hit interface para han software han hin nga wiki, ngan in pinasasaliporan para makalikay hit pag-abuso.\nPara makadugang o makaliwat hin mga paghubad para han tanan nga mga wiki, alayon paggamit han [//translatewiki.net/ translatewiki.net], an kanan MediaWiki proyekto hin lokalisasyon.",
-       "editinginterface": "'''Pahimatngon:''' Imo ginliliwat an pakli nga gingagamit paghatag hin interface text para han software.\nAn mga pagbag-o hini nga pakli in makakaapekto han user interface han iba nga mga gumaramit hini nga wiki.\nPara makadugang o makabag-o han mga paghubad para han ngatanan nga mga wiki, alayon paggamit han [//translatewiki.net/ translatewiki.net], an lokalisasyon nga proyekto han MediaWiki.",
+       "editinginterface": "'''Pahimatngon:''' Imo ginliliwat an pakli nga gingagamit paghatag hin interface text para han software.\nAn mga pagbag-o hinin nga pakli in makakaapekto han itsura han user interface han iba nga mga gumaramit hini nga wiki.",
+       "translateinterface": "Para han pagdugang o pagbag-o han mga paghubad han ngatanan nga mga wiki, alayon paggamit han [//translatewiki.net/ translatewiki.net], an MediaWiki lokalisasyon nga proyekto.",
        "cascadeprotected": "Ini nga pakli in pinapasaliporan hin pagliwat tungod ini in nalalakip ha masunod nga {{PLURAL:$1|pakli, kun diin |mga pakli, kun diin}} pinapasaliporan hit \"cascading\" nga pagpili nga pinaandar:\n$2",
        "namespaceprotected": "Diri ka gintutugutan pagliwat han mga pakli ha ngaran-lat'ang nga '''$1'''.",
        "customcssprotected": "Diri ka gintutugotan pagliwat hini nga CSS nga pakli, tungod nga nagsusulod ini hin kanan iba nga tawo personal nga karuyagon.",
        "yourdomainname": "Imo dominyo:",
        "password-change-forbidden": "Diri ka makakabalyo hin pulong-pagsulod ha dinhi nga wiki.",
        "externaldberror": "Mayda authenticaton database error o diri ka tinutugotan pag-update an imo akwant ha gawas.",
-       "login": "Sakob",
+       "login": "Mag ''log in''",
        "nav-login-createaccount": "Magpalista nga masakob / paghimo hin bag-o nga akawnt",
        "userlogin": "Magpasabot nga masakob / paghimo hin akawnt",
        "userloginnocreate": "Magpasabot nga masakob",
-       "logout": "Gawas",
-       "userlogout": "Gawas",
-       "notloggedin": "Diri sakob",
+       "logout": "Mag ''log out''",
+       "userlogout": "Mag ''log out''",
+       "notloggedin": "Diri naka-log in",
        "userlogin-noaccount": "Waray ka akawnt?",
        "userlogin-joinproject": "Tambong ha {{SITENAME}}",
        "nologin": "Waray ka akawnt? $1.",
        "createaccount-text": "Mayda tawo nga naghimo hin akawnt nga gingamit an imo email address ha {{SITENAME}} ($4) nga ginngaranan nga \"$2\", nga may-ada tigaman-panakob nga \"$3\".\n\nKinahanglan ka maglog-in ngan igbal-iw an imo tigaman-panakob yana dayon.\n\nPuydi nimo pabay-on ini nga mensahe, kun ini nga paghimo hin akwant in nagsayop la.",
        "login-throttled": "Damo na an imo login attempts ha pagkayana.\nAlayon paghulat hin $1 san-o ka umutro.",
        "login-abort-generic": "An imo paglog-in in diri malinamposon - Naundang",
+       "login-migrated-generic": "An imo account in nabalhin, ngan an imo agnay-gumaramit in waray na hinin nga wiki.",
        "loginlanguagelabel": "Pinulongan: $1",
        "suspicious-userlogout": "Waray ka tugoti pag-logout tungod nga baga ini ginpadangat hin usa nga broken browser o caching proxy.",
        "createacct-another-realname-tip": "Ada la ha imo kun karuyag mo igbutang an imo tinuod nga ngaran.\nKun pinili mo ito ighatag, gagamiton ini paghatag hin atribusyon ha gumaramit para hit ira buhat.",
        "copyrightwarning": "Iginpapasabot nga an ngatanan nga imo gin-amot ha {{SITENAME}} iginhatag mo ha ilarom han $2 (kitaa an $1 para han mga detalye).  Kun diri mo igkakalipay nga an imo ginsurat waray kalooy nga liliwaton ngan igpapakalat hit bisan hin-o nga it may gusto, alayon ayaw hiton igsumitir dinhi. <br />\nNasaad ka liwat nga imo ini kalugaringon nga ginsurat, o ginkopya nimo ini tikang ha panimongto nga dominyo o kapareho nga waray-sabit nga kuruhaon.\n'''Ayaw igsumitir an mga buhat nga may ''copyright'' hin waray sarit!'''",
        "copyrightwarning2": "Alayon kasabot nga an ngatanan nga mga kontribusyon ha {{SITENAME}} in puydi liwaton, saliwanon, o tanggalon hin bisan hin-o nga karuyag magbuhat.\nKun diri mo karuyag nga an imo sinurat in maliliwat la hin waray kalooy, ayaw gud igsumite dinhi.<br />\nNasaad ka gihap nga ikaw mismo an nagsurat hini, o ginkopya mo ini ha dominyo publiko o kaparehas nga talwas nga ginkuhaan (kitaa an $1 para hin mga detalye).\n<strong>Ayaw igsumite an mga buhat nga naka-copywrite nga waray pagtugot!</strong>",
        "templatesused": "{{PLURAL:$1|Batakan|Mga batakan}} nga gingamit dinhi nga pakli:",
+       "templatesusedpreview": "{{PLURAL:$1|Batakan|Mga batakan}} nga gingamit hinin nga pahiuna-nga-pagawas:",
+       "templatesusedsection": "{{PLURAL:$1|Batakan|Mga batakan}} nga gingamit hinin nga seksyon:",
        "template-protected": "(pinaliporan)",
        "template-semiprotected": "(katunga nga pinasaliporan)",
        "hiddencategories": "Ini nga pakli in api han {{PLURAL:$1|1 nakatago nga kaarangay|$1 nakatago nga kaarangay}}:",
        "recreate-moveddeleted-warn": "'''Pahimatngon: Naghihimo ka hin pakli nga ginpara na.'''\n\nAngay mo hunahunaon kon naangay ba nga magpadayon hin pagliwat hini nga pakli.\nAn talaan hin pagpara ngan pagbalhin hini nga pakli ginhahatag dinhi para hin masayon nga pagkita:",
        "moveddeleted-notice": "Ini nga pakli in ginpara.\nAn taramdan han pagpara ngan pagbalhin para han pakli in ginhahatag ha ubos para han kasarigan.",
        "log-fulllog": "Kitaa an bug-os nga taramdan",
+       "edit-gone-missing": "Diri nakakaupdate han pakli.\nBaga inin ginpara na.",
        "edit-conflict": "Diri pagkakauroyon han pagliwat.",
        "edit-no-change": "Ginpabay-an an im pagliwat, mahitungod nga waray pagbalyo nga nabuhat ha nakasurat.",
        "postedit-confirmation-created": "Nahimo an pakli.",
+       "postedit-confirmation-restored": "Ginbalik an pakli.",
        "postedit-confirmation-saved": "Natipig an imo ginliwat.",
        "edit-already-exists": "Diri nakakahimo hin bag-o nga pakli.\nAada na ito.",
        "defaultmessagetext": "Aada-nga-daan nga teksto han mensahe",
        "mergehistory-empty": "Waray mga rebisyon in puydi matampo.",
        "mergehistory-no-source": "Waray pa an tinikangan nga pakli nga $1.",
        "mergehistory-no-destination": "Waray pa an kakadtuan nga pakli nga $1.",
+       "mergehistory-autocomment": "Gintampo an [[:$1]] tipakadto ha [[:$2]]",
        "mergehistory-comment": "Gintampo an [[:$1]] ngada ha [[:$2]]: $3",
        "mergehistory-same-destination": "An gintikangan ngan kakadtoan nga mga pakli in diri puydi magkaparo",
        "mergehistory-reason": "Katadungan:",
        "notextmatches": "Waray teksto han pakli an parehas",
        "prevn": "naha-una nga {{PLURAL:$1|$1}}",
        "nextn": "sunod nga {{PLURAL:$1|$1}}",
+       "prev-page": "nahiuna nga pakli",
+       "next-page": "masunod nga pakli",
        "prevn-title": "Nahiuna $1 {{PLURAL:$1|resulta|mga resulta}}",
        "nextn-title": "Sunod nga $1 {{PLURAL:$1|resulta|mga resulta}}",
        "shown-title": "Kitaa $1 {{PLURAL:$1|resulta|mga resulta}} kada pakli",
        "search-result-category-size": "{{PLURAL:$1|1 nga api|$1 nga mga api}} ({{PLURAL:$2|1 nga ubos-nga-kaarangay|$2 nga mga ubos-nga-kaarangay}}, {{PLURAL:$3| 1 nga fayl|$3 nga mga fayl}})",
        "search-redirect": "(redirekta $1)",
        "search-section": "(bahin $1)",
+       "search-category": "(kaarangay $1)",
        "search-suggest": "Buot sidngon mo ba: $1",
        "search-interwiki-caption": "Mga bugto nga proyekto",
        "search-interwiki-default": "Mga resulta tikang han $1:",
        "userrights-reason": "Katadungan:",
        "userrights-no-interwiki": "\nDiri ka gintutugotan pagliwat han mga katungod han gumaramit ha iba nga mga wiki.",
        "userrights-nodatabase": "Waray kaaagii an Database $1 o diri ini aada ha lokal.",
+       "userrights-notallowed": "Waray nim pagtugot hin pagdugang o pagtanggal hin mga katungod han gumaramit.",
        "userrights-changeable-col": "Mga hugpo nga puydi mo labtan",
        "userrights-unchangeable-col": "Mga hugpo nga diri mo puydi labtan",
+       "userrights-removed-self": "Malinamposon nim gintanggal an imo kalugaringon mga katungod. Tungod hito, diri ka na makaka-access hini nga pakli.",
        "group": "Hugpo:",
        "group-user": "Mga gumaramit",
        "group-autoconfirmed": "Mga gumaramit nga lugaring nakokonpirma",
        "group-bot": "Mga bot",
        "group-sysop": "Mga magdudumara",
        "group-bureaucrat": "Mga burokrata",
-       "group-suppress": "Mga nanginginano",
+       "group-suppress": "Mga suppressor",
        "group-all": "(ngatanan)",
        "group-user-member": "{{HENERO:$1|gumaramit}}",
        "group-bot-member": "bot",
        "right-move": "Igbalhin an mga pakli",
        "right-move-subpages": "Igbalhin an pakli lakip an ira mga bahinpakli",
        "right-move-rootuserpages": "Igbalhin an gamot nga mga pakli han gumaramit",
+       "right-move-categorypages": "Balhina an mga kaarangay nga pakli",
        "right-movefile": "Balhina an mga paypay",
        "right-upload": "Igkarga paigbaw an mga paypay",
        "right-reupload": "Sapawa an mga aada nga mga paypay",
        "right-viewmyprivateinfo": "Kitaa an imo kalugaringon nga pribado nga datos (sugad han email address, tinuod nga ngaran)",
        "right-import": "Man-aangbit hin mga pakli tikang ha iba nga mga wiki",
        "right-importupload": "Man-aangbit hin mga pakli tikang ha uska paypay nga iginkarga-pasaka",
+       "right-patrol": "Igmarka an kanan iba mga pagliwat komo ginpatrolya na",
        "right-mergehistory": "Igtampo an kaagi han mga pakli",
        "right-userrights": "Igliwat an ngatanan nga mga katungod han gumaramit",
        "right-userrights-interwiki": "Igliwat an mga katungod han gumaramit han mga gumaramit ha iba nga mga wiki",
        "action-mergehistory": "Igtampo an kaagi hini nga pakli",
        "action-userrights": "Igliwat an ngatanan nga mga katungod han gumaramit",
        "action-sendemail": "Padara hin mga e-mail",
+       "action-editmywatchlist": "igliwat an imo watchlist",
+       "action-viewmywatchlist": "kitaa an imo watchlist",
+       "action-viewmyprivateinfo": "kitaa an imo pribado nga impormasyon",
+       "action-editmyprivateinfo": "igliwat an imo pribado nga impormasyon",
+       "action-editcontentmodel": "igliwat an content model han uska pakli",
+       "action-managechangetags": "himua ngan igpara na mga tag tikang ha database",
        "nchanges": "$1 {{PLURAL:$1|pagbag-o|mga pagbabag-o}}",
        "enhancedrc-history": "kasaysayan",
        "recentchanges": "Mga kabag-ohan",
        "recentchanges-label-plusminus": "An kadako han pakli in nabag-o hin ini nga numero nga mga byte",
        "recentchanges-legend-heading": "'''Leyenda:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (kitaa gihapon [[Special:NewPages|talaan han mga bag-o nga pakli]])",
+       "recentchanges-submit": "Pakit-a",
        "rcnotefrom": "An ha ubos in mga pagbabag-o tikang han <strong>$2</strong> (kutob ngadto ha <strong>$1</strong> nga ginpakita).",
        "rclistfrom": "Pakit-a an mga ginbag-ohan tikang han $3 $2",
        "rcshowhideminor": "$1 gudti nga mga pagliwat",
        "rcshowhidebots-show": "Pakit-a",
        "rcshowhidebots-hide": "Tago-a",
        "rcshowhideliu": "$1 an mga rehistrado nga gumaramit",
+       "rcshowhideliu-show": "Pakit-a",
        "rcshowhideliu-hide": "Tago-a",
        "rcshowhideanons": "$1 waray nagpakilala nga mga gumaramit",
        "rcshowhideanons-show": "Pakit-a",
        "rcshowhideanons-hide": "Tago-a",
        "rcshowhidepatr": "$1 mga pinatrolya nga mga paliwat",
+       "rcshowhidepatr-show": "Pakit-a",
+       "rcshowhidepatr-hide": "Tago-a",
        "rcshowhidemine": "$1 akon mga ginliwat",
        "rcshowhidemine-show": "Pakit-a",
        "rcshowhidemine-hide": "Tago-a",
+       "rcshowhidecategorization-show": "Pakit-a",
+       "rcshowhidecategorization-hide": "Tago-a",
        "rclinks": "Igpakita an katapusan nga $1 nga pagbabag-o ha sulod han urhi nga $2 ka mga adlaw<br />$3",
        "diff": "kaibhan",
        "hist": "kaagi",
        "minoreditletter": "g",
        "newpageletter": "B",
        "boteditletter": "b",
+       "number_of_watching_users_pageview": "[$1 nagbabatay hin {{PLURAL:$1|gumaramit|mga gumaramit}}]",
        "rc_categories_any": "Bisan ano nga",
        "rc-change-size-new": "$1 {{PLURAL:$1|nga byte|nga mga byte}} kahuman han pagbag-o",
        "newsectionsummary": "/* $1 */ bag-o nga bahin",
        "upload-file-error": "Sayop ha sulod",
        "upload-misc-error": "Waray kasasabti nga sayop hin pagkarga-paigbaw",
        "upload-http-error": "Mayda nahitabo nga sayop hin HTTP: $1",
+       "upload-dialog-title": "Ig-upload an file",
        "upload-dialog-button-cancel": "Pasagda",
        "upload-dialog-button-done": "Tima na",
        "upload-dialog-button-save": "Igtipig",
        "download": "pagkarga paubos",
        "unwatchedpages": "Mga paypay nga gintanggal an pagbantay",
        "listredirects": "Talaan hin mga redirect",
+       "listduplicatedfiles": "Lista han mga file nga may kadoble",
        "unusedtemplates": "Waray kagamiti nga mga batakan",
        "unusedtemplateswlh": "iba nga mga sumpay",
        "randompage": "Bisan ano nga pakli",
        "usermaildisabled": "Waray ginpagana an e-mail han gumaramit",
        "usermaildisabledtext": "Diri ka makakapadangat hin e-mail ha iba nga mga gumaramit ha dinhi nga wiki",
        "noemailtitle": "Waray e-mail address",
+       "emailtarget": "Igbutang an agnay-gumaramit hit makarawat",
        "emailusername": "Agnay hiton gumaramit:",
        "emailusernamesubmit": "Igsumite",
        "emailfrom": "Tikang kan:",
        "emailmessage": "Buot igpasabot:",
        "emailsend": "Igpadara",
        "emailccme": "Igemail ako hini nga kopya hit ak buot igpasabot.",
+       "emailccsubject": "Kopya han imo mensahe nga ginpadangat kan $1: $2",
        "emailsent": "Napadara an e-mail",
+       "emailsenttext": "Ginpadara na an imo email nga mensahe.",
        "usermessage-summary": "Nabilin hin mensahe pansistema",
        "usermessage-editor": "Mensahero han sistema",
        "watchlist": "Barantayan",
        "rollbackfailed": "Diri malinamposon an paglibot-pabalik",
        "revertpage": "Ginpabalik an ginliwat ni [[Special:Contributions/$2|$2]] ([[User talk:$2|hiruhimangraw]]) ngadto ha urhi nga pagliwat ni [[User:$1|$1]]",
        "sessionfailure-title": "Pakyas an sesyon",
+       "changecontentmodel-reason-label": "Rason:",
        "protectlogpage": "Talaan han pinasaliporan",
        "protectedarticle": "pinasaliporan \"[[$1]]\"",
        "prot_1movedto2": "[[$1]] in ginbalhin ngadto ha [[$2]]",
index c79ee35..ec65b9a 100644 (file)
        "category-article-count-limited": "下底个{{PLURAL:$1|页面|$1只页面}}属于当前分类。",
        "category-file-count": "{{PLURAL:$2|箇分类便只下头个文件。|箇分类里有下头$1个文件,共$2个文件。}}",
        "category-file-count-limited": "下底个{{PLURAL:$1|文件|$1只文件}}属于当前分类。",
-       "listingcontinuesabbrev": "接落。",
+       "listingcontinuesabbrev": "",
        "index-category": "索引拉许个页面",
        "noindex-category": "朆索引个页",
        "broken-file-category": "有无用文件链接个页",
        "print": "打印",
        "view": "望",
        "view-foreign": "登$1上看",
-       "edit": "编",
+       "edit": "编",
        "edit-local": "编辑本地说明",
        "create": "建",
        "create-local": "添加本地说明",
        "unprotectthispage": "更改此页个保护",
        "newpage": "新页",
        "talkpage": "探討箇頁",
-       "talkpagelinktext": "讨论",
+       "talkpagelinktext": "讲张",
        "specialpage": "特別頁",
        "personaltools": "私人家伙",
        "articlepage": "望内容页",
        "viewhelppage": "望幫忙頁",
        "categorypage": "望分類頁",
        "viewtalkpage": "望探討頁",
-       "otherlanguages": "别样话版",
+       "otherlanguages": "别样闲话版本",
        "redirectedfrom": "(从$1转戳到箇里)",
        "redirectpagesub": "轉戳頁",
        "redirectto": "重定向到:",
-       "lastmodifiedat": "箇页此垡来$1 $2改进。",
+       "lastmodifiedat": "箇只页面阿末趟修订来拉$1 $2。",
        "viewcount": "箇頁望過$1垡。",
        "protectedpage": "畀保护个页面",
        "jumpto": "蹦到:",
        "pool-timeout": "等锁过时",
        "pool-queuefull": "池队列满哉",
        "pool-errorunknown": "弗识个错误",
-       "pool-servererror": "池计数器服务现在弗好用($1)",
+       "pool-servererror": "池计数器服务弗能用($1)。",
        "poolcounter-usage-error": "用法出错:$1",
        "aboutsite": "有关{{SITENAME}}",
        "aboutpage": "Project:关于",
        "viewsourceold": "望源碼",
        "editlink": "编",
        "viewsourcelink": "望源码",
-       "editsectionhint": "编段: $1",
+       "editsectionhint": "编辑章节:$1",
        "toc": "目录",
        "showtoc": "顯示",
        "hidetoc": "囥脫",
        "laggedslavemode": "警告: 页面可能弗包含最近个更新。",
        "readonly": "數據庫鎖牢",
        "enterlockreason": "请输入锁定个原因,包括预计解锁个辰光",
-       "readonlytext": "数据库目前禁止输入新内容及更改,\n箇蛮有可能是因为数据库拉许维修,完成仔即可恢复。\n\n管理员有如下解释:$1",
+       "readonlytext": "数据库目前锁牢勒上,禁止输入新内容及更改,箇蛮有可能是因为数据库拉许维修,完成仔即可恢复。\n\n系统管理员有如下解释:$1",
        "missing-article": "数据库寻弗着想寻个页面文本:名字“$1”$2。\n\n箇一般是由于点击了链向旧有差异或历史个链接,而原有修订已拨删除导致个。\n\n如果弗是箇种情况,你侬作兴寻着软件里一个错误。畀URL地址记落来,搭[[Special:ListUsers/sysop|管理员]]报告。",
        "missingarticle-rev": "(版本#:$1)",
        "missingarticle-diff": "(两样:$1、$2)",
        "title-invalid-empty": "请求个页面标题是空个,或着只包括名字空间个名称。",
        "title-invalid-utf8": "请求个页面标题包括一只无效个UTF-8序列。",
        "title-invalid-interwiki": "请求个页面标题包含跨wiki个链接,伊弗好用于标题。",
-       "title-invalid-talk-namespace": "请求个页面标题引用子一只弗好存在个讨论页面。",
+       "title-invalid-talk-namespace": "请求个页面标题引用著一只弗能存在个讨论页。",
        "title-invalid-characters": "请求个页面标题包括无效字符:“$1”。",
        "title-invalid-relative": "标题有相对个路径。相关个页面标题(./, ../)呒没效果,因为用户浏览器经常呒没办法到达这些页面。",
        "title-invalid-magic-tilde": "请求个页面标题包含呒没效果个连续波浪线(<nowiki>~~~</nowiki>)。",
        "mypreferencesprotected": "你个私人偏好你呒处编。",
        "ns-specialprotected": "特殊页编辑是弗来三个。",
        "titleprotected": "箇只标题已经拨[[User:$1|$1]]保护以防止创建。理由是''$2''。",
-       "filereadonlyerror": "\"$1\"文件呒处改,文件存勒 \"$2\" 是只读模式。管理员考虑畀渠锁牢个理由是:\"$3\"。",
+       "filereadonlyerror": "“$1”文件呒处改,因为文件库“$2”是只读模式。\n\n锁牢数据库个系统管理员解释如下:“$3”。",
        "invalidtitle-knownnamespace": "非法个题目头,有名字空间$2搭文字$3",
        "invalidtitle-unknownnamespace": "非法个题目头,有弗识个数字$1搭文字$2",
        "exception-nologin": "朆登录",
        "welcomecreation-msg": "你个账号建起来哉。\n覅忘记哉走去改你个[[Special:Preferences|{{SITENAME}}个私人偏好]]。",
        "yourname": "用户名:",
        "userlogin-yourname": "用户名",
-       "userlogin-yourname-ph": "æ\89\93è¿\9b你侬个ç\94¨æ\88·å\90\8d",
+       "userlogin-yourname-ph": "打进侬个用户名",
        "createacct-another-username-ph": "打进用户名",
-       "yourpassword": "密码:",
+       "yourpassword": "密码",
        "userlogin-yourpassword": "密码",
        "userlogin-yourpassword-ph": "密码打进去",
-       "createacct-yourpassword-ph": "密码打进去",
+       "createacct-yourpassword-ph": "打进密码",
        "yourpasswordagain": "密码再打一遍:",
        "createacct-yourpasswordagain": "确认密码",
        "createacct-yourpasswordagain-ph": "再打一遍密码",
        "createaccountreason": "理由:",
        "createacct-reason": "理由:",
        "createacct-reason-ph": "为何物建别样账号",
-       "createacct-submit": "建侬个账号",
+       "createacct-submit": "建侬个账号",
        "createacct-another-submit": "建立账号",
-       "createacct-benefit-heading": "{{SITENAME}} 是搭你侬样个人建起个。",
+       "createacct-benefit-heading": "{{SITENAME}}靠像侬一样个人建立。",
        "createacct-benefit-body1": "{{PLURAL:$1|编写}}",
        "createacct-benefit-body2": "{{PLURAL:$1|页}}",
-       "createacct-benefit-body3": "此垡 {{PLURAL:$1|出力个人}}",
+       "createacct-benefit-body3": "此垡{{PLURAL:$1|出力个人}}",
        "badretype": "侬输入个密码弗匹配。",
        "usernameinprogress": "迭个用户名个账户创建已经勒了进行。请侬等一等。",
        "userexists": "输入个用户名有人用哉。请再选个两样个名字。",
        "mailerror": "发送邮件错误:$1",
        "acct_creation_throttle_hit": "弗好意思,使用箇只IP个访客已经创建仔$1只账号,迭个是箇段辰光里向所允许个最大值。箇咾使用箇只IP个地址个访客暂时弗好再创建账户。",
        "emailauthenticated": "侬个电子邮箱地址已经垃拉$2 $3确认。",
-       "emailnotauthenticated": "侬个电子邮箱地址还朆确认。\n下底个功能弗会发送任何邮件。",
+       "emailnotauthenticated": "侬个电子邮箱地址还朆确认。下底个功能弗会发送任何邮件。",
        "noemailprefs": "指定一只电子邮箱地址以使用箇眼功能。",
        "emailconfirmlink": "确认邮箱地址",
        "invalidemailaddress": "邮箱地址格式弗对,请输入正确个邮箱地址或清空输入框。",
        "login-throttled": "你侬试登忒多次哉。\n等 $1 再试试凑相。",
        "login-abort-generic": "登录弗成功 - 已终止",
        "login-migrated-generic": "侬个账号已经畀移脱哉,并且侬个用户名来箇wiki弗再存在。",
-       "loginlanguagelabel": "语言:$1",
+       "loginlanguagelabel": "闲话:$1",
        "suspicious-userlogout": "侬登出个要求已经拨回头脱,因为渠可能是由已损坏个浏览器或者缓存代理传送个。",
        "createacct-another-realname-tip": "真实姓名是选填个项目。\n假使侬选择提供伊,伊会得用勒了贡献署名方面高头。",
        "pt-login": "登录",
        "pt-login-button": "登录",
        "pt-createaccount": "建账号",
        "pt-userlogout": "登出",
-       "user-mail-no-addy": "尝试发送邮件而弗附带电子邮件个地址。",
-       "user-mail-no-body": "试图发送空个或者主体短的一点也弗合理个电子邮件",
+       "user-mail-no-addy": "尝试发送电子邮件而弗带地址。",
+       "user-mail-no-body": "尝试发送空个或者短得弗合理个电子邮件",
        "changepassword": "改密码",
        "resetpass_announce": "要完成登录,侬必须设定一只新密码。",
        "resetpass_header": "更改密码",
        "oldpassword": "旧密码:",
-       "newpassword": "新密码:",
-       "retypenew": "再打一遍新密码:",
+       "newpassword": "新密码",
+       "retypenew": "再打一遍新密码",
        "resetpass_submit": "设置密码再登录",
        "changepassword-success": "密碼改好哉!\n能界登錄當中...",
        "changepassword-throttled": "侬试登录忒多次哉。等$1再试试看。",
        "resetpass-submit-loggedin": "更改密码",
        "resetpass-submit-cancel": "取消",
        "resetpass-wrong-oldpass": "无效个临时或者现有密码。\n侬作兴已经成功拿密码改脱,或者已经请求一个新个临时密码。",
-       "resetpass-recycled": "请é\87\8d置侬个å¯\86ç \81æ\98¯å¿\92侬å½\93å\89\8då¯\86ç \81ä¸\8då\90\8c个密码。",
+       "resetpass-recycled": "请é\87\8dç½®ä¸\80å\8fªæ\90­ä¾¬å½\93å\89\8då¯\86ç \81å¼\97ä¸\80æ ·个密码。",
        "resetpass-temp-password": "临时密码:",
        "resetpass-abort-generic": "密码更改已经畀扩展程序中止。",
        "resetpass-expired": "侬个密码到期哉。请设置新个登录密码。",
        "extlink_tip": "外部链接(前头记牢加 http://)",
        "headline_sample": "标题文本",
        "headline_tip": "2级标题文字",
-       "nowiki_sample": "箇里插入非格式文本",
+       "nowiki_sample": "箇里插入非格式文本",
        "nowiki_tip": "弗管wiki格式",
        "image_tip": "嵌入文件",
        "media_tip": "文件链接",
        "watchthis": "关注箇页",
        "savearticle": "保存页面",
        "preview": "望望相",
-       "showpreview": "显示望望相",
+       "showpreview": "显示预览",
        "showdiff": "显示变化",
        "blankarticle": "<strong>警告:</strong>侬要创建个页面是空白个。如果侬再次点击“{{int:savearticle}}”,一只呒不任何内容个页面会畀创建。",
-       "anoneditwarning": "<strong>è­¦å\91\8aï¼\9a</strong>ä½ å\91\92ä¸\8dç\99»å½\95ã\80\82å¦\82æ\9e\9cä½ å\81\9aä»\94å\95¥ç¼\96è¾\91ï¼\8cç®\87ä¹\88你个IPå\9c°å\9d\80ä¼\9aå\85¬å¼\80å\8f¯è§\81ã\80\82å¦\82æ\9e\9cä½ <strong>[$1 ç\99»å½\95]</strong>æ\88\96<strong>[$2 å\88\9b建]</strong>ä¸\80个账å\8f·ï¼\8c你个ç¼\96è¾\91ä¼\9aå½\92å\8a\9fäº\8eä½ ç\94¨æ\88·å\90\8dä¸\8båº\95ï¼\8cè\80\8cä¸\94ä¼\9aæ\9c\89å\85¶ä»\96好å¤\84。",
+       "anoneditwarning": "<strong>è­¦å\91\8aï¼\9a</strong>侬å¼\97æ\9b¾ç\99»å½\95ã\80\82å¦\82æ\9e\9c侬å\81\9aä»\94å\95¥ç¼\96è¾\91ï¼\8cç®\87ä¹\88侬个IPå\9c°å\9d\80ä¼\9aå\85¬å¼\80å\8f¯è§\81ã\80\82å¦\82æ\9e\9c侬<strong>[$1 ç\99»å½\95]</strong>æ\88\96<strong>[$2 å\88\9b建ä¸\80å\8fªè´¦å\8f·]</strong>ï¼\8c侬个ç¼\96è¾\91ä¼\9aå½\92å\8a\9fäº\8e侬ç\94¨æ\88·å\90\8dä¸\8båº\95ï¼\8cè\80\8cä¸\94ä¼\9aæ\9c\89å\85¶ä»\96ä¼\98ç\82¹。",
        "anonpreviewwarning": "''侬弗曾登录。侬个IP位址会得记录拉此页个编辑历史里向。''",
        "missingsummary": "'''提示:''' 侬弗曾提供编辑摘要。假使侬再次单击保存,侬个编辑将弗带编辑摘要保存。",
-       "selfredirect": "<strong>警告:</strong>侬来上拿本页面重定向到它自家。\n侬可能搞错重定向目标,或者侬来上编辑错个页面。\n如果侬再次点击“{{int:savearticle}}”,重定向弗管哪亨会畀创建。",
+       "selfredirect": "<strong>警告:</strong>侬来上拿本页面重定向到它自家。侬可能搞错著重定向个目标,或者侬来上编辑错个页面。如果侬再次点击“{{int:savearticle}}”,重定向弗管哪亨会畀创建。",
        "missingcommenttext": "请垃下头输入备注。",
        "missingcommentheader": "<strong>提示:</strong>侬弗曾为此评论提供标题。如果侬再次单击“{{int:savearticle}}”,侬个编辑将弗带标题保存。",
        "summary-preview": "摘要预览:",
        "newarticletext": "倷跟仔链接来着一个还弗勒里个页面。\n要创建该页面呢,就勒下底个框框里向开始写([$1 帮助页面]浪有更加多个信息)。\n要是倷是弗用心到该搭个说话,只要点击倷浏览器个'''返回'''揿钮。",
        "anontalkpagetext": "---- ''箇是一个还弗曾建立账户个匿名用户个讨论页, 箇咾我伲只好用IP地址来搭渠联络。该IP地址可能由几名用户共享。如果侬是一名匿名用户并认为箇只页面高头个评语搭侬弗搭界,请 [[Special:UserLogin/signup|创建新账户]]或[[Special:UserLogin|登录]]来避免垃拉将来搭其他匿名用户混淆。''",
        "noarticletext": "箇页目前呒有文本。\n你侬好来别个页[[Special:Search/{{PAGENAME}}|搜寻箇页标题]]、<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜寻相关日志]要勿[{{fullurl:{{FULLPAGENAME}}|action=edit}} 编箇页]。</span>",
-       "noarticletext-nopermission": "箇页目前还呒有文本。\n你侬好徕别个页[[Special:Search/{{PAGENAME}}|搜寻箇页标题]],\n要勿<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 搜寻相关日志]</span>,暂时弗允许你侬建箇页。",
+       "noarticletext-nopermission": "箇只页面目前还呒不文本。侬好来别个页面[[Special:Search/{{PAGENAME}}|寻箇页标题]],或者<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} 寻相关日志]</span>,但必过侬呒不权限建立箇只页面。",
        "userpage-userdoesnotexist": "用户账户“<nowiki>$1</nowiki>”弗曾创建。请垃拉创建/编辑迭个页面前头先检查一记。",
        "userpage-userdoesnotexist-view": "用户账户“$1”弗曾创建。",
        "blocked-notice-logextract": "箇位用户箇歇畀封锁垃许。\n下头有最近个封锁纪录以供参考:",
-       "clearyourcache": "'''注意:垃拉保存之后,侬必须清除浏览器个缓存再好看见所作出个改变。'''\n'''Mozilla / Firefox / Safari''':揿牢''Shift''再点击''刷新'',或揿''Ctrl-F5''或''Ctrl-R''(垃拉Mac上揿 ''Command-R'');\n'''Konqueror''':只需点击''刷新''或揿''F5'';\n'''Opera''':垃拉 ''工具→首选项''里向完整清除渠拉个缓存,或揿''Alt-F5'';\n'''Internet Explorer''':揿牢''Ctrl''再点击''刷新'',或揿''Ctrl-F5''。",
+       "clearyourcache": "<strong>注意:</strong>垃拉保存之后,侬作兴要清除浏览器个缓存再好看见改变。\n* <strong>Firefox或Safari:</strong>揿牢“Shift”个同时点击“刷新”,或揿“Ctrl-F5”或“Ctrl-R”(Mac上是“⌘-R”)\n* <strong>Google Chrome:</strong>揿“Ctrl-Shift-R”(Mac上是“⌘-Shift-R”)\n* <strong>Internet Explorer:</strong>揿牢“Ctrl”个同时点击“刷新”,或揿“Ctrl-F5”\n* <strong>Opera:</strong>垃拉“工具→首选项”里向清除缓存",
        "usercssyoucanpreview": "'''提示:''' 垃拉保存之前请用“{{int:showpreview}}”揿钮来测试新 CSS 。",
        "userjsyoucanpreview": "'''提示:''' 垃拉保存之前请用“{{int:showpreview}}”揿钮来测试新 JavaScript 。",
        "usercsspreview": "'''注意侬只是垃许预览侬个 CSS。'''\n'''还弗曾保存!'''",
        "session_fail_preview": "'''弗好意思!由于会话数据落失,我伲弗好处理侬个编辑。'''请重试。如果再次失败,请尝试[[Special:UserLogout|登出]]之后重新登录。",
        "session_fail_preview_html": "'''弗好意思!我伲弗好处理侬垃拉进程数据落失辰光个编辑。'''\n\n''由于{{SITENAME}}允许使用原始个 HTML,为著防范 JavaScript 攻击,预览已畀隐藏。''\n\n'''如果这是一次合法的编辑,请重新进行尝试。'''如果还不行,请 [[Special:UserLogout|退出]]并重新登录。",
        "token_suffix_mismatch": "'''由于侬用户端里向个编辑令牌毁损仔一些标点符号字元,为防止编辑个文字损坏,侬个编辑已经畀回头。'''\n箇种情况通常出现垃拉使用含有交关bug、以网络为主个匿名代理服务个辰光。",
-       "editing": "来里编$1",
+       "editing": "来里编$1",
        "creating": "创建“$1”",
        "editingsection": "来里编辑$1(段落)",
        "editingcomment": "垃许编辑 $1 (新段落)",
        "editconflict": "编辑冲突: $1",
-       "explainconflict": "有人垃拉侬开始编辑之后更改仔页面。\n上头个文字框内显示个是箇歇本页个内容。\n侬个修改显示垃拉下底只文字框里向。\n侬应当拿侬个修改加入到现有个内容里向。\n<b>只有</b>上头文字框里向个内容会得垃侬点击\"保存页面\"之后畀保存。",
+       "explainconflict": "有人垃拉侬开始编辑之后更改仔页面。上头个文字框内显示个是箇歇本页个内容。侬个修改显示垃拉下底只文字框里向。侬应当拿侬个修改合并到现有个文本里向。<strong>只有</strong>上头文字框里向个内容会得垃侬点击“{{int:savearticle}}”之后畀保存。",
        "yourtext": "侬个文字",
        "storedversion": "已保存版本",
        "nonunicodebrowser": "'''警告:侬个浏览器弗兼容Unicode编码。'''箇搭有一只工作区将使侬可以安全编辑页面:非ASCII字符将以十六进制编码方式出现垃拉编辑框里向。",
        "editingold": "''' 注意:倷勒里改动一只已经过期个页面修改。 如果倷保存俚个说话,勒拉该个修改之后个亨白浪当个修改侪会呒拨个。'''",
        "yourdiff": "两样",
-       "copyrightwarning": "请注æ\84\8f你侬对{{SITENAME}}个ä¸\80å\88\87è´¡ç\8c®å\85¨å¿\85é¡»å¾\95$2ä¸\8b头å\8f\91å¸\83ï¼\8cæ\9f¥$1æ\9c\9bç»\86è\8a\82ã\80\82\nå\81\87使你侬å¼\97æ\83³è\87ªå·±ä¸ªæ\96\87å­\97é\81­å\88°é\9a\8fæ\84\8fä¿®æ\94¹æ\90­è½¬å\8f\91ï¼\8cè¦\85æ\8f\90交ä¸\8aæ\9d¥ã\80\82<br />\n你侬ä¹\9fè¦\81å\90\91æ\88\91é\87\8cä¿\9dè¯\81ï¼\8cç®\87æ\98¯ä½ ä¾¬è\87ªå®¶å\86\99个ï¼\8cè¦\81å\8b¿ä»\8eå¼\97å\8f\97ç\89\88æ\9d\83ä¿\9dæ\8a¤ä¸ªè¦\81å\8b¿å·®å¼\97å¤\9a个è\87ªç\94±èµ\84æº\90æ\9d¥ã\80\82\n'''è¦\85å¾\95æ\9c\86è\8e·å¾\97æ\8e\88æ\9d\83个æ\83\85å\86µä¸\8bå\8f\91表ï¼\81'''<br />",
+       "copyrightwarning": "请注æ\84\8f侬对{{SITENAME}}个æ\89\80æ\9c\89è´¡ç\8c®ä¾ªå¿\85é¡»å\9e\83æ\8b\89$2ä¸\8b头å\8f\91å¸\83ï¼\88请æ\9f¥ç\9c\8bå\9e\83æ\8b\89$1个ç»\86è\8a\82ï¼\89ã\80\82å\81\87使侬å¼\97å¸\8cæ\9c\9b侬个æ\96\87å­\97ç\95\80ä»»æ\84\8fä¿®æ\94¹æ\90­å\86\8då\8f\91å¸\83ï¼\8c请å¼\97è¦\81æ\8f\90交ã\80\82<br />\n侬å\90\8cæ\97¶ä¹\9fè¦\81å\90\91é\98¿æ\8b\89ä¿\9dè¯\81侬æ\89\80æ\8f\90交个å\86\85容æ\98¯ä¾¬è\87ªå®¶æ\89\80ä½\9cï¼\8cæ\88\96å¾\97è\87ªä¸\80个å¼\97å\8f\97ç\89\88æ\9d\83ä¿\9dæ\8a¤æ\88\96ç\9b¸ä¼¼è\87ªç\94±ä¸ªæ\9d¥æº\90ã\80\82<strong>å¼\97è¦\81å\9e\83æ\8b\89å¼\97æ\9b¾è\8e·å¾\97æ\8e\88æ\9d\83个æ\83\85å\86µä¸\8b头å\8f\91表ï¼\81</strong>",
        "copyrightwarning2": "请注意侬对{{SITENAME}}个所有贡献\n侪可能畀别个贡献者编辑,修改或删除。\n假使侬弗希望侬个文字畀任意修改搭仔再发布,请弗要提交。<br />\n侬同时也要向我伲保证侬提交个内容是侬自家所作,或得自一个弗受版权保护或相似自由个来源(参阅$1个细节)。\n''' 弗要垃拉弗曾获得授权个情况下头发表!'''",
-       "longpageerror": "'''错误:侬提交个文本长度有$1KB,大于$2KB个顶大值。'''该文本弗能保存。",
-       "readonlywarning": "<strong>警告:数据库锁定垃许维护,侬箇歇弗好保存侬个修改。</strong>侬作兴希望先拿侬个文字复制并保存到文本文件,等歇再修改。\n\n锁牢数据库个管理员有如下解释:$1",
-       "protectedpagewarning": "'''警告:此页已经畀保护,只有拥有管理员权限个用户再好修改。'''\n最近个日志垃拉下底提供以便参考:",
+       "longpageerror": "<strong>错误:侬提交个文本长度有$1KB,大于$2KB个顶大值。</strong>该文本弗能保存。",
+       "readonlywarning": "<strong>è­¦å\91\8aï¼\9aæ\95°æ\8d®åº\93é\94\81å®\9aå\9e\83许维æ\8a¤ï¼\8c侬ç®\87æ­\87å¼\97好ä¿\9då­\98侬个修æ\94¹ã\80\82</strong>侬ä½\9cå\85´å¸\8cæ\9c\9bå\85\88æ\8b¿ä¾¬ä¸ªæ\96\87å­\97å¤\8då\88¶å¹¶ä¿\9då­\98å\88°æ\96\87æ\9c¬æ\96\87件ï¼\8cç­\89æ­\87å\86\8dä¿®æ\94¹ã\80\82\n\né\94\81ç\89¢æ\95°æ\8d®åº\93个系ç»\9f管ç\90\86å\91\98æ\9c\89å¦\82ä¸\8b解é\87\8aï¼\9a$1",
+       "protectedpagewarning": "<strong>警告:此页已经畀保护,只有拥有管理员权限个用户再好修改。</strong>最近个日志垃拉下底提供以便参考:",
        "semiprotectedpagewarning": "'''注意:''' 本页面畀锁定,仅限注册用户编辑。\n最近个日志垃拉下底提供以便参考:",
        "cascadeprotectedwarning": "<strong>警告:</strong>本页已经畀保护,只有拥有管理员权限个用户再好修改,因为本页已畀下底眼级联保护个{{PLURAL:$1|一只|多只}}页面所嵌入:",
        "titleprotectedwarning": "'''警告:本页面已畀锁定,需要[[Special:ListGroupRights|指定权限]]方可创建。'''\n最近个日志垃拉下底提供以便参考:",
        "permissionserrorstext": "为仔下头个{{PLURAL:$1|原因|原因}}咾侬无权进行箇只操作:",
        "permissionserrorstext-withaction": "为仔下头个{{PLURAL:$1|原因|原因}}咾侬无权进行$2操作:",
        "recreate-moveddeleted-warn": "'''警告: 你侬要转建一个之前删脱过个页面。'''\n\n你侬应该要考虑考虑继续编箇页是否合适。\n方便考虑,箇页个删记录提供到下头:",
-       "moveddeleted-notice": "箇页删脱哉。\n箇页个删搭移个日志徕下头提供以便参考。",
+       "moveddeleted-notice": "箇页删脱哉。箇页个删除搭移动记录提供垃拉下头以便参考。",
        "log-fulllog": "查看完整日志",
        "edit-hook-aborted": "编辑畀钩子取消。\n渠弗曾畀出解释。",
        "edit-gone-missing": "弗好更新页面。\n渠作兴齐巧畀删除。",
        "currentrev-asof": "于$1个最新修订版",
        "revisionasof": "垃拉$1所作出个修订版",
        "revision-info": "{{GENDER:$6|$2}}$1个版本$7",
-       "previousrevision": "←还旧版",
+       "previousrevision": "←旧点个版本",
        "nextrevision": "新点个版本→",
-       "currentrevisionlink": "最版本",
+       "currentrevisionlink": "最版本",
        "cur": "当前",
        "next": "后头",
        "last": "上个",
        "page_first": "最前",
-       "page_last": "末",
+       "page_last": "末",
        "histlegend": "选择比较版本:标记要比较个两只版本,回车或者揿页面底里个揿钮。<br /> 图例:(当前) = 搭当前版本有啥两样, (上个) = 搭上个版本有啥两样,小 = 小改动。",
        "history-fieldset-title": "浏览页史",
        "history-show-deleted": "只准删脱个",
        "textmatches": "页面内容匹配",
        "notextmatches": "呒没匹配个页面文本",
        "prevn": "前$1个",
-       "nextn": "下个 {{PLURAL:$1|$1}}",
+       "nextn": "下$1个",
        "prev-page": "上页",
        "next-page": "下页",
        "prevn-title": "前$1个结果",
        "search-section": "(段落 $1)",
        "search-category": "(分类$1)",
        "search-file-match": "(匹配文件内容)",
-       "search-suggest": "你侬æ\98¯寻:$1",
+       "search-suggest": "侬å\95\8aæ\98¯æ\9d¥ä¸\8a寻:$1",
        "search-rewritten": "显示$1个结果。另寻$2。",
        "search-interwiki-caption": "姊妹项目",
        "search-interwiki-default": "来自$1个结果:",
        "stub-threshold": "短链接格式阈值($1):",
        "stub-threshold-disabled": "停用",
        "recentchangesdays": "“近段辰光个改动”当中显示几日天:",
-       "recentchangesdays-max": "最长 $1 日",
+       "recentchangesdays-max": "顶多$1天",
        "recentchangescount": "默认显示个编辑数:",
        "prefs-help-recentchangescount": "迭个包括近段辰光个改动、页面历史搭著日志。",
        "savedprefs": "倷个偏好已经保存哉。",
        "timezonelegend": "时区:",
        "localtime": "当地辰光:",
        "timezoneuseserverdefault": "使用wiki默认值($1)",
-       "timezoneuseoffset": "其(指定时差)",
+       "timezoneuseoffset": "其(指定时差)",
        "servertime": "服务器辰光:",
        "guesstimezone": "从浏览器填写",
        "timezoneregion-africa": "非洲",
        "prefs-custom-js": "自定义JavaScript",
        "prefs-common-css-js": "所有皮肤一道用个CSS/JavaScript:",
        "prefs-emailconfirm-label": "电子邮件确认:",
-       "youremail": "电子信箱:",
+       "youremail": "电子信箱",
        "username": "{{GENDER:$1|用户名}}:",
        "prefs-registration": "注册辰光:",
-       "yourrealname": "真名字:",
-       "yourlanguage": "语言:",
-       "yournick": "绰号:",
+       "yourrealname": "真名字",
+       "yourlanguage": "界面语言:",
+       "yournick": "新签名:",
        "badsig": "无效原始签名;检查 HTML 标签。",
        "yourgender": "侬希望畀哪亨称呼?",
        "gender-unknown": "提到侬个辰光,软件会尽量用性别中立个词汇",
        "right-editmyprivateinfo": "编辑侬个私人数据(如电子邮件地址、真实姓名)",
        "right-editmyoptions": "编辑侬个个人设置",
        "right-sendemail": "發郵件畀各許人",
+       "grant-group-page-interaction": "搭頁面互動",
+       "grant-group-file-interaction": "搭媒體互動",
+       "grant-group-watchlist-interaction": "搭侬个关注表互动",
+       "grant-group-email": "發電子信",
+       "grant-rollback": "畀修改擂轉到頁面",
+       "grant-sendemail": "發電子信畀各許用戶",
        "newuserlogpage": "用户创建日志",
        "newuserlogpagetext": "箇是用户创建个记录。",
        "rightslog": "用户权限日志",
        "booksources-search-legend": "搜索图书来源",
        "booksources-search": "搜寻",
        "specialloguserlabel": "用戶:",
-       "speciallogtitlelabel": "ç\9b®æ¨\99ï¼\88æ¨\99é¡\8cè¦\81å¼\97ç\94¨æ\88):",
+       "speciallogtitlelabel": "ç\9b®æ \87ï¼\88æ \87é¢\98ï¼\8cæ\88\96é\92\88对ç\94¨æ\88·ä½¿ç\94¨{{ns:user}}:ç\94¨æ\88·å\90\8d):",
        "log": "记录",
        "alllogstext": "所有{{SITENAME}}公开日志个联合展示。侬可以选择日志类型、用户名(区分大小写)或者相关页面(区分大小写)来缩小查询范围。",
        "allpages": "全部页面",
        "unwatch": "弗关注",
        "unwatchthispage": "停止监控",
        "notanarticle": "弗是內容頁",
-       "watchlist-details": "弗包括讨论页,有 $1 页徕你侬关注表里向。",
+       "watchlist-details": "有$1页垃拉侬关注表高头,弗包括讨论页。",
        "wlheader-showupdated": "勒侬上趟查看之后畀修改个页面<strong>加粗</strong>显示。",
        "wlnote": "下底是{{PLURAL:$2|过去<strong>$2</strong>个钟头}}个{{PLURAL:$1|最后<strong>$1</strong>届更改}},截至$3 $4。",
        "wlshowlast": "显示上$1个钟头$2日天",
        "changed": "改变哉",
        "deletepage": "删脱页面",
        "confirm": "确认",
+       "excontentauthor": "内容是:“$1”,唯一贡献者是“[[Special:Contributions/$2|$2]]”([[User talk:$2|讲张]])",
        "historywarning": "<strong>警告:</strong>侬要删脱个页面有$1次{{PLURAL:$1|修订}}历史:",
        "confirmdeletetext": "侬即将删除一只页面或图像以及其历史。\n请确定侬要进行此项操作,并且了解其后果,同时侬个行为符合[[{{MediaWiki:Policy-url}}|the policy]]。",
        "actioncomplete": "操作完成哉",
        "rollbackfailed": "恢复失败",
        "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}}]])。",
-       "revertpage": "恢复[[Special:Contributions/$2|$2]]([[User talk:$2|讲张]])个改动到[[User:$1|$1]]个阿末只版本",
+       "revertpage": "取消[[Special:Contributions/$2|$2]]([[User talk:$2|讲张]])个改动;恢复到[[Special:Contributions/$1|$1]]个阿末只版本",
        "protectlogpage": "保护日志",
        "protectedarticle": "保护“[[$1]]”",
        "modifiedarticleprotection": "“[[$1]]”个保护等级改好哉",
        "undeleteviewlink": "望",
        "undeletecomment": "理由:",
        "undelete-search-submit": "搜尋",
-       "namespace": "名字空间:",
+       "namespace": "名字空间",
        "invert": "反选择",
        "tooltip-invert": "请选择该框来囥脱指定名字空间(搭有关名字空间,如果你选择)个页面更改",
        "namespace_association": "有关个名字空间",
        "anoncontribs": "贡献",
        "contribsub2": "{{GENDER:$3|$1}}个贡献($2)",
        "uctop": "(此垡)",
-       "month": "从箇月起 (要勿还要早):",
-       "year": "从箇年起 (要勿还要早):",
+       "month": "从箇月往前:",
+       "year": "从箇年往前:",
        "sp-contributions-newbies": "只显示新用户个贡献",
        "sp-contributions-blocklog": "查封记录",
-       "sp-contributions-talk": "è¨\8eè«\96",
+       "sp-contributions-talk": "讲张",
        "sp-contributions-search": "搜寻贡献记录",
        "sp-contributions-username": "IP地址要勿用户名:",
        "sp-contributions-submit": "搜寻",
        "linkshere": "下头个页链到[[:$1]]:",
        "nolinkshere": "呒有页链到 '''[[:$1]]'''。",
        "isredirect": "转戳页",
-       "istemplate": "包",
+       "istemplate": "包",
        "isimage": "文件鏈接",
        "whatlinkshere-prev": "前$1个",
        "whatlinkshere-next": "后$1个",
        "articleexists": "叫箇只名字个页面已经有垃许哉,要么侬拣个名字是无效个。请重新拣只名字。",
        "cantmove-titleprotected": "侬弗可以移动迭个页面到个个位置,因为迭个新标题已经拨保护拉许以防止创建。",
        "movetalk": "移动相关讨论页",
-       "movelogpage": "移记录",
+       "movelogpage": "移记录",
        "movelogpagetext": "下底是拨拉捅荡个页面列表。",
        "movereason": "理由:",
        "revertmove": "恢复",
        "allmessagesname": "名字",
        "allmessagesdefault": "默认文本",
        "allmessagescurrent": "当前文本",
-       "allmessagestext": "该个是MediaWiki名字空间里可用个系统消息列表。\n如果想为MediaWiki个本地化贡献翻译,请访问[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki本地化]搭[//translatewiki.net translatewiki.net]。",
+       "allmessagestext": "该个是MediaWiki名字空间里可用个系统消息列表。如果想为MediaWiki个本地化贡献翻译,请访问[https://www.mediawiki.org/wiki/Special:MyLanguage/Localisation MediaWiki本地化]搭[//translatewiki.net translatewiki.net]。",
        "allmessagesnotsupportedDB": "'''{{ns:special}}:Allmessages''' 呒处显示,因为 '''$wgUseDatabaseMessages''' 关勒浪。",
        "thumbnail-more": "放大",
        "filemissing": "文件寻弗着哉",
-       "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-edit": "编辑箇页",
        "tooltip-ca-addsection": "开始新段",
        "tooltip-ca-viewsource": "箇页受保护,你好望源代码",
-       "tooltip-ca-history": "箇页以早个版本",
+       "tooltip-ca-history": "该只页面老早个版本",
        "tooltip-ca-protect": "保护箇页",
        "tooltip-ca-delete": "删脱箇页",
-       "tooltip-ca-move": "移箇页",
-       "tooltip-ca-watch": "畀箇页加进你侬个关注表里",
+       "tooltip-ca-move": "移动该只页面",
+       "tooltip-ca-watch": "拿箇只页面加到侬个关注表里向",
        "tooltip-ca-unwatch": "拿箇只页面从侬个关注表里删脱",
        "tooltip-search": "搜寻{{SITENAME}}",
-       "tooltip-search-go": "转到页本确切名称,如果存在",
+       "tooltip-search-go": "如果存在相同标题,箇么直接去该页面",
        "tooltip-search-fulltext": "搜寻包含箇星文本个页面",
        "tooltip-p-logo": "翻到封面",
        "tooltip-n-mainpage": "翻到封面",
        "tooltip-n-mainpage-description": "翻到封面",
        "tooltip-n-portal": "有关箇计划,啥好做,应该哪能做",
-       "tooltip-n-currentevents": "寻当前事件个背景信息",
+       "tooltip-n-currentevents": "寻当前事件个背景信息",
        "tooltip-n-recentchanges": "列出wiki里箇阶段个变化",
        "tooltip-n-randompage": "随机打开只页面",
        "tooltip-n-help": "寻求帮助",
        "tooltip-t-recentchangeslinked": "箇页链出去个全部页箇阶段变化",
        "tooltip-feed-rss": "订阅本页",
        "tooltip-feed-atom": "此页个Atom 订阅",
-       "tooltip-t-contributions": "查看箇位用户个贡献",
+       "tooltip-t-contributions": "{{GENDER:$1|箇位用户}}个贡献列表",
        "tooltip-t-emailuser": "发电子信畀箇个用户",
        "tooltip-t-upload": "上传文件",
        "tooltip-t-specialpages": "全部特殊页列表",
        "tooltip-ca-nstab-help": "查看帮忙页面",
        "tooltip-ca-nstab-category": "望分类页",
        "tooltip-minoredit": "标作小编写",
-       "tooltip-save": "ä¿\9då­\98你侬个修æ\94¹",
+       "tooltip-save": "保存侬个修改",
        "tooltip-preview": "望望相你侬个修改,保存之前用!",
-       "tooltip-diff": "æ\98¾ç¤ºä½ ä¾¬å¯¹æ\96\87æ\9c¬ä¸ªä¿®æ\94¹",
+       "tooltip-diff": "显示侬对文本个修改",
        "tooltip-compareselectedversions": "查看本页面两只选定个修订版个差异。",
        "tooltip-watch": "拿箇页加到侬个关注表里",
        "tooltip-rollback": "揿一记“回转”就回退到上一位贡献者个编辑状态",
        "watchlisttools-raw": "编写原始关注表",
        "signature": "[[{{ns:user}}:$1|$2]]([[{{ns:user_talk}}:$1|讨论]])",
        "version": "版本",
-       "specialpages": "特殊页",
+       "specialpages": "特别页面",
        "tag-filter": "[[Special:Tags|标签]]过滤器:",
        "tag-list-wrapper": "([[Special:Tags|$1个标签]]:$2)",
        "tags-active-yes": "好",
        "logentry-delete-delete": "$1{{GENDER:$2|删除}}页面$3",
        "revdelete-restricted": "已将限制应用到管理员",
        "revdelete-unrestricted": "已移除对管理员个限制",
+       "logentry-block-block": "$1{{GENDER:$2|查封}}{{GENDER:$4|$3}},终止辰光为$5$6",
        "logentry-move-move": "$1{{GENDER:$2|捅荡}}页面$3到$4",
        "logentry-newusers-create": "用户账号$1畀{{GENDER:$2|创建}}",
+       "logentry-newusers-create2": "用户账号$3畀$1{{GENDER:$2|创建}}",
+       "logentry-newusers-autocreate": "用户账号$1畀自动{{GENDER:$2|创建}}",
        "logentry-upload-upload": "$1{{GENDER:$2|上传}}$3",
        "rightsnone": "(呒)",
        "revdelete-summary": "编辑摘要",
-       "searchsuggest-search": "搜寻"
+       "searchsuggest-search": "搜寻",
+       "pagelang-language": "闲话"
 }
index c2a6910..98cd410 100644 (file)
@@ -21,6 +21,7 @@
        "tog-hideminor": "დოფული ციქა რედაქტირაფა ეკონია თირაფეფს",
        "tog-hidepatrolled": "დოფულით პატრულირებულ რედაქტირაფეფი ასერდეიან თირაფეფს",
        "tog-newpageshidepatrolled": "დოფულით პატრულირებულ ხასჷლეფი ახალ ხასჷლეფიშ ერკებულშე",
+       "tog-hidecategorization": "ხასჷლეფიშ კატეგოიზაციაშ ტყობინაფა",
        "tog-extendwatchlist": "გოფაჩი ოთოჸუჯე ერკებული არძო თირაფეფიშ ოძირაფალო, ამარდეიან თირაფეფიშ მეკოროცხილო",
        "tog-usenewrc": "ბოლო ცვლილებების და კონტროლის სიის ცვლილებების დაჯგუფება",
        "tog-numberheadings": "ავტომატურო დონომერე დუდჯოხოეფი",
@@ -50,6 +51,7 @@
        "tog-watchlisthideliu": "დამალეთ დარეგისტრირებულ მოხმარებელთა ცვლილებები ჩემი კონტროლის სიიდან",
        "tog-watchlisthideanons": "დამალეთ ანონიმურ მომხმარებელთა შესწორებები ჩემი კონტროლის სიიდან",
        "tog-watchlisthidepatrolled": "დამალეთ საკონტროლო სიიდან პატრულირებული რედაქტირებები",
+       "tog-watchlisthidecategorization": "ხასჷლეფიშ კატეგოიზაციაშ ტყობინაფა",
        "tog-ccmeonemails": "გამომიგზავნე ელფოსტების ასლები, რომლებსაც მე სხვა მომხმარებლებს ვუგზავნი",
        "tog-diffonly": "დამალე გვერდის შიგთავსი ცვლილების ქვევით",
        "tog-showhiddencats": "ქაძირი ფულირი კატეგორიეფი",
        "createaccountreason": "მიზეზი:",
        "createacct-reason": "მიზეზი",
        "createacct-reason-ph": "რატომ ქმნით ახალ ანგარიშს?",
-       "createacct-captcha": "თხილუაშ კონტროლი",
-       "createacct-imgcaptcha-ph": "გენშიონით ჟი მოჩამილი ტექსტი",
        "createacct-submit": "გონწყით თქვენ ანგარიში",
        "createacct-another-submit": "გონწყით თქვენ ანგარიში",
        "createacct-benefit-heading": "{{SITENAME}} გოჭყაფილი რე თქვანნერი ადამიერეფიშით.",
        "passwordreset-emailtext-ip": "ვიღაცამ (შესაძლოა თქვენ, ამ IP-მისამართიდან $1) მოითხოვა თქვენი \nპაროლის თავიდან დაყენება საიტისათვის {{SITENAME}} ($4).\n{{PLURAL:$3|შემდეგი ანგარიში მიბმულია|შემდეგი ანგარიშები მიბმულია}} ამ ელ.ფოსტის მისამართზე:\n\n$2\n\n{{PLURAL:$3|ეს დროებითი პაროლი|ეს დროებითი პაროლები}} იმოქმედებს {{PLURAL:$5|ერთი დღე|$5 დღე}}.\nთქვენ უნდა შეხვიდეთ სისტემაში და აირჩიოთ ახალი პაროლი.\nთუ თქვენ არ გაგიკეთებიათ აღნიშნული მოთხოვნა, ან გაიხსენეთ თქვენი პაროლი\nდა აღარ გსურთ მისი შეცვლა, მაშინ შეგიძლიათ იგნორირება გაუკეთოთ ამ შეტყობინებას\nდა გააგრძელოთ თქვენი ძველი პაროლის გამოყენება.",
        "passwordreset-emailtext-user": "მომხმარებელმა $1 პროექტიდან {{SITENAME}} მოითხოვა თქვენი \nპაროლის თავიდან დაყენება საიტისათვის {{SITENAME}} ($4).\n{{PLURAL:$3|შემდეგი ანგარიში მიბმულია|შემდეგი ანგარიშები მიბმულია}} ამ ელ.ფოსტის მისამართზე:\n\n$2\n\n{{PLURAL:$3|ეს დროებითი პაროლი|ეს დროებითი პაროლები}} იმოქმედებს {{PLURAL:$5|ერთი დღე|$5 დღე}}.\nთქვენ უნდა შეხვიდეთ სისტემაში და აირჩიოთ ახალი პაროლი.\nთუ თქვენ არ გაგიკეთებიათ აღნიშნული მოთხოვნა, ან გაიხსენეთ თქვენი პაროლი\nდა აღარ გსურთ მისი შეცვლა, მაშინ შეგიძლიათ იგნორირება გაუკეთოთ ამ შეტყობინებას\nდა გააგრძელოთ თქვენი ძველი პაროლის გამოყენება.",
        "passwordreset-emailelement": "მომხმარებლის სახელი: \n$1\n\nდროებითი პაროლი: \n$2",
-       "passwordreset-emailsent": "პაროლის თავიდან დასაყენებელი ელ.ფოსტა გაიგზავნა.",
+       "passwordreset-emailsentemail": "პაროლის თავიდან დასაყენებელი ელ.ფოსტა გაიგზავნა.",
        "passwordreset-emailsent-capture": "ქვემოთ ნაჩვენები პაროლის თავიდან დასაყენებელი წერილი გაიგზავნა.",
        "passwordreset-emailerror-capture": "ქვემოთ მოცემულია შექმნილი პაროლის დასაყენებელი წერილი, რომლის გაგზავნაც {{GENDER:$2|მომხმარებელთან}} ვერ მოხერხდა: $1 გამო",
        "changeemail": "გენშიონით თქვანი ელ. ფოსტაშ ოწურაფუ",
-       "changeemail-text": "შეავსეთ ეს ფორმა თქვენი ელ-ფოსტის მისამართის შესაცვლელად. თქვენი პაროლის შეყვანა დაგჭირდებათ ამ ცვლილების დასადასტურებლად.",
+       "changeemail-header": "შეავსეთ ეს ფორმა თქვენი ელ-ფოსტის მისამართის შესაცვლელად. თქვენი პაროლის შეყვანა დაგჭირდებათ ამ ცვლილების დასადასტურებლად.",
        "changeemail-no-info": "კონკრეტულად ამ გვერდთან სამუშაოდ თქვენ უნდა წარადგინოთ თავი სისტემისადმი.",
        "changeemail-oldemail": "ელ-ფოსტის ამჟამინდელი მისამართი:",
        "changeemail-newemail": "ახალი ელ-ფოსტის მისამართი:",
        "prefs-help-prefershttps": "ამ კონფიგურაციის არჩევა შედეგს გამოიღებს შემდგომი ავტორიზაციის შედმეგ.",
        "prefswarning-warning": "თქვენ შეიტანეთ ცვლილება თქვენ პარამეტრებში, რომელიც ჯერ კიდევ არ არის შენახული. თუ თქვენ დატოვებთ ამ გვერდს და არ დააჭერთ \"$1\"-ს, პარამეტრები არ იქნება განახლებული.",
        "prefs-tabs-navigation-hint": "რჩევა: თქვენ შეგიძლიათ გამოიყენოთ ისრის კლავიშები მარცხნივ ან მარჯვნივ ჩანართებსა და ჩანართბის სიას შორის არსებული გადასვლებისათვის",
-       "email-address-validity-valid": "ელ-ფოსტის მისამართი სწორად გამოიყურება",
-       "email-address-validity-invalid": "მიუთითეთ სწორი ელ-ფოსტის მისამართი",
        "userrights": "მომხმარებელთა უფლებების მართვა",
        "userrights-lookup-user": "მომხმარებელთა ჯგუფების მართვა",
        "userrights-user-editname": "შეიტანეთ მომხმარებლის სახელი:",
        "right-blockemail": "ელ ფოსტის გაგზავნის აკრძალვა",
        "right-hideuser": "მომხმარებლის სახელის დაბლოკვა და მისი დამალვა საზოგადოებისგან",
        "right-ipblock-exempt": "IP ბლოკის, ავტობლოკის და დიაპაზონთა ბლოკის გასვლა",
-       "right-proxyunbannable": "პროქსის ავტობლოკის გადასვლა",
        "right-unblockself": "საკუთარი თავის განბლოკვა",
        "right-protect": "გვერდების დაცვის დონის შეცვლა და კასკადურად დაცული გვერდების რედაქტირება",
        "right-editprotected": "გვერდების რედაქტირება რომლებიც დაცულია როგორც „{{int:protect-level-sysop}}“",
        "rcshowhidemine": "ჩქიმ რედაქტირაფეფიშ $1",
        "rcshowhidemine-show": "ძირაფა",
        "rcshowhidemine-hide": "ტყობინაფა",
+       "rcshowhidecategorization": "$1 ხასჷლაშ კატეროტიზაცია",
+       "rcshowhidecategorization-show": "ძირაფა",
+       "rcshowhidecategorization-hide": "ტყობინაფა",
        "rclinks": "ეკონია $2 დღას ღოლამირ ეკონია $1 თირაფეფიშ ძირაფა <br />$3",
        "diff": "შხვანერობა",
        "hist": "ისტ.",
        "upload-dialog-button-done": "ღოლამირჷ რე",
        "upload-dialog-button-save": "ჩუალა",
        "upload-dialog-button-upload": "ეხარგუა",
-       "upload-process-error": "ჩილათაქ მოხვადჷ",
        "upload-form-label-select-file": "გეგშაგორით ფაილი",
        "upload-form-label-infoform-title": "დეტალეფი",
        "upload-form-label-infoform-name": "ჯოხო",
        "log-title-wildcard": "სათაურების ძებნა, რომლებიც იწყება ამ ტექსტით",
        "showhideselectedlogentries": "არჩეული ჟურნალის ჩანაწერის ჩვენება/დამალვა",
        "log-edit-tags": "ტეგების რედაქტირება ამორჩეული ჟურნალის ჩანაწერებიდან",
+       "checkbox-all": "არძა",
+       "checkbox-none": "აკა ვარი",
+       "checkbox-invert": "ინოხუნაფა",
        "allpages": "არძა ხასილა",
        "nextpage": "უკულ ხასილა ($1)",
        "prevpage": "წოხლენ ხასილა ($1)",
        "unwatch": "კონტროლიშ გოუქვაფა",
        "watchlist-details": "{{PLURAL:$1|$1 ხასილა|$1 ხასილეფ}} რე თქვან კონტროლიშ ერკებულს, სხუნუაშ ხასილეფიშ მეუკოროცხუო.",
        "wlshowlast": "ეკონია $1 საათიშ $2 დღაშ  ძირაფა",
+       "watchlistall2": "არძა",
        "watchlist-options": "კონტროლიშ ერკებულიშ ოფციეფ",
        "watching": "კონტროლირებად...",
        "unwatching": "კონტროლ მონწყუმილ რე ...-შა",
        "tooltip-t-recentchangeslinked": "თე ხასჷლაწკჷმა მერსხილ ხასჷლეფშა ასერდე მიშაღალირი თირაფეფი",
        "tooltip-feed-rss": "მოჩამილი ხასილაშ RSS არხიშ ტრანსლაცია",
        "tooltip-feed-atom": "ათე ხასჷლაშ ატომ არხიშ ტრანსლაცია",
-       "tooltip-t-contributions": "á\83\97á\83\94 á\83\9bá\83\90á\83®á\83\95á\83\90á\83 á\83\94á\83\91á\83£á\83¨ á\83\9bá\83\98á\83¨á\83\90á\83¦á\83\90á\83\9aá\83\98á\83  თიაშ ერკებულიშ ძირაფა",
-       "tooltip-t-emailuser": "á\83\9bá\83\98á\83\93á\83£á\83¯á\83¦á\83\9dá\83\9cá\83\98á\83\97 á\83\94á\83\9a\83¤á\83\9dá\83¨á\83¢á\83\90 á\83\90á\83\97á\83\94 á\83\9bá\83\90á\83®á\83\95á\83\90á\83 á\83\94á\83\91á\83£á\83¡",
+       "tooltip-t-contributions": "á\83\9bá\83\90á\83®á\83\95á\83\90á\83 á\83\94á\83\91á\83£á\83¨  {{GENDER:$1}} á\83\9bá\83\98á\83¨á\83\9cá\83\90á\83¦á\83\94á\83\9aá\83\98 თიაშ ერკებულიშ ძირაფა",
+       "tooltip-t-emailuser": "á\83\9bá\83\98á\83\93á\83£á\83¯á\83¦á\83\9dá\83\9cá\83\98á\83\97 á\83\94á\83\9a\83¤á\83\9dá\83¨á\83¢á\83\90 á\83\9bá\83\90á\83®á\83\95á\83\90á\83 á\83\94á\83\91á\83£á\83¡ {{GENDER:$1}}",
        "tooltip-t-upload": "გეხარგე ფაილეფი",
        "tooltip-t-specialpages": "არძო სპეციალურ ხასჷლაშ ერკებული",
        "tooltip-t-print": "თე ხასჷლაშ ობეშტალი ვერსია",
index e4bd61f..47223bc 100644 (file)
        "remembermypassword": "געדענקען מיין אַריינלאָגירן אין דעם דאָזיקן בראַוזער (ביז $1 {{PLURAL:$1|טאָג|טעג}})",
        "userlogin-remembermypassword": "לאז מיך בלײַבן ארײַנלאגירט",
        "userlogin-signwithsecure": "ניצן זיכערן סארווער",
+       "cannotloginnow-title": "קען נישט אריינלאגירן אצינד",
        "yourdomainname": "אײַער געביט:",
        "password-change-forbidden": "איר קען נישט ענדערן פאסווערטער אויף דער וויקי.",
        "externaldberror": "עס איז אדער פארגעקומען אן אויטענטיקאציע דאטנבאזע פֿעלער אדער איר זענט נישט ערמעגליכט צו דערהיינטיגן אייער דרויסנדיגע קאנטע.",
        "resetpass_submit": "שטעלן פאסווארט און אריינלאגירן",
        "changepassword-success": "אייער פאַסווארט איז געטוישט געווארן מיט דערפֿאלג!",
        "changepassword-throttled": "איר האט געפרוווט צופֿיל מאל אריינלאגירן.\nזייט אזוי גוט און וואַרט $1 איידער איר פרוווט נאכאמאל.",
+       "botpasswords-label-create": "שאַפֿן",
+       "botpasswords-label-update": "דערהײַנטיקן",
+       "botpasswords-label-cancel": "אַנולירן",
+       "botpasswords-label-resetpassword": "ווידערשטעלן פאַסווארט",
        "resetpass_forbidden": "פאסווערטער קענען נישט ווערן געטוישט",
        "resetpass-no-info": "איר דארפֿט זיין אריינלאגירט צוצוקומען גלייך צו דעם דאזיגן בלאט.",
        "resetpass-submit-loggedin": "טוישן פאסווארט",
        "passwordreset-emailtext-ip": "עמעצער (מסתמא איר, פון IP אדרעס $1) האט געבעטן צוריקצושטעלן אייער פאסווארט פאר {{SITENAME}} ($4). די פאלגנדע באניצער {{PLURAL:$3|קאנטע איז|קאנטעס זענען}}\nפארבונדן מיט דעם ע־פאסט אדרעס:\n\n$2\n\n{{PLURAL:$3|דאס פראוויזארישע פאסווארט|די פראוויזארישע פאסווערטער}} וועלן אויסגיין נאך {{PLURAL:$5|איין טאג|$5 טעג}}.\nאיר זאלט אריינלאגירן און קלויבן א נייע פאסווארט אצינד. טאמער א צווייטער האט געשיקט די בקשה,\nאדער ווען איר געדענקט יא אייער פריעריקע פאסווארט, און וויל עס נישט ענדערן,\n קענט איר איגנארירן דעם אנזאג און ניצן ווייטער דאס אלטע פאסווארט.",
        "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": "×\98×\90×\9eער ×\90×\99×\96 ×\93×\90ס ×\90×\9f ×\90×\99×\99× ×\92עשר×\99×\91ענער ×¢Ö¾×¤×\90ס×\98 ×\90×\93רעס ×¤×\90ר אייער קאנטע, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
+       "passwordreset-emailsentemail": "×\98×\90×\9eער ×\90×\99×\96 ×\93ער ×¢Ö¾×¤×\90ס×\98 ×\90×\93רעס ×¤×\90רקנ×\99פ×\98 ×\9e×\99×\98 אייער קאנטע, וועט מען שיקן א פאסווארט צוריקשטעלן ע-פּאָסט.",
        "passwordreset-emailsent-capture": "מען האט געשיקט א פאסווארט צוריקשטעלן בליצבריוו, וואס ווערט געוויזן אונטן.",
        "passwordreset-emailerror-capture": "מען האט געשאפן א פאסווארט צוריקשטעלן בליצבריוו, וואס ווערט געוויזן אונטן, אבער שיקן צום {{GENDER:$2|באניצער}}איז דורכגעפאלן: $1",
        "changeemail": "ענדערן אדער אראפנעמען ע-פּאָסט אַדרעס",
        "right-blockemail": "בלאקירן א באַניצער פֿון שיקן ע־פאסט",
        "right-hideuser": "בלאקירן באַניצער־נאָמען און פֿאַרבארגן אים",
        "right-ipblock-exempt": "ארומגיין IP בלאקן, אויטאבלאקן און גרייך־בלאקן",
-       "right-proxyunbannable": "ארומגיין אויטאמאטישע בלאקירן פון פראקסיס",
        "right-unblockself": "זיך אליין אויפֿשפאַרן",
        "right-protect": "ענדערן שוץ ניוואען און רעדאַגירן קאסקאד־געשיצטע בלעטער",
        "right-editprotected": "רעדאַגירן בלעטער געשיצט ווי \"{{int:protect-level-sysop}}\"",
        "javascripttest-pagetext-frameworks": "ביטע קלויבט איינעם פון די פאלגנדע טעסטן־גערעם: $1",
        "javascripttest-pagetext-skins": "קלויבט א באניצער־אייבערפלאך מיט וואס דורכצופירן די בדיקות:",
        "javascripttest-qunit-intro": "זעט [$1 דאקומענטאציע פאר טעסטן] בײַ mediawiki.org.",
-       "tooltip-pt-userpage": "אייער באניצער בלאט",
+       "tooltip-pt-userpage": "אייער {{GENDER:|באניצער|באניצערין}} בלאט",
        "tooltip-pt-anonuserpage": "באַניצער בלאַט פון דעם IP אַדרעס",
-       "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": "אלע בײַשטײַערונגען פון דעם באניצער",
-       "tooltip-t-emailuser": "שיקן א בליצבריוו צו דעם בַאניצער",
+       "tooltip-t-contributions": "א ליסטע פון בײַשטײַערונגען פון {{GENDER:$1|דעם באניצער|דער באניצערין}}",
+       "tooltip-t-emailuser": "שיקן א בליצבריוו צו {{GENDER:$1|דעם באניצער|דער באניצערין}}",
        "tooltip-t-info": "נאָך אינפאָרמאַציע וועגן דעם בלאַט",
        "tooltip-t-upload": "ארויפלאדן טעקעס",
        "tooltip-t-specialpages": "אלע ספעציעלע בלעטער",
        "revdelete-uname-unhid": "באַניצער נאָמען ארויסגעגעבן",
        "revdelete-restricted": "צוגעלייגט באגרעניצונגען פאר סיסאפן",
        "revdelete-unrestricted": "אוועקגענומען באגרעניצונגען פאר סיסאפן",
+       "logentry-suppress-block": "$1 {{GENDER:$2|האט בלאקירט}} {{GENDER:$4|$3}} מיט אן אויסלאז צייט פון $5 $6",
        "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 אריבער ווייטערפירונג",
        "expand_templates_ok": "אויספֿירן",
        "expand_templates_remove_comments": "אראפנעמען הערות",
        "expand_templates_preview": "פֿאראויסשטעלונג",
+       "pagelang-submit": "איינגעבן",
+       "mediastatistics-header-total": "אלי טעקעס",
        "special-characters-group-latin": "לאַטייניש",
        "special-characters-group-latinextended": "לאַטייַן פֿאַרברייטערט",
        "special-characters-group-ipa": "אינטערנאַציאנאלער פֿאנעטישער אלפֿאבעט (IPA)",
index dae4262..fd14d64 100644 (file)
                        "Jiang123aa",
                        "Cdz",
                        "凡人丶",
-                       "Nbdd0121"
+                       "Nbdd0121",
+                       "Apflu"
                ]
        },
        "tog-underline": "链接下划线:",
-       "tog-hideminor": "隐藏最近更改中的小编辑",
-       "tog-hidepatrolled": "隐藏最近更改中的已巡查编辑",
-       "tog-newpageshidepatrolled": "隐藏新页面列表中的已巡查页面",
-       "tog-hidecategorization": "隐藏页面的分类",
+       "tog-hideminor": "在最近更改中隐藏小编辑",
+       "tog-hidepatrolled": "在最近更改中隐藏已巡查的编辑",
+       "tog-newpageshidepatrolled": "在新页面列表中隐藏已巡查页面",
+       "tog-hidecategorization": "隐藏页面的分类",
        "tog-extendwatchlist": "扩展监视列表以显示所有更改,而不仅是最近的更改",
        "tog-usenewrc": "按页面合并最近更改和监视列表中的更改",
        "tog-numberheadings": "自动将标题编号",
        "october-date": "10月$1日",
        "november-date": "11月$1日",
        "december-date": "12月$1日",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|分类}}",
        "category_header": "分类“$1”中的页面",
        "subcategories": "子分类",
        "ok": "确定",
        "backlinksubtitle": "←$1",
        "retrievedfrom": "取自“$1”",
-       "youhavenewmessages": "有$1($2)。",
-       "youhavenewmessagesfromusers": "有来自{{PLURAL:$3|其他用户|$3个用户}}的$1($2)。",
+       "youhavenewmessages": "{{PLURAL:$3|您}}有$1($2)。",
+       "youhavenewmessagesfromusers": "{{PLURAL:$4|您}}有来自{{PLURAL:$3|其他用户|$3个用户}}的$1($2)。",
        "youhavenewmessagesmanyusers": "你有来自多个用户的$1($2)。",
        "newmessageslinkplural": "{{PLURAL:$1|新信息|999=新消息}}",
        "newmessagesdifflinkplural": "最后{{PLURAL:$1|更改|999=更改}}",
        "laggedslavemode": "'''警告:'''页面中可能没有包含最近的更新。",
        "readonly": "数据库被锁定",
        "enterlockreason": "请输入锁定的原因,这包括预计解除锁定的时间",
-       "readonlytext": "数据库当前被锁定,不能添加新条目或进行其他修改,锁定可能是因为例行的数据库维护,完成后即可恢复正常。\n\n锁定数据库的系统管理员提供的解释:$1",
+       "readonlytext": "数据库当前被锁定,不能添加新条目或进行其他修改,锁定可能是因为例行的数据库维护,完成后即可恢复正常。\n\n锁定数据库的系统管理员做出如下解释:$1",
        "missing-article": "数据库找不到预期的页面文字:“$1”$2。\n\n这通常是由于点击了链向旧有差异或历史的链接,而原有版本已被删除导致的。\n\n如果情况不是这样,您可能找到了软件的一个内部错误。请记录下URL地址,并向[[Special:ListUsers/sysop|管理员]]报告。",
        "missingarticle-rev": "(版本#:$1)",
        "missingarticle-diff": "(差异:$1,$2)",
        "mypreferencesprotected": "您没有权限来编辑您的个人设置。",
        "ns-specialprotected": "特殊页面不可编辑。",
        "titleprotected": "此标题已被[[User:$1|$1]]保护以防止创建。理由是“$2”。",
-       "filereadonlyerror": "因为媒体库$2处于只读模式而无法修改文件$1。\n\n执行锁定的系统管理员给出如下解释:$3。",
+       "filereadonlyerror": "因为媒体库“$2”处于只读模式而无法修改文件“$1”。\n\n锁定数据库的系统管理员做出如下解释:“$3”。",
        "invalidtitle-knownnamespace": "使用名字空间“$2”和文本“$3”的无效标题",
        "invalidtitle-unknownnamespace": "使用未知名字空间编号$1和文本“$2”的无效标题",
        "exception-nologin": "未登录",
        "virus-scanfailed": "扫描失败(代码 $1)",
        "virus-unknownscanner": "未知的反病毒软件:",
        "logouttext": "<strong>您现在已经退出登录。</strong>\n\n请注意一些页面可能仍然显示您处于登录状态,直到您清空浏览器缓存为止。",
+       "cannotlogoutnow-title": "现在不能退出",
+       "cannotlogoutnow-text": "当使用$1时无法退出。",
        "welcomeuser": "欢迎,$1!",
        "welcomecreation-msg": "你的账户已创建。请不要忘记更改你的[[Special:Preferences|{{SITENAME}}设置]]。",
        "yourname": "用户名:",
        "remembermypassword": "在该浏览器记住我的登录状态(最长$1天)",
        "userlogin-remembermypassword": "记住我的登录状态",
        "userlogin-signwithsecure": "使用安全连接",
+       "cannotloginnow-title": "现在不能登录",
+       "cannotloginnow-text": "当使用$1时无法登录。",
        "yourdomainname": "您的域名:",
        "password-change-forbidden": "您不能在本wiki上更改密码。",
        "externaldberror": "验证数据库出错或您被禁止更新您的外部账号。",
        "nocookiesnew": "该用户帐户已被创建,但登录失败。{{SITENAME}}使用Cookie实现用户登录。您已禁用Cookie,请启用Cookie,然后使用你的新用户名与密码登录。",
        "nocookieslogin": "{{SITENAME}}使用Cookie实现用户登录。您已停用Cookie。请启用Cookie后再试。",
        "nocookiesfornew": "该用户账户未被创建,我们不能确认它的来源。请确保你已启用Cookie,刷新本页后再试。",
-       "noname": "你没有指定有效的用户名。",
+       "noname": "指定有效的用户名。",
        "loginsuccesstitle": "登录成功",
        "loginsuccess": "<strong>您现在已经以\"$1\"的身份登录了{{SITENAME}}。</strong>",
        "nosuchuser": "没有名为“$1”的用户。用户名区分大小写。请检查你的拼写或[[Special:UserLogin/signup|创建新账户]]。",
        "nosuchusershort": "没有名为“$1”的用户。请检查你的拼写。",
-       "nouserspecified": "你必须指定用户名。",
+       "nouserspecified": "您必须指定一个用户名。",
        "login-userblocked": "该用户已被封禁,禁止登录。",
        "wrongpassword": "您输入的密码错误。请重试。",
        "wrongpasswordempty": "密码输入为空。请重试。",
        "resetpass_submit": "设定密码并登录",
        "changepassword-success": "您已经修改了您的密码!",
        "changepassword-throttled": "您最近尝试了多次登录。请等待$1后再试。",
+       "botpasswords": "机器人密码",
+       "botpasswords-summary": "<em>机器人密码</em>允许通过API访问用户账户而不使用账户的主要登录凭据。通过机器人密码登录时,用户权限可能会受限制。\n\n如果您不知道为什么您想这样做,您就不应该这样做。没有人会要求您生成这些密码之一,并向其提供。",
+       "botpasswords-disabled": "机器人密码已禁用。",
+       "botpasswords-no-central-id": "要使用机器人密码,您必须登录至已集中的账户。",
+       "botpasswords-existing": "现有机器人密码",
+       "botpasswords-createnew": "创建新的机器人密码",
+       "botpasswords-editexisting": "编辑现有的机器人密码",
+       "botpasswords-label-appid": "机器人名:",
+       "botpasswords-label-create": "创建",
+       "botpasswords-label-update": "更新",
+       "botpasswords-label-cancel": "取消",
+       "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": "机器人密码已创建",
+       "botpasswords-created-body": "机器人密码“$1”已成功更新。",
+       "botpasswords-updated-title": "机器人密码已更新",
+       "botpasswords-updated-body": "机器人密码“$1”已成功更新。",
+       "botpasswords-deleted-title": "机器人密码已删除",
+       "botpasswords-deleted-body": "机器人密码“$1”已删除。",
+       "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-no-info": "您必须登录后直接进入这个页面。",
        "resetpass-submit-loggedin": "更改密码",
        "passwordreset-emailtext-ip": "有人(可能是您,来自IP地址$1)请求重设{{SITENAME}}($4)上相关账户的密码。以下$3个账户与该电子邮件地址关联:\n\n$2\n\n这个临时密码将会在{{PLURAL:$5|一天|$5天}}后过期。请立即登录并设置新的密码。如果请求是其他人发出的,或者您已回忆起您的旧密码并不再需要更改,您可以忽略本条消息并继续使用原密码。",
        "passwordreset-emailtext-user": "用户$1请求重设{{SITENAME}}($4)上您的账户的密码。{{PLURAL:$3|以下账户|此账户}}与该电子邮件地址关联:\n\n$2\n\n这个临时密码将会在{{PLURAL:$5|一天|$5天}}后过期。请立即登录并设置新的密码。如果请求是其他人发出的,或者您已回忆起您的旧密码并不再需要更改,您可以忽略本条消息并继续使用原密码。",
        "passwordreset-emailelement": "用户名:\n$1\n\n临时密码:\n$2",
-       "passwordreset-emailsentemail": "å¦\82æ\9e\9cæ\82¨ç\9a\84è´¦æ\88·æ\9c\89ä¸\80个已注å\86\8cç\9a\84ç\94µå­\90é\82®ä»¶å\9c°å\9d\80的话,将发送一封密码重置邮件。",
-       "passwordreset-emailsentusername": "如果有对应注册的电子邮件地址的话,将发送一封密码重置邮件。",
+       "passwordreset-emailsentemail": "å¦\82æ\9e\9cæ­¤é\82®ä»¶å\9c°å\9d\80ä¸\8eæ\82¨ç\9a\84è´¦æ\88·ç\9b¸å\85³è\81\94的话,将发送一封密码重置邮件。",
+       "passwordreset-emailsentusername": "如果有邮件地址与此用户名相关联的话,将发送一封密码重置邮件。",
        "passwordreset-emailsent-capture": "密码重设电子邮件已发送,并在下面显示。",
        "passwordreset-emailerror-capture": "重置密码邮件已生成,但是无法向{{GENDER:$2|下列用户}} 发送:$1",
        "changeemail": "更改或移除电子邮件地址",
        "copyrightwarning2": "请注意,您对{{SITENAME}}的所有贡献都可能被其他贡献者编辑,修改或删除。如果您不希望您的文字被任意修改和再散布,请不要提交。<br />\n您同时也要向我们保证您所提交的内容是您自己所作,或得自一个不受版权保护或相似自由的来源(参阅$1的细节)。'''不要在未获授权的情况下发表!'''",
        "editpage-cannot-use-custom-model": "此页面的内容模型不能被更改。",
        "longpageerror": "'''错误:您所提交的文本长度有{{PLURAL:$1|1|$1}}KB,这大于{{PLURAL:$2|1|$2}}KB的最大值。'''\n因此,该文本无法保存。",
-       "readonlywarning": "<strong>警告:数据库被锁定以进行维护,所以您目前将无法保存您的编辑。</strong>您可能希望将您的文本复制粘贴到一个文本文档并保存它,以便稍后更改。\n\n锁定数据库的系统管理员有如下解释:$1",
+       "readonlywarning": "<strong>警告:数据库被锁定以进行维护,所以您目前将无法保存您的编辑。</strong>您可以将您的文本复制粘贴到一个文本文档并保存它,以便稍后更改。\n\n锁定数据库的系统管理员做出如下解释:$1",
        "protectedpagewarning": "'''警告:本页面已被保护,只有拥有管理员权限的用户可以编辑。'''下面提供最后的日志条目以供参考:",
        "semiprotectedpagewarning": "'''注意:'''本页面已被保护,只有注册用户可以编辑。下面提供最后的日志条目以供参考:",
        "cascadeprotectedwarning": "<strong>警告:</strong>本页面已经被保护,只有拥有管理员权限的用户可以编辑,因为它被嵌入于以下启用连锁保护的{{PLURAL:$1|页面}}中:",
        "edit-conflict": "编辑冲突。",
        "edit-no-change": "因为没有文字更改,你的编辑已被忽略。",
        "postedit-confirmation-created": "页面已创建。",
-       "postedit-confirmation-restored": "页面已创建。",
+       "postedit-confirmation-restored": "页面已恢复。",
        "postedit-confirmation-saved": "你的编辑已保存。",
        "edit-already-exists": "不可以建立一个新页面。\n它已经存在。",
        "defaultmessagetext": "默认消息文本",
        "rev-deleted-text-view": "本页面版本已被'''删除'''。你可以查看它,详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
        "rev-suppressed-text-view": "该页面版本已经被'''监督隐藏'''。您可以查看它。在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到详细的信息。",
        "rev-deleted-no-diff": "你不能查看该差异,因为其中一个版本已被'''删除'''。详情请见[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]。",
-       "rev-suppressed-no-diff": "你不能查看该差异,因为其中一个版本已被'''删除'''。",
+       "rev-suppressed-no-diff": "无法查看该差异,因为其中一个版本已被<strong>删除<strong>。",
        "rev-deleted-unhide-diff": "该差异对比的其中的一个版本已经被<strong>删除</strong>。在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中可以找到更多的信息。如果您想继续的话,您仍然可以[$1 查看此版本]。",
        "rev-suppressed-unhide-diff": "该页面的其中一次版本已经被<strong>监督隐藏</strong>。\n在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中可以找到更多的资料。如果您想继续的话,您可以仍然[$1 去查看这版本]。",
        "rev-deleted-diff-view": "差异对比中的一次版本已被<strong>删除</strong>。您可以对比此差异。详细信息可在[{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 删除日志]中找到。",
        "rev-suppressed-diff-view": "差异对比中的一个版本已被<strong>监督隐藏</strong>。您可以对比此差异。详细信息可在[{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 监督日志]中找到。",
-       "rev-delundel": "æ\98¾ç¤º/é\9a\90è\97\8f",
+       "rev-delundel": "æ\9b´æ\94¹å\8f¯è§\81æ\80§",
        "rev-showdeleted": "显示",
        "revisiondelete": "删除/还原版本",
        "revdelete-nooldid-title": "无效目标版本",
        "prefs-custom-css": "自定义CSS",
        "prefs-custom-js": "自定义JavaScript",
        "prefs-common-css-js": "所有皮肤共用的CSS/JavaScript:",
-       "prefs-reset-intro": "你可以使用本页面重置你的系统设置为网站默认值。该操作不能撤销。",
+       "prefs-reset-intro": "可以通过本页面将系统设置重置为网站默认值。该操作无法撤销。",
        "prefs-emailconfirm-label": "电子邮件确认:",
        "youremail": "电子邮件:",
        "username": "{{GENDER:$1|用户名}}:",
        "userrights": "用户权限管理",
        "userrights-lookup-user": "管理用户组",
        "userrights-user-editname": "输入用户名:",
-       "editusergroup": "编辑用户组",
+       "editusergroup": "编辑{{GENDER:$1|用户}}组",
        "editinguser": "更改{{GENDER:$1|用户}}<strong>[[User:$1|$1]]</strong>的用户权限$2",
        "userrights-editusergroup": "编辑用户组",
-       "saveusergroups": "保存用户组",
+       "saveusergroups": "保存{{GENDER:$1|用户}}组",
        "userrights-groupsmember": "用户组:",
        "userrights-groupsmember-auto": "自动用户组:",
        "userrights-groups-help": "你可以更改该用户的用户组:\n* 选中的选项框表示该用户属于该用户组。\n* 未选中的选项框表示该用户不属于该用户组。\n* 星号(*)表示一旦添加该用户组后不能删除,反之亦然。",
        "group-bot": "机器人",
        "group-sysop": "管理员",
        "group-bureaucrat": "行政员",
-       "group-suppress": "监督员",
+       "group-suppress": "Flow监督员",
        "group-all": "(所有)",
        "group-user-member": "{{GENDER:$1|用户}}",
        "group-autoconfirmed-member": "自动确认用户",
        "group-bot-member": "机器人",
        "group-sysop-member": "{{GENDER:$1|管理员}}",
        "group-bureaucrat-member": "行政员",
-       "group-suppress-member": "{{GENDER:$1|监督员}}",
+       "group-suppress-member": "{{GENDER:$1|Flow监督员}}",
        "grouppage-user": "{{ns:project}}:用户",
        "grouppage-autoconfirmed": "{{ns:project}}:自动确认用户",
        "grouppage-bot": "{{ns:project}}:机器人",
        "right-createpage": "创建非讨论页面",
        "right-createtalk": "创建讨论页面",
        "right-createaccount": "创建账户",
+       "right-autocreateaccount": "通过外部用户账户自动登录",
        "right-minoredit": "标记编辑为小编辑",
        "right-move": "移动页面",
        "right-move-subpages": "移动页面及其子页面",
        "right-blockemail": "阻止用户发送电子邮件",
        "right-hideuser": "封禁并隐藏用户名",
        "right-ipblock-exempt": "绕过IP封禁、自动封禁和段封禁",
-       "right-proxyunbannable": "避开代理服务器的自动封禁",
        "right-unblockself": "自我解封",
        "right-protect": "更改保护级别和编辑受级联保护页面",
        "right-editprotected": "编辑保护级别为“{{int:protect-level-sysop}}”的页面",
        "right-managechangetags": "从数据库创建和删除[[Special:Tags|标签]]",
        "right-applychangetags": "连同某人的更改一起应用[[Special:Tags|标签]]",
        "right-changetags": "在个别修订和日志记录中添加和移除任意[[Special:Tags|标签]]",
+       "grant-generic": "“$1”权限束",
+       "grant-group-page-interaction": "与页面交互",
+       "grant-group-file-interaction": "与媒体交互",
+       "grant-group-watchlist-interaction": "与您的监视列表交互",
+       "grant-group-email": "发送电子邮件",
+       "grant-group-high-volume": "执行大量活动",
+       "grant-group-customization": "自定义与设置",
+       "grant-group-administration": "执行行政操作",
+       "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-editprotected": "编辑受保护页面",
+       "grant-highvolume": "大容量编辑",
+       "grant-oversight": "隐藏用户和阻止修订",
+       "grant-patrol": "巡查对页面的更改",
+       "grant-protect": "保护页面和取消页面保护",
+       "grant-rollback": "回退对页面的更改",
+       "grant-sendemail": "给其他用户发送电子邮件",
+       "grant-uploadeditmovefile": "上传,替换和移动文件",
+       "grant-uploadfile": "上传新文件",
+       "grant-basic": "基本权限",
+       "grant-viewdeleted": "查看已删除文件和页面",
+       "grant-viewmywatchlist": "查看您的监视列表",
        "newuserlogpage": "用户创建日志",
        "newuserlogpagetext": "这是用户创建的日志。",
        "rightslog": "用户权限日志",
        "action-createpage": "创建页面",
        "action-createtalk": "创建讨论页面",
        "action-createaccount": "创建该用户账户",
+       "action-autocreateaccount": "自动创建该外部用户账户",
        "action-history": "查看此页历史",
        "action-minoredit": "标记该编辑为小编辑",
        "action-move": "移动本页",
        "upload-form-label-select-file": "选择文件",
        "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": "文件名",
        "foreign-structured-upload-form-label-own-work": "这是我的作品",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "如果您并不拥有此文件的版权,或者您希望将其以其他许可协议发布,请考虑使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 共享资源的上传向导]。",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "如果网站允许依据他们的方针上传此文件的话,您也可以尝试使用[[Special:Upload|{{SITENAME}}上的上传页面]]。",
        "foreign-structured-upload-form-2-label-intro": "感谢您贡献图片以用于{{SITENAME}}。您只应在其满足以下条件时贡献:",
-       "foreign-structured-upload-form-2-label-ownwork": "它必须完全<strong>由您创建</strong>,而不只是从Internet上找到",
+       "foreign-structured-upload-form-2-label-ownwork": "它必须完全<strong>由您创作</strong>,而不只是从Internet上获取",
        "foreign-structured-upload-form-2-label-noderiv": "它不包含<strong>其他任何人</strong>的成果,或者来自他们的灵感",
        "foreign-structured-upload-form-2-label-useful": "它应当有<strong>教育意义</strong>并对教育他人有用",
        "foreign-structured-upload-form-2-label-ccbysa": "它必须<strong>允许</strong>依据[https://creativecommons.org/licenses/by-sa/4.0/ 知识共享 署名-相同方式共享4.0]许可协议在网络上再发布",
        "foreign-structured-upload-form-3-label-yes": "是",
        "foreign-structured-upload-form-3-label-no": "否",
        "foreign-structured-upload-form-3-label-alternative": "很遗憾,这种情况下,此工具不支持上传此文件。您仍应当使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 共享资源上传向导]上传此文件,只要它依据自由许可协议可用于该网站的话。",
-       "foreign-structured-upload-form-4-label-good": "使用此工具,您可以上传您创建的教育性图形和您创建的照片,它不包含其他任何人的作品。",
+       "foreign-structured-upload-form-4-label-good": "使用此工具,您可以上传您创建的教育性图片和您拍摄的照片,不能包含其他任何人的作品。",
        "foreign-structured-upload-form-4-label-bad": "您不能上传来自搜索引擎或从其他网站上下载的图片。",
        "backend-fail-stream": "无法流传送文件$1。",
        "backend-fail-backup": "无法备份文件$1。",
        "log-title-wildcard": "搜索以该文字开头的标题",
        "showhideselectedlogentries": "显示/隐藏所选日志项",
        "log-edit-tags": "编辑选定日志记录的标签",
+       "checkbox-select": "选择:$1",
+       "checkbox-all": "全部",
+       "checkbox-none": "无",
+       "checkbox-invert": "反选",
        "allpages": "所有页面",
        "nextpage": "下一页($1)",
        "prevpage": "上一页($1)",
        "listgrouprights-namespaceprotection-header": "名字空间限制",
        "listgrouprights-namespaceprotection-namespace": "名字空间",
        "listgrouprights-namespaceprotection-restrictedto": "允许用户编辑的权限",
+       "listgrants": "权限",
+       "listgrants-summary": "以下是与用户权限访问相关的授权列表。用户可以授予应用程序访问它们的账户,但限定于用户授予应用程序的功能权限。代表一位用户的应用程序不能使用用户没有的权限。\n这里可能有关于个别权限的[[{{MediaWiki:Listgrouprights-helppage}}|额外信息]]。",
+       "listgrants-grant": "授权",
+       "listgrants-rights": "权限",
        "trackingcategories": "追踪分类",
        "trackingcategories-summary": "本页面列举由MediaWiki软件自动添加的追踪分类。它们的名字可通过修改{{ns:8}}名字空间对应的系统信息而变更。",
        "trackingcategories-msg": "追踪分类",
        "wlshowhideanons": "匿名用户",
        "wlshowhidepatr": "已巡查的编辑",
        "wlshowhidemine": "我的编辑",
+       "wlshowhidecategorization": "页面分类",
        "watchlist-options": "监视列表选项",
        "watching": "正在监视...",
        "unwatching": "正在取消监视...",
        "unblock": "解封用户",
        "blockip": "封禁{{GENDER:$1|用户}}",
        "blockip-legend": "封禁用户",
-       "blockiptext": "使用下方的表单来禁止来自特定IP地址或用户名的写访问。\n只有在为了防止破坏,并符合[[{{MediaWiki:Policy-url}}|方针]]的情况下才可采取此行动。\n请在下面输入一个具体的理由(例如引述一个被破坏的页面)。",
+       "blockiptext": "使用下方的表单来禁止来自特定IP地址或用户名的写访问。\n只有在为了防止破坏,并符合[[{{MediaWiki:Policy-url}}|方针]]的情况下才可采取此行动。\n请在下面输入一个具体的理由(例如引述一个被破坏的页面)。\n您可以使用[https://zh.wikipedia.org/wiki/无类别域间路由 CIDR]语法封禁IP地址段;允许的最大段是/$1(用于IPv4)和/$2(用于IPv6)。",
        "ipaddressorusername": "IP地址或用户名:",
        "ipbexpiry": "终止时间:",
        "ipbreason": "原因:",
        "block-log-flags-hiddenname": "隐藏用户名",
        "range_block_disabled": "管理员执行段封禁的权限已被禁用。",
        "ipb_expiry_invalid": "无效的终止时间。",
+       "ipb_expiry_old": "终止时间已过去。",
        "ipb_expiry_temp": "隐藏用户名的封禁必须是永久性的。",
        "ipb_hide_invalid": "无法封禁此账户;它拥有多于$1次编辑。",
        "ipb_already_blocked": "“$1”已被封禁。",
        "lockedbyandtime": "(由 {{GENDER:$1|$1}} 于$2 $3执行)",
        "move-page": "移动$1",
        "move-page-legend": "移动页面",
-       "movepagetext": "您可以使用下面的表单来重命名一个页面,同时将其版本历史移动到新页面。同时老的条目将会被重定向到新条目。您可以自动地将重定向更新到原条目。如果您不选择这样做的话,请检查[[Special:DoubleRedirects|双重]]或[[Special:BrokenRedirects|损坏重定向]]链接。您有责任确保链接会被正确指向他们应该被指向的地方。\n\n注意:即使新条目已经有对应页面,此页面也<strong>不会</strong>被移动,除非新页面无任何编辑历史或是重定向页。这意味着您可在误操作后将页面移回原处,同时,您也无法覆盖现有页面。\n\n<strong>警告!</strong>对这样一个经常被访问的页面而言这可能是一个重大且唐突的更改;请在行动前先了解您的修改可能带来的一切后果。",
-       "movepagetext-noredirectfixer": "用下面的表单来重命名一个页面,并将其版本历史同时移动到新页面。\n老的页面将成为新页面的重定向页。\n请检查[[Special:DoubleRedirects|双重重定向]]或[[Special:BrokenRedirects|损坏重定向]]链接。\n您应当负责确定所有链接依然会链到指定的页面。\n\n注意如果新页面已经有内容的话,页面将'''不会'''被移动,\n除非新页面无内容或是重定向页,而且没有版本历史。\n这意味着您再必要时可以在移动到新页面后再移回老的页面,\n同时您也无法覆盖现有页面。\n\n'''警告!'''\n对一个经常被访问的页面而言这可能是一个重大与唐突的更改;\n请在行动前先确定您了解其所可能带来的后果。",
+       "movepagetext": "您可以使用下面的表单来重命名一个页面,同时将其版本历史移动到新页面。同时老的条目将会被重定向到新条目。您可以自动地将重定向更新到原条目。如果您不选择这样做的话,请检查[[Special:DoubleRedirects|双重]]或[[Special:BrokenRedirects|损坏重定向]]链接。您有责任确保链接会被正确指向他们应该被指向的地方。\n\n注意:即使新条目已经有对应页面,此页面也<strong>不会</strong>被移动,除非新页面无任何编辑历史或是重定向页。这意味着您可在误操作后将页面移回原处,同时,您也无法覆盖现有页面。\n\n<strong>注意:</strong>\n对这样一个经常被访问的页面而言这可能是一个重大且唐突的更改;请在行动前先了解您的修改可能带来的一切后果。",
+       "movepagetext-noredirectfixer": "用下面的表单来重命名一个页面,并将其版本历史同时移动到新页面。\n老的页面将成为新页面的重定向页。\n请检查[[Special:DoubleRedirects|双重重定向]]或[[Special:BrokenRedirects|损坏重定向]]链接。\n您应当负责确定所有链接依然会链到指定的页面。\n\n注意如果新页面已经有内容的话,页面将<strong>不会</strong>被移动,\n除非新页面无内容或是重定向页,而且没有版本历史。\n这意味着您再必要时可以在移动到新页面后再移回老的页面,\n同时您也无法覆盖现有页面。\n\n<strong>注意:</strong>\n对一个经常被访问的页面而言这可能是一个重大与唐突的更改;\n请在行动前先确定您了解其所可能带来的后果。",
        "movepagetalktext": "如果您勾选此框,相关联的讨论页将被自动移动到新的标题,除非这里已经有了一个非空讨论页。\n\n在这种情况下,如有需要,您将不得不手动移动或合并页面。",
        "moveuserpage-warning": "'''警告:'''你将移动一个用户页面。请注意,只有该页面会被移动,该用户''不''会被更名。",
        "movecategorypage-warning": "<strong>警告:</strong>您将移动分类页面。请注意只有此页面将会移动,旧有分类的任何页面将<em>不会</em>同步移动。",
        "movenosubpage": "这个页面没有子页面。",
        "movereason": "原因:",
        "revertmove": "恢复",
-       "delete_and_move_text": "== 需要删除 ==\n\n目标页面“[[:$1]]”已存在。是否确认删除该页面以便进行移动?",
+       "delete_and_move_text": "目标页面“[[:$1]]”已存在。您是否希望删除它以便移动?",
        "delete_and_move_confirm": "是,删除该页面",
        "delete_and_move_reason": "删除以便移动[[$1]]",
        "selfmove": "原始标题和目标标题相同,无法对页面进行自我移动。",
        "move-leave-redirect": "保留重定向",
        "protectedpagemovewarning": "'''警告:'''本页面已被保护,只有拥有管理员权限的用户可以移动。下面提供最后的日志条目以供参考:",
        "semiprotectedpagemovewarning": "'''注意:'''本页面已被保护,只有注册用户可以移动。下面提供最后的日志条目以供参考:",
-       "move-over-sharedrepo": "== 文件已存在 ==\n[[:$1]]已于共享资源存在,将文件移动到此标题会覆盖共享资源中的文件。",
+       "move-over-sharedrepo": "[[:$1]]已在一个共享的存储库存在。将文件移动到此标题将覆盖共享的文件。",
        "file-exists-sharedrepo": "同名文件已于共享资源存在。\n请选择另一个文件名。",
        "export": "导出页面",
        "exporttext": "您可以将特定页面或一组页面的文本以及编辑历史以 XML 格式导出;这样可以将有关页面通过“[[Special:Import|导入页面]]”导入到另一个运行 MediaWiki 的网站。\n\n要导出页面,请在下面的文本框中输入页面标题,每行一个标题,并选择您是否需要导出带有页面历史的以前的版本,或是只选择导出带有最后一次编辑信息的当前版本。\n\n此外您还可以利用链接导出文件,例如您可以使用[[{{#Special:Export}}/{{MediaWiki:Mainpage}}]]导出“[[{{MediaWiki:Mainpage}}]]”页面。",
        "export-download": "另存为文件",
        "export-templates": "包含模板",
        "export-pagelinks": "包含链接页面的搜索深度:",
+       "export-manual": "手动添加页面:",
        "allmessages": "系统消息",
        "allmessagesname": "名称",
        "allmessagesdefault": "默认信息文字",
        "javascripttest-pagetext-frameworks": "请选择以下的框架之一:$1",
        "javascripttest-pagetext-skins": "选择外观来运行测试:",
        "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-mycontris": "{{GENDER:|您}}的贡献的列表",
        "tooltip-pt-anoncontribs": "来自此IP地址的编辑列表",
        "tooltip-pt-login": "我们鼓励您登录;然而,这不是强制性的",
        "tooltip-pt-logout": "退出登录",
        "tooltip-t-recentchangeslinked": "链自本页的页面的最近更改",
        "tooltip-feed-rss": "本页面的RSS源",
        "tooltip-feed-atom": "本页面的Atom源",
-       "tooltip-t-contributions": "由此用户做出的贡献列表",
-       "tooltip-t-emailuser": "给该用户发送电子邮件",
+       "tooltip-t-contributions": "由{{GENDER:$1|此用户}}做出的贡献列表",
+       "tooltip-t-emailuser": "给{{GENDER:$1|该用户}}发送电子邮件",
        "tooltip-t-info": "关于此页面的更多信息",
        "tooltip-t-upload": "上传文件",
        "tooltip-t-specialpages": "所有特殊页面的列表",
        "pageinfo-category-files": "文件数",
        "markaspatrolleddiff": "标记为已巡查",
        "markaspatrolledtext": "标记此页面为已巡查",
+       "markaspatrolledtext-file": "将此文件版本标记为已巡查",
        "markedaspatrolled": "标记为已检查",
        "markedaspatrolledtext": "[[:$1]]的已选中版本已被标识为已巡查。",
-       "rcpatroldisabled": "最新更改检查被关闭",
+       "rcpatroldisabled": "最近更改巡查已禁用",
        "rcpatroldisabledtext": "最近更改巡查功能目前已关闭。",
        "markedaspatrollederror": "不能标志为已检查",
        "markedaspatrollederrortext": "你需要指定一个版本以标记为已巡查。",
        "newimages-legend": "过滤",
        "newimages-label": "文件名(或它的一部份):",
        "newimages-showbots": "显示机器人上传",
+       "newimages-hidepatrolled": "隐藏已巡查的上传",
        "noimages": "无可查看文件。",
        "ilsubmit": "搜索",
        "bydate": "按日期",
        "scarytranscludefailed-httpstatus": "[模板$1读取失败:HTTP $2]",
        "scarytranscludetoolong": "[URL过长]",
        "deletedwhileediting": "<strong>警告:</strong>此页在您开始编辑之后已经被删除!",
-       "confirmrecreate": "在您开始编辑这个页面后,用户[[User:$1|$1]] ([[User talk:$1|讨论]])以下列原因删除了这个页面:\n: ''$2''\n请确认在您重新创建页面前三思。",
-       "confirmrecreate-noreason": "用户 [[User:$1|$1]]([[User talk:$1|talk]]) 在您开始编辑之后删除此页面。请确认您确实要重新创建此页面。",
+       "confirmrecreate": "在您开始编辑后,[[User:$1|$1]]([[User talk:$1|讨论]])因以下列原因{{GENDER:$1|删除}}了该页面:\n: <em>$2</em>\n请确认在您重新创建页面前三思。",
+       "confirmrecreate-noreason": "用户 [[User:$1|$1]]([[User talk:$1|talk]]) 在您开始编辑之后{{GENDER:$1|删除}}此页面。请确认您确实要重新创建此页面。",
        "recreate": "重新创建",
        "confirm_purge_button": "确定",
        "confirm-purge-top": "要清除此页面的缓存吗?",
        "watchlisttools-edit": "查看并编辑监视列表",
        "watchlisttools-raw": "编辑原始监视列表",
        "signature": "[[{{ns:user}}:$1|$2]]([[{{ns:user_talk}}:$1|讨论]])",
+       "timezone-local": "本地",
        "duplicate-defaultsort": "<strong>警告:</strong>默认排序关键词“$2”覆盖了之前的默认排序关键词“$1”。",
        "duplicate-displaytitle": "<strong>警告:</strong>显示的标题“$2”重写了此前显示的标题“$1”。",
        "invalid-indicator-name": "<strong>错误:</strong>页面状态指示器的<code>name</code>属性必须不为空。",
        "version-libraries-license": "许可协议",
        "version-libraries-description": "描述",
        "version-libraries-authors": "作者",
-       "redirect": "重定向(按文件、用户、页面或版本ID)",
+       "redirect": "重定向(按文件、用户、页面、修订版本ID或日志ID)",
        "redirect-legend": "重定向至文件或页面",
-       "redirect-summary": "本特殊页面可以跳转至一个文件(提供文件名)、页面(提供版本ID或页面ID)或用户页面(提供数字用户ID)。用法:[[{{#Special:Redirect}}/file/Example.jpg]]、[[{{#Special:Redirect}}/page/64308]]、[[{{#Special:Redirect}}/revision/328429]]或[[{{#Special:Redirect}}/user/101]]。",
+       "redirect-summary": "本特殊页面可以跳转至一个文件(提供文件名)、页面(提供修订版本ID或页面ID)、用户页(提供数字用户ID)或日志记录(提供日志ID)。用法:[[{{#Special:Redirect}}/file/Example.jpg]]、[[{{#Special:Redirect}}/page/64308]]、[[{{#Special:Redirect}}/revision/328429]]、[[{{#Special:Redirect}}/user/101]]或[[{{#Special:Redirect}}/logid/186]]。",
        "redirect-submit": "提交",
        "redirect-lookup": "基于:",
        "redirect-value": "值:",
        "redirect-page": "页面ID",
        "redirect-revision": "页面版本ID",
        "redirect-file": "文件名",
+       "redirect-logid": "日志ID",
        "redirect-not-exists": "没找到相应值",
        "fileduplicatesearch": "搜索重复文件",
        "fileduplicatesearch-summary": "根据哈希(hash)值搜索重复文件。",
        "compare-submit": "对比",
        "compare-invalid-title": "您指定的标题无效。",
        "compare-title-not-exists": "您指定的标题不存在。",
-       "compare-revision-not-exists": "指定的版本不存在。",
+       "compare-revision-not-exists": "指定的版本不存在。",
        "dberr-problems": "抱歉!本网站出现了一些技术问题。",
        "dberr-again": "请等待几分钟后重试。",
        "dberr-info": "(无法访问数据库:$1)",
        "expand_templates_preview": "预览",
        "expand_templates_preview_fail_html": "<em>因为{{SITENAME}}启用了Raw HTML并且丢失了会话数据,预览被隐藏以防止JavaScript攻击。</em>\n\n<strong>如果这是合法的预览尝试,请再次重试。</strong>\n如果仍然不能工作,尝试[[Special:UserLogout|退出]]并重新登录。",
        "expand_templates_preview_fail_html_anon": "<em>因为{{SITENAME}}启用了Raw 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字节}}($2;$3%)",
+       "mediastatistics-bytespertype": "此段落的总文件大小:{{PLURAL:$1|$1字节}}($2;$3%)。",
+       "mediastatistics-allbytes": "所有文件的总文件大小:{{PLURAL:$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个结尾逗号从JSON移除",
        "json-error-unknown": "JSON出现问题。错误:$1",
        "json-error-depth": "超出最大堆栈深度",
        "mw-widgets-dateinput-no-date": "没有选定日期",
        "mw-widgets-titleinput-description-new-page": "页面不存在",
        "mw-widgets-titleinput-description-redirect": "重定向至$1",
-       "api-error-blacklisted": "请选择其他描述性的标题。"
+       "api-error-blacklisted": "请选择其他描述性的标题。",
+       "sessionmanager-tie": "不能结合多个请求的身份验证类型:$1。",
+       "sessionprovider-generic": "$1会话",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "基于cookie的会话",
+       "sessionprovider-nocookies": "Cookie可能已被禁用。确保您已启用cookie,并重试。",
+       "randomrootpage": "随机根页面"
 }
index 6797eb0..e2c79f6 100644 (file)
                ]
        },
        "tog-underline": "底線標示連結:",
-       "tog-hideminor": "隱藏最近變更中的小修訂",
-       "tog-hidepatrolled": "隱藏最近變更中巡查過的編輯",
+       "tog-hideminor": "隱藏近期變更中的小修訂",
+       "tog-hidepatrolled": "隱藏近期變更中巡查過的編輯",
        "tog-newpageshidepatrolled": "隱藏新頁面清單中巡查過的頁面",
        "tog-hidecategorization": "隱藏頁面分類",
        "tog-extendwatchlist": "展開監視清單顯示包含最近以外的所有變更",
-       "tog-usenewrc": "依最近變更與監視清單的頁面分類顯示變更",
+       "tog-usenewrc": "依近期變更與監視清單的頁面分類顯示變更",
        "tog-numberheadings": "標題自動編號",
        "tog-showtoolbar": "顯示編輯工具列",
        "tog-editondblclick": "開啟滑鼠雙擊編輯頁面",
        "tog-watchlisthidebots": "隱藏監視清單中機器人的編輯",
        "tog-watchlisthideminor": "隱藏監視清單中的小修訂",
        "tog-watchlisthideliu": "隱藏監視清單中已登入使用者的編輯",
+       "tog-watchlistreloadautomatically": "查詢條件變更時自動重新讀取監視清單 (需要使用 JavaScript)",
        "tog-watchlisthideanons": "隱藏監視清單中匿名使用者的編輯",
        "tog-watchlisthidepatrolled": "隱藏監視清單中已巡查的編輯",
        "tog-watchlisthidecategorization": "隱藏頁面分類",
        "october-date": "十月 $1 日",
        "november-date": "十一月 $1 日",
        "december-date": "十二月 $1 日",
+       "period-am": "AM",
+       "period-pm": "PM",
        "pagecategories": "{{PLURAL:$1|分類|$1 個分類}}",
        "category_header": "分類 \"$1\" 中的頁面",
        "subcategories": "子分類",
        "site-atom-feed": "$1 的 Atom 摘要",
        "page-rss-feed": "\"$1\" 的 RSS 摘要",
        "page-atom-feed": "\"$1\" 的 Atom 摘要",
-       "red-link-title": "$1(頁面不存在)",
+       "red-link-title": "$1 (頁面不存在)",
        "sort-descending": "降冪排序",
        "sort-ascending": "昇冪排序",
        "nstab-main": "頁面",
        "databaseerror-query": "查詢:$1",
        "databaseerror-function": "功能:$1",
        "databaseerror-error": "錯誤:$1",
+       "transaction-duration-limit-exceeded": "為了避免造成大量備援延遲,因寫入時間 ($1) 已超出了 $2 {{PLURAL:$2|秒|秒}}限制,此次操作已被中止。\n若您一次修改了許多項目,可嘗試分批處理。",
        "laggedslavemode": "<strong>警告:</strong>頁面可能不包含最近的更新。",
        "readonly": "資料庫已鎖定",
        "enterlockreason": "請輸入鎖定的原因,包括估計重新開放的時間",
-       "readonlytext": "資料庫目前已鎖定無法新增或修改資料,\n可能正在進行例行的資料庫維修作業,完成之後即可恢復正常。\n\n鎖定資料庫的管理員說明:$1",
+       "readonlytext": "è³\87æ\96\99庫ç\9b®å\89\8då·²é\8e\96å®\9aç\84¡æ³\95æ\96°å¢\9eæ\88\96ä¿®æ\94¹è³\87æ\96\99ï¼\8c\nå\8f¯è\83½æ­£å\9c¨é\80²è¡\8cä¾\8bè¡\8cç\9a\84è³\87æ\96\99庫維修ä½\9c業ï¼\8cå®\8cæ\88\90ä¹\8bå¾\8cå\8d³å\8f¯æ\81¢å¾©æ­£å¸¸ã\80\82\n\né\8e\96å®\9aè³\87æ\96\99庫ç\9a\84系統管ç\90\86å\93¡èªªæ\98\8eï¼\9a$1",
        "missing-article": "資料庫查無預期的頁面文字,名稱為 \"$1\" $2。\n\n通常是因您連結到了已被刪除的差異或歷史頁面。\n\n若您所遇到的不是這個情況,您可能是發現軟體問題。\n請記錄 URL 位址,並向 [[Special:ListUsers/sysop|管理員]] 回報此問題。",
        "missingarticle-rev": "(修訂#:$1)",
        "missingarticle-diff": "(差異:$1, $2)",
        "mypreferencesprotected": "您沒有權限編輯您的偏好設定。",
        "ns-specialprotected": "特殊頁面無法編輯。",
        "titleprotected": "此標題已經被 [[User:$1|$1]] 保護以防止建立,原因是 \"<em>$2</em>\"。",
-       "filereadonlyerror": "無法修改檔案 \"$1\" 因為檔案庫 \"$2\" 目前處於唯讀模式。\n\n鎖定的管理員說明:\"$3\"。",
+       "filereadonlyerror": "ç\84¡æ³\95ä¿®æ\94¹æª\94æ¡\88 \"$1\" å\9b ç\82ºæª\94æ¡\88庫 \"$2\" ç\9b®å\89\8dè\99\95æ\96¼å\94¯è®\80模å¼\8fã\80\82\n\né\8e\96å®\9aç\9a\84系統管ç\90\86å\93¡èªªæ\98\8eï¼\9a\"$3\"ã\80\82",
        "invalidtitle-knownnamespace": "命名空間 \"$2\" 與名稱 \"$3\" 是無效的標題",
        "invalidtitle-unknownnamespace": "不明的命名空間編號 $1 與名稱 \"$2\" 是無效的標題",
        "exception-nologin": "未登入",
        "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": "您不可變更此 Wiki 上的密碼。",
        "externaldberror": "這可能是由於資料庫驗證錯誤,或是不允許您更新外部帳號。",
        "wrongpasswordempty": "輸入的密碼是空的。\n請再試一次。",
        "passwordtooshort": "您的密碼至少需要 $1 個字元。",
        "passwordtoolong": "密碼不能超過 {{PLURAL:$1|1 個字元|$1 個字元}}。",
+       "passwordtoopopular": "不能使用常見的密碼,請選擇使用更具獨特性的密碼。",
        "password-name-match": "您的密碼不可以跟使用者名稱相同。",
        "password-login-forbidden": "此使用者名稱和密碼已被禁止使用。",
        "mailmypassword": "重設密碼",
        "resetpass_submit": "設定密碼並登入",
        "changepassword-success": "您的密碼已變更成功!",
        "changepassword-throttled": "您最近嘗試了太多次登入。\n請等待 $1 後再試。",
+       "botpasswords": "機器人密碼",
+       "botpasswords-summary": "<em>機器人密碼</em> 可在不需帳號的主要登入密碼情況下,允許透過 API 存取使用者帳號。 可限制使用機器人密碼登入的使用者權限。\n\n若在尚無法了解為何要設定機器人密碼之前不應使用此功能。 且不該有任何人會向您索取機器人密碼。",
+       "botpasswords-disabled": "機器人密碼已停用。",
+       "botpasswords-no-central-id": "要使用機器人密碼,您必須登入帳號集中管理系統。",
+       "botpasswords-existing": "已存在機器人密碼",
+       "botpasswords-createnew": "建立新機器人密碼",
+       "botpasswords-editexisting": "編輯已存在的機器人密碼",
+       "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": "機器人密碼 \"$1\" 已建立成功。",
+       "botpasswords-updated-title": "已更新機器人密碼",
+       "botpasswords-updated-body": "機器人密碼 \"$1\" 已修改成功。",
+       "botpasswords-deleted-title": "已刪除機器人密碼",
+       "botpasswords-deleted-body": "機器人密碼 \"$1\" 已刪除。",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider 無法使用。",
+       "botpasswords-restriction-failed": "機器人密碼限制已拒絕此次登入。",
+       "botpasswords-invalid-name": "指定的使用者名稱未包含機器人密碼分隔字元 (\"$1\")。",
+       "botpasswords-not-exist": "使用者 \"$1\" 並沒有名稱為 \"$2\" 的機器人密碼。",
        "resetpass_forbidden": "無法變更密碼",
        "resetpass-no-info": "您必須直接登入存取這個頁面。",
        "resetpass-submit-loggedin": "變更密碼",
        "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-emailsentusername": "若此確實為您使用者名稱所登記的電子郵件地址,將會寄出重設密碼的信件給您。",
        "passwordreset-emailsent-capture": "已寄出重設密碼的電子郵件,並於下方顯示。",
        "passwordreset-emailerror-capture": "下列為重設密碼的電子郵件內容,傳送給{{GENDER:$2|使用者}}失敗:$1",
        "changeemail": "變更或移除電子郵件地址",
        "copyrightwarning2": "請注意,所有於 {{SITENAME}} 所做的貢獻可能會被其他貢獻者編輯,修改或刪除。\n若您不希望您的著作被任意修改與散佈,請勿在此發表文章。<br />\n您同時向我們保証在此的著作內容是您自行撰寫,或是取自不受版權保護的公開領域或自由資源 (詳情請見 $1)。\n<strong>請勿在未經授權的情況下發表文章!</strong>",
        "editpage-cannot-use-custom-model": "此頁面的內容模型不能被修改。",
        "longpageerror": "<strong>錯誤:您所送出的文字內容共有 {{PLURAL:$1|1 KB|$1 KB}},已超出系統上限 {{PLURAL:$2|1 KB|$2 KB}}。</strong>\n\n無法儲存。",
-       "readonlywarning": "<strong>警告:資料庫已被鎖定以進行維護,因此無法儲存您目前所做的編輯動作。</strong>\n您可先複製您的文字並貼上到文字檔案中儲存,稍後再儲存您編輯。\n\n鎖定資料庫的管理員有以下說明:$1",
+       "readonlywarning": "<strong>è­¦å\91\8aï¼\9aè³\87æ\96\99庫已被é\8e\96å®\9a以é\80²è¡\8c維護ï¼\8cå\9b æ­¤ç\84¡æ³\95å\84²å­\98æ\82¨ç\9b®å\89\8dæ\89\80å\81\9aç\9a\84編輯å\8b\95ä½\9cã\80\82</strong>\næ\82¨å\8f¯å\85\88è¤\87製æ\82¨ç\9a\84æ\96\87å­\97並貼ä¸\8aå\88°æ\96\87å­\97æª\94æ¡\88中å\84²å­\98ï¼\8cç¨\8då¾\8cå\86\8då\84²å­\98æ\82¨ç·¨è¼¯ã\80\82\n\né\8e\96å®\9aè³\87æ\96\99庫ç\9a\84系統管ç\90\86å\93¡æ\9c\89以ä¸\8b說æ\98\8eï¼\9a$1",
        "protectedpagewarning": "<strong>警告:本頁已經被保護,只有擁有管理員權限的使用者才可編輯。</strong>\n以下提供最近的日誌以便參考:",
        "semiprotectedpagewarning": "<strong>注意:</strong>本頁已經被保護,只有已註冊的使用者才可編輯。\n以下提供最近的日誌以便參考:",
        "cascadeprotectedwarning": "<strong>警告:</strong>本頁已經被保護,只有擁有管理員權限的使用者才可編輯,此頁面被下列{{PLURAL:$1|頁面|頁面}}引用因此連鎖保護:",
        "permissionserrors": "權限錯誤",
        "permissionserrorstext": "由於下列{{PLURAL:$1|原因}},您沒有權限進行目前的動作:",
        "permissionserrorstext-withaction": "由於下列{{PLURAL:$1|原因}},您沒有權限進行 $2 的動作:",
-       "contentmodelediterror": "æ\82¨ç\84¡æ³\95編輯此修è¨\82ï¼\8cå\9b æ­¤ä¿®è¨\82使ç\94¨ç\9a\84å\85§å®¹æ¨¡å\9e\8bç\82º <code>$1</code> è\80\8cç\9b®å\89\8d使ç\94¨ç\9a\84é \81é\9d¢å\85§å®¹æ¨¡å\9e\8bç\82º <code>$2</code>。",
+       "contentmodelediterror": "æ\82¨ç\84¡æ³\95編輯此修è¨\82ï¼\8cå\9b æ­¤ä¿®è¨\82使ç\94¨ç\9a\84å\85§å®¹æ¨¡å\9e\8bç\82º <code>$1</code> è\88\87ç\9b®å\89\8d使ç\94¨ç\9a\84é \81é\9d¢å\85§å®¹æ¨¡å\9e\8b <code>$2</code> ä¸\8då\90\8c。",
        "recreate-moveddeleted-warn": "<strong>警告:您正重新建立先前已刪除的頁面。</strong>\n\n您應考慮是否繼續編輯此頁。\n在此提供刪除與移動日誌方便作為參考:",
        "moveddeleted-notice": "此頁面已刪除。\n下方提供此頁面的刪除和移動日誌以便參考。",
        "moveddeleted-notice-recent": "抱歉,此頁面最近被刪除 (24 小時內)。\n以下提供此頁面的刪除與移動日誌做為參考。",
        "rev-suppressed-unhide-diff": "檢視差異的其中一個修訂已被 <strong>禁止顯示</strong>。\n可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。\n若您要繼續,您仍可以 [$1 檢視此差異]。",
        "rev-deleted-diff-view": "檢視差異的其中一個修訂已被 <strong>刪除</strong>。\n您可繼續檢視差異,可至 [{{fullurl:{{#Special:Log}}/delete|page={{FULLPAGENAMEE}}}} 刪除日誌] 取得詳細資訊。",
        "rev-suppressed-diff-view": "檢視差異的其中一個修訂已被 <strong>禁止顯示</strong>。\n您可繼續檢視差異,可至 [{{fullurl:{{#Special:Log}}/suppress|page={{FULLPAGENAMEE}}}} 禁止顯示日誌] 取得詳細資訊。",
-       "rev-delundel": "更改顯示設定",
+       "rev-delundel": "變更可見性",
        "rev-showdeleted": "顯示",
        "revisiondelete": "刪除/取消刪除修訂",
        "revdelete-nooldid-title": "無效的目標修訂",
        "stub-threshold": "短頁面連結格式門檻值 ($1):",
        "stub-threshold-sample-link": "樣本",
        "stub-threshold-disabled": "已停用",
-       "recentchangesdays": "最近更改顯示的天數:",
+       "recentchangesdays": "最近變更顯示的天數:",
        "recentchangesdays-max": "最多 $1 {{PLURAL:$1|天}}",
        "recentchangescount": "預設顯示的編輯數:",
        "prefs-help-recentchangescount": "這包含最近更改、頁面歷史以及日誌。",
        "userrights-notallowed": "您沒有權限加入或刪除使用者權限。",
        "userrights-changeable-col": "您可變更的群組",
        "userrights-unchangeable-col": "您不可變更的群組",
-       "userrights-conflict": "使用者權限更改發生衝突!請重新檢視並確認你的更改。",
+       "userrights-conflict": "使用者權限變更發生衝突!請檢閱並確認你的變更。",
        "userrights-removed-self": "您已成功移除自己的權限,您已無法再次存取此頁面。",
        "group": "群組:",
        "group-user": "使用者",
        "right-createpage": "建立頁面 (不含討論頁面)",
        "right-createtalk": "建立討論頁面",
        "right-createaccount": "建立新的使用者帳號",
+       "right-autocreateaccount": "使用外部使用者帳號自動登入",
        "right-minoredit": "標示編輯為小修訂",
        "right-move": "移動頁面",
        "right-move-subpages": "移動頁面與其子頁面",
        "right-blockemail": "封鎖使用者傳送電子郵件",
        "right-hideuser": "封鎖使用者名稱,避免公開顯示",
        "right-ipblock-exempt": "略過 IP 封鎖、自動封鎖及範圍封鎖檢查",
-       "right-proxyunbannable": "略過 Proxy 自動封鎖檢查",
        "right-unblockself": "解除封鎖自己",
        "right-protect": "更改保護層級及編輯被連鎖保護的頁面",
        "right-editprotected": "編輯保護層級為 \"{{int:protect-level-sysop}}\" 的頁面",
        "right-importupload": "由檔案上傳匯入頁面",
        "right-patrol": "標示其他人的編輯爲已巡查",
        "right-autopatrol": "將自己的編輯自動標示為已巡查",
-       "right-patrolmarks": "檢視最近更改的巡查標記",
+       "right-patrolmarks": "檢視最近變更的巡查標記",
        "right-unwatchedpages": "檢視未監視的頁面",
        "right-mergehistory": "合併頁面歷史",
        "right-userrights": "編輯所有使用者的權限",
        "right-managechangetags": "建立並自資料庫移除[[Special:Tags|標籤]]",
        "right-applychangetags": "連同某個人的變更一起套用[[Special:Tags|標籤]]",
        "right-changetags": "加入與移除任何於各別修訂與日誌項目的[[Special:Tags|標籤]]",
+       "grant-generic": "\"$1\" 權限組合",
+       "grant-group-page-interaction": "與頁面互動",
+       "grant-group-file-interaction": "與媒體互動",
+       "grant-group-watchlist-interaction": "與您的監視清單互動",
+       "grant-group-email": "傳送電子郵件",
+       "grant-group-high-volume": "執行大量活動",
+       "grant-group-customization": "自訂與偏好設定",
+       "grant-group-administration": "執行管理操作",
+       "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-editprotected": "編輯受保護的頁面",
+       "grant-highvolume": "大量編輯",
+       "grant-oversight": "隱藏使用者和禁止顯示修訂",
+       "grant-patrol": "巡邏頁面的變更",
+       "grant-protect": "保護與取消保護頁面",
+       "grant-rollback": "還原頁面的變更",
+       "grant-sendemail": "傳送電子郵件聯絡其他使用者",
+       "grant-uploadeditmovefile": "上傳、取代與移動檔案",
+       "grant-uploadfile": "上傳新檔案",
+       "grant-basic": "基本權限",
+       "grant-viewdeleted": "檢視已刪除的檔案及頁面",
+       "grant-viewmywatchlist": "檢視您的監視清單",
        "newuserlogpage": "建立使用者日誌",
        "newuserlogpagetext": "此為建立使用者的日誌。",
        "rightslog": "使用者權限日誌",
        "action-createpage": "建立頁面",
        "action-createtalk": "建立討論頁面",
        "action-createaccount": "建立此使用者帳號",
+       "action-autocreateaccount": "自動建立此外部使用者帳號",
        "action-history": "檢視此頁面歷史",
        "action-minoredit": "標示此編輯為小修訂",
        "action-move": "移動此頁面",
        "recentchanges-legend": "最近變更選項",
        "recentchanges-summary": "追蹤 Wiki 中此頁面的最近更改。",
        "recentchanges-noresult": "於指定時間內沒有符合條件的變更。",
-       "recentchanges-feed-description": "追蹤 Wiki 中此訂閱來源的最近更改。",
+       "recentchanges-feed-description": "追蹤 Wiki 中此摘要的最近變更。",
        "recentchanges-label-newpage": "該編輯建立了新頁面",
        "recentchanges-label-minor": "該編輯是一個小修訂",
        "recentchanges-label-bot": "該編輯由機器人執行",
        "recentchanges-legend-heading": "'''說明:'''",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (請參考[[Special:NewPages|新頁面]])",
        "recentchanges-legend-plusminus": "(<em>±123</em>)",
+       "recentchanges-submit": "顯示",
        "rcnotefrom": "以下{{PLURAL:$5|為}}自 <strong>$3 $4</strong> 以來的變更 (最多顯示 <strong>$1</strong> 筆)。",
-       "rclistfrom": "顯示è\87ª $3 $2 ä»¥ä¾\86ç\9a\84æ\9c\80è¿\91æ\9b´æ\94¹",
+       "rclistfrom": "顯示è\87ª $3 $2 ä»¥ä¾\86ç\9a\84æ\96°è®\8aæ\9b´",
        "rcshowhideminor": "$1 小修訂",
        "rcshowhideminor-show": "顯示",
        "rcshowhideminor-hide": "隱藏",
        "foreign-structured-upload-form-label-own-work-message-shared": "我保証我擁有此檔案的版權,並且不反悔同意使用 [https://creativecommons.org/licenses/by-sa/4.0/ Creative Commons Attribution-ShareAlike 4.0] 授權條款發佈此檔案到維基媒體共享資源,並且我同意 [https://wikimediafoundation.org/wiki/Terms_of_Use 使用條款]。",
        "foreign-structured-upload-form-label-not-own-work-message-shared": "若您並未擁有此檔案的版權,或者您希望使用其他的授權條款發佈此檔案,請考慮使用[https://commons.wikimedia.org/wiki/Special:UploadWizard 通用上傳精靈]。",
        "foreign-structured-upload-form-label-not-own-work-local-shared": "若該站的授權政策允許上傳此檔案,您可能會希望直接嘗試使用 [[Special:Upload|{{SITENAME}} 的上傳頁面]]。",
+       "foreign-structured-upload-form-2-label-intro": "感謝您貢獻影像給 {{SITENAME}},您應確認您的影像符合以下條件下繼續:",
+       "foreign-structured-upload-form-2-label-ownwork": "影像必須完全由<strong>您所創作</strong>,並非取自網際網路",
+       "foreign-structured-upload-form-2-label-noderiv": "影像並未包含<strong>他人的成果</strong>,或是來自他人的靈感",
+       "foreign-structured-upload-form-2-label-useful": "影像應具有<strong>教育意義</strong>並對教學他人有幫助",
+       "foreign-structured-upload-form-2-label-ccbysa": "影像必須採用 [https://creativecommons.org/licenses/by-sa/4.0/ 創用CC 姓名標示─相同方式分享 4.0] 授權條款,<strong>可以永久發布r</strong>於網際網路上",
+       "foreign-structured-upload-form-3-label-question-website": "您是否自其他網站下載此影像,或取自影像搜尋的結果?",
+       "foreign-structured-upload-form-3-label-yes": "是",
+       "foreign-structured-upload-form-3-label-no": "否",
        "backend-fail-stream": "無法傳輸檔案 \"$1\"。",
        "backend-fail-backup": "無法備份檔案 \"$1\"。",
        "backend-fail-notexists": "檔案 $1 不存在。",
        "mostrevisions": "最多修訂的頁面",
        "prefixindex": "所有頁面與字首",
        "prefixindex-namespace": "所有含字首的頁面 ($1 命名空間)",
+       "prefixindex-submit": "顯示",
        "prefixindex-strip": "於清單中省略字首",
        "shortpages": "過短的頁面",
        "longpages": "過長的頁面",
        "protectedpages-performer": "保護使用者",
        "protectedpages-params": "保護參數",
        "protectedpages-reason": "原因",
+       "protectedpages-submit": "顯示頁面",
        "protectedpages-unknown-timestamp": "不明",
        "protectedpages-unknown-performer": "不明的使用者",
        "protectedtitles": "受保護標題",
        "protectedtitles-summary": "此頁面列出目前受保護的標題。 欲查詢受保護頁面清單,請參考 [[{{#special:ProtectedPages}}|{{int:protectedpages}}]]。",
        "protectedtitlesempty": "目前沒有使用這些參數的受保護標題。",
+       "protectedtitles-submit": "顯示標題",
        "listusers": "使用者清單",
        "listusers-editsonly": "只顯示有編輯的使用者",
        "listusers-creationsort": "依建立日期排序",
        "usereditcount": "$1 次{{PLURAL:$1|編輯}}",
        "usercreated": "於 $1 $2 {{GENDER:$3|建立}}",
        "newpages": "新頁面",
+       "newpages-submit": "顯示",
        "newpages-username": "使用者名稱:",
        "ancientpages": "最舊頁面",
        "move": "移動",
        "specialloguserlabel": "執行者:",
        "speciallogtitlelabel": "目標 (標題或以 {{ns:user}}:使用者 表示使用者):",
        "log": "日誌",
+       "logeventslist-submit": "顯示",
        "all-logs-page": "所有公開日誌",
        "alllogstext": "合併顯示所有 {{SITENAME}} 中所有類型的日誌。\n您可以點選下拉式選單選擇日誌的類型,指定使用者名稱 (區分大小寫) 或影響的頁面 (區分大小寫)。",
        "logempty": "無符合條件的日誌。",
        "log-title-wildcard": "搜尋以此欄位文字為字首的標題",
        "showhideselectedlogentries": "顯示/隱藏已選擇的日誌項目",
        "log-edit-tags": "編輯已選擇日誌項目的標籤",
+       "checkbox-all": "全部",
        "allpages": "所有頁面",
        "nextpage": "下一頁 ($1)",
        "prevpage": "上一頁 ($1)",
        "cachedspecial-viewing-cached-ts": "你正在檢視此頁面的快取版本,可能不完全與實際相同。",
        "cachedspecial-refresh-now": "檢視最新版本。",
        "categories": "分類",
+       "categories-submit": "顯示",
        "categoriespagetext": "下列為包含頁面或媒體的{{PLURAL:$1|分類}}。\n[[Special:UnusedCategories|未使用的分類]] 不會在此顯示。\n請參考 [[Special:WantedCategories|需要的分類]]。",
        "categoriesfrom": "顯示分類開始於:",
        "special-categories-sort-count": "依數量排列",
        "activeusers-hidebots": "隱藏機器人",
        "activeusers-hidesysops": "隱藏管理員",
        "activeusers-noresult": "查無使用者。",
+       "activeusers-submit": "顯示活動中的使用者",
        "listgrouprights": "使用者群組權限",
        "listgrouprights-summary": "以下為此 Wiki 的使用者群組清單,以及相關的存取權限。\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-summary": "以下為與使用者權限相關連的授權清單。使用者可以授權應用程式使用他們自己的帳號,但限於使用者授予應用程式的權限。同時,應用程式亦不能使用使用者本身沒有的權限。\n您可能可以在[[{{MediaWiki:Listgrouprights-helppage}}|這裡]]取得個別權限的額外資料。",
+       "listgrants-grant": "授權",
+       "listgrants-rights": "權限",
        "trackingcategories": "追蹤分類",
        "trackingcategories-summary": "此頁面列出由 MediaWiki 軟體自動產生用來追蹤頁面的分類,這些分類的名稱可由命名空間 {{ns:8}} 中的相關系統訊息中修改。",
        "trackingcategories-msg": "追蹤分類",
        "wlshowlast": "顯示最近 $1 小時 $2 天",
        "watchlistall2": "全部",
        "watchlist-hide": "隱藏",
-       "wlshowtime": "顯示最近:",
+       "watchlist-submit": "顯示",
+       "wlshowtime": "要顯示的時間長度:",
        "wlshowhideminor": "小編輯",
        "wlshowhidebots": "機器人",
        "wlshowhideliu": "已註冊使用者",
        "wlshowhideanons": "匿名使用者",
        "wlshowhidepatr": "已巡查編輯",
        "wlshowhidemine": "我的編輯",
+       "wlshowhidecategorization": "頁面分類",
        "watchlist-options": "監視清單選項",
        "watching": "正在監視...",
        "unwatching": "正在停止監視...",
        "delete-confirm": "刪除 \"$1\"",
        "delete-legend": "刪除",
        "historywarning": "<strong>警告:</strong>您正要刪除的頁面內含 $1 次{{PLURAL:$1|的修訂}}歷史:",
+       "historyaction-submit": "顯示",
        "confirmdeletetext": "您正要刪除一個頁面或圖片以及其所有歷史。\n請確定您了解要進行此項操作所造成的後果,同時確認您的行為符合[[{{MediaWiki:Policy-url}}]] 規範。",
        "actioncomplete": "操作完成",
        "actionfailed": "操作失敗",
        "rollback-success": "已還原 $1 所做的編輯;\n變更回由 $2 修訂的最後一個版本。",
        "sessionfailure-title": "連線階段失敗",
        "sessionfailure": "您的登入連線階段似乎有問題,\n為了預防連線階段受到劫持攻擊,此動作已經被取消。\n請返回上一頁,重新讀取該頁面再試一次。",
-       "changecontentmodel": "更改頁面的內容模型",
+       "changecontentmodel": "變更頁面的內容模型",
        "changecontentmodel-legend": "變更內容模型",
        "changecontentmodel-title-label": "頁面標題",
        "changecontentmodel-model-label": "新內容模型",
        "whatlinkshere-hidelinks": "$1 連結",
        "whatlinkshere-hideimages": "$1 檔案連結",
        "whatlinkshere-filters": "搜尋",
+       "whatlinkshere-submit": "前往",
        "autoblockid": "自動封鎖 #$1",
        "block": "封鎖使用者",
        "unblock": "解除封鎖使用者",
        "blockip": "封鎖{{GENDER:$1|使用者}}",
        "blockip-legend": "封鎖使用者",
-       "blockiptext": "填寫以下表單可封鎖特定 IP 位址或使用者名稱的存取權限。\n這個動作應用來避免破壞行為,可根據 [[{{MediaWiki:Policy-url}}|管理政策]]。\n請在下方填寫一個具體的原因 (例如:引述一段破壞頁面的事實)。",
+       "blockiptext": "填寫以下表單可封鎖特定 IP 位址或使用者名稱的存取權限。\n這個動作應用來避免破壞行為,可根據 [[{{MediaWiki:Policy-url}}|管理政策]]。\n請在下方填寫一個具體的原因 (例如:引述一段破壞頁面的事實)。\n您可以使用 [https://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing CIDR] 語法格式封鎖 IP 範圍,最大允許的範圍 IPv4 為 /$1、IPv6 為 /$2。",
        "ipaddressorusername": "IP 位址或使用者名稱:",
        "ipbexpiry": "期限:",
        "ipbreason": "原因:",
        "export-download": "儲存為檔案",
        "export-templates": "包含模板",
        "export-pagelinks": "包含連結的頁面深度:",
+       "export-manual": "手動加入頁面:",
        "allmessages": "系統訊息",
        "allmessagesname": "名稱",
        "allmessagesdefault": "預設的訊息文字",
        "javascripttest-pagetext-frameworks": "請選擇下列一種測試 Framework:$1",
        "javascripttest-pagetext-skins": "選擇執行測試的外觀:",
        "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-mycontris": "{{GENDER:|您的}}貢獻清單",
+       "tooltip-pt-anoncontribs": "由此 IP 位址編輯的清單",
        "tooltip-pt-login": "建議您先登入,但並非必要。",
        "tooltip-pt-logout": "登出",
        "tooltip-pt-createaccount": "我們會鼓勵您建立一個帳號並且登入,即使這不是必要的動作。",
        "tooltip-n-randompage": "隨機進入一個頁面",
        "tooltip-n-help": "尋求協助的地方",
        "tooltip-t-whatlinkshere": "列出所有連結此頁面的頁面",
-       "tooltip-t-recentchangeslinked": "此頁面連結到其他頁面的最近更改",
+       "tooltip-t-recentchangeslinked": "此頁面連結至其他頁面的最近變更",
        "tooltip-feed-rss": "此頁面的 RSS 摘要",
        "tooltip-feed-atom": "此頁面的 Atom 摘要",
-       "tooltip-t-contributions": "此使用者的貢獻清單",
-       "tooltip-t-emailuser": "傳送電子郵件聯絡這位使用者",
+       "tooltip-t-contributions": "{{GENDER:$1|此使用者}}的貢獻清單",
+       "tooltip-t-emailuser": "傳送電子郵件聯絡{{GENDER:$1|這位使用者}}",
        "tooltip-t-info": "更多關於此頁面的資訊",
        "tooltip-t-upload": "上傳檔案",
        "tooltip-t-specialpages": "全部特殊頁面的清單",
        "pageinfo-category-files": "檔案數量",
        "markaspatrolleddiff": "標記為已巡查",
        "markaspatrolledtext": "標記此頁面為已巡查",
+       "markaspatrolledtext-file": "標記此檔案版本為己巡查",
        "markedaspatrolled": "己標記為已巡查",
        "markedaspatrolledtext": "已標記選擇的修訂 [[:$1]] 為已巡查。",
-       "rcpatroldisabled": "最近更改巡查已停用",
+       "rcpatroldisabled": "最近變更巡查已停用",
        "rcpatroldisabledtext": "最新變更巡查的功能目前已停用。",
        "markedaspatrollederror": "無法標記為已巡查",
        "markedaspatrollederrortext": "您需指定要標記為已巡查的修訂。",
        "newimages-legend": "搜尋",
        "newimages-label": "檔案名稱 (或部份檔名):",
        "newimages-showbots": "顯示由機器人上傳的檔案",
+       "newimages-hidepatrolled": "隱藏己巡查上傳",
        "noimages": "無任何圖片。",
        "ilsubmit": "搜尋",
        "bydate": "依日期",
        "exif-compression-6": "JPEG (舊)",
        "exif-copyrighted-true": "受版權保護",
        "exif-copyrighted-false": "版權狀態不明",
+       "exif-photometricinterpretation-1": "黑白 (黑為 0)",
        "exif-unknowndate": "日期不明",
        "exif-orientation-1": "標準",
        "exif-orientation-2": "水平翻轉",
        "watchlisttools-edit": "檢視並編輯監視清單",
        "watchlisttools-raw": "編輯原始監視清單",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|對話]])",
+       "timezone-local": "當地",
        "duplicate-defaultsort": "<strong>警告:</strong>預設的排序鍵 \"$2\" 會覆蓋先前預設的排序鍵 \"$1\"。",
        "duplicate-displaytitle": "<strong>警告:</strong> 顯示標題 \"$2\" 覆蓋之前的顯示標題 \"$1\"。",
        "invalid-indicator-name": "<strong>錯誤:</strong>頁面狀態指示的 <code>name</code> 屬性不能為空。",
        "logentry-delete-restore": "$1 還原頁面 $3",
        "logentry-delete-event": "$1 {{GENDER:$2|已更改}} $3 中 {{PLURAL:$5|1 筆日誌|$5 筆日誌}}的可見性:$4",
        "logentry-delete-revision": "$1 {{GENDER:$2|已更改}}頁面 $3 中 {{PLURAL:$5|1 筆修訂|$5 筆修訂}}的可見性:$4",
-       "logentry-delete-event-legacy": "$1 {{GENDER:$2|已更改}} $3 中日誌的可見性",
+       "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-event": "$1 {{GENDER:$2|已暗中更改}} $3 中 {{PLURAL:$5|1 筆日誌|$5 筆日誌}}的可見性:$4",
        "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 已由其他 wiki {{GENDER:$2|匯入}} $3",
+       "logentry-import-interwiki-details": "$1 已自 $5 {{GENDER:$2|匯入}} $3 ($4 {{PLURAL:$4|修訂|修訂}})",
        "logentry-merge-merge": "$1 將 $3 {{GENDER:$2|合併}}至 $4 (修訂版本至 $5)",
        "logentry-move-move": "$1 {{GENDER:$2|已移動}}頁面 $3 至 $4",
        "logentry-move-move-noredirect": "$1 {{GENDER:$2|已移動}}頁面 $3 至 $4,不留重新導向",
        "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 位元組}} ($2; $3%)",
+       "mediastatistics-bytespertype": "此章節總檔案大小:$1 位元組 ($2; $3%)。",
+       "mediastatistics-allbytes": "所有檔案的總檔案大小:$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 個 JSON 結尾的{{PLURAL:$1|逗號}}",
        "json-error-unknown": "JSON 發生問題。錯誤:$1",
        "json-error-depth": "已超出堆疊深度限制",
        "mw-widgets-dateinput-no-date": "未選擇日期",
        "mw-widgets-titleinput-description-new-page": "頁面不存在",
        "mw-widgets-titleinput-description-redirect": "重新導向至 $1",
-       "api-error-blacklisted": "請選擇另一個更具描述性的標題。"
+       "api-error-blacklisted": "請選擇另一個更具描述性的標題。",
+       "sessionprovider-generic": "$1 連線階段",
+       "sessionprovider-mediawiki-session-cookiesessionprovider": "以 cookie 為基礎的連線階段",
+       "sessionprovider-nocookies": "Cookie 功能可能已被關閉,請確認您改開啟 Cookie 功能並重新啟動。",
+       "randomrootpage": "隨機根頁面"
 }
index 5441237..8afa04b 100644 (file)
@@ -380,6 +380,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'عشوائي', 'صفحة_عشوائية' ),
        'RandomInCategory'          => array( 'عشوائي_في_تصنيف' ),
        'Randomredirect'            => array( 'تحويلة_عشوائية' ),
+       'Randomrootpage'            => array( 'صفحة_جذر_عشوائية' ),
        'Recentchanges'             => array( 'أحدث_التغييرات' ),
        'Recentchangeslinked'       => array( 'أحدث_التغييرات_الموصولة', 'تغييرات_مرتبطة' ),
        'Redirect'                  => array( 'تحويل' ),
index e2078ad..99b7db5 100644 (file)
@@ -109,6 +109,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'عشوائى', 'صفحه_عشوائيه' ),
        'RandomInCategory'          => array( 'عشوائى_جوه_تصنيف' ),
        'Randomredirect'            => array( 'تحويله_عشوائيه' ),
+       'Randomrootpage'            => array( 'صفحه_جدر_عشوائيه' ),
        'Recentchanges'             => array( 'اخر_تعديلات' ),
        'Recentchangeslinked'       => array( 'اجدد_التغييرات_اللى_معمول_ليها_لينك', 'تغييرات_مرتبطه' ),
        'Redirect'                  => array( 'تحويل' ),
index 45a13bf..1f74047 100644 (file)
@@ -113,6 +113,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'تسادوپین_وّرق' ),
        'RandomInCategory'          => array( 'تسادوپی_بئ_تهری_تا' ),
        'Randomredirect'            => array( 'تسادوپین_تغیرمیسر' ),
+       'Randomrootpage'            => array( 'تسادوپین_پایگ_ئی_وّرق' ),
        'Recentchanges'             => array( 'آخیرئین_تغیران' ),
        'Recentchangeslinked'       => array( 'مربوتین_تغیران' ),
        'Redirect'                  => array( 'تغیرمسیر' ),
index b5c1475..c31d745 100644 (file)
@@ -112,6 +112,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Zufällige_Seite' ),
        'RandomInCategory'          => array( 'Zufällig_in_Kategorie' ),
        'Randomredirect'            => array( 'Zufällige_Weiterleitung' ),
+       'Randomrootpage'            => array( 'Zufällige_Stammseite' ),
        'Recentchanges'             => array( 'Letzte_Änderungen' ),
        'Recentchangeslinked'       => array( 'Änderungen_an_verlinkten_Seiten' ),
        'Redirect'                  => array( 'Weiterleitung' ),
index 6adf6f1..69ccb44 100644 (file)
@@ -125,6 +125,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Raştameye', 'PelaRaştameyiye' ),
        'RandomInCategory'          => array( 'KategoriyaRaştameyiye' ),
        'Randomredirect'            => array( 'SerberdışoRaştameye' ),
+       'Randomrootpage'            => array( 'RaştamayePerraréçi' ),
        'Recentchanges'             => array( 'VurnayışêPeyêni' ),
        'Recentchangeslinked'       => array( 'GıreyêVurnayışêPeyênan' ),
        'Redirect'                  => array( 'Serberdış' ),
index ec20601..2a1f85b 100644 (file)
@@ -395,6 +395,7 @@ $specialPageAliases = array(
        'Blankpage'                 => array( 'BlankPage' ),
        'Block'                     => array( 'Block', 'BlockIP', 'BlockUser' ),
        'Booksources'               => array( 'BookSources' ),
+       'BotPasswords'              => array( 'BotPasswords' ),
        'BrokenRedirects'           => array( 'BrokenRedirects' ),
        'Categories'                => array( 'Categories' ),
        'ChangeContentModel'        => array( 'ChangeContentModel' ),
@@ -425,6 +426,7 @@ $specialPageAliases = array(
        'Listbots'                  => array( 'ListBots' ),
        'Listfiles'                 => array( 'ListFiles', 'FileList', 'ImageList' ),
        'Listgrouprights'           => array( 'ListGroupRights', 'UserGroupRights' ),
+       'Listgrants'                => array( 'ListGrants' ),
        'Listredirects'             => array( 'ListRedirects' ),
        'ListDuplicatedFiles'       => array( 'ListDuplicatedFiles', 'ListFileDuplicates' ),
        'Listusers'                 => array( 'ListUsers', 'UserList' ),
@@ -461,6 +463,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Random', 'RandomPage' ),
        'RandomInCategory'          => array( 'RandomInCategory' ),
        'Randomredirect'            => array( 'RandomRedirect' ),
+       'Randomrootpage'            => array( 'RandomRootpage' ),
        'Recentchanges'             => array( 'RecentChanges' ),
        'Recentchangeslinked'       => array( 'RecentChangesLinked', 'RelatedChanges' ),
        'Redirect'                  => array( 'Redirect' ),
index b8ef360..912b33d 100644 (file)
@@ -101,6 +101,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Juhuslik_artikkel' ),
        'RandomInCategory'          => array( 'Juhuslik_kategoorias' ),
        'Randomredirect'            => array( 'Juhuslik_ümbersuunamine' ),
+       'Randomrootpage'            => array( 'Juhuslik_juurlehekülg' ),
        'Recentchanges'             => array( 'Viimased_muudatused' ),
        'Recentchangeslinked'       => array( 'Seotud_muudatused' ),
        'Redirect'                  => array( 'Ümbersuunamine' ),
index fa359fc..57fdc4e 100644 (file)
@@ -112,6 +112,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'صفحهٔ_تصادفی' ),
        'RandomInCategory'          => array( 'تصادفی_در_رده' ),
        'Randomredirect'            => array( 'تغییرمسیر_تصادفی' ),
+       'Randomrootpage'            => array( 'صفحهٔ_پایهٔ_تصادفی' ),
        'Recentchanges'             => array( 'تغییرات_اخیر' ),
        'Recentchangeslinked'       => array( 'تغییرات_مرتبط' ),
        'Redirect'                  => array( 'تغییرمسیر' ),
index c2e5433..48af44d 100644 (file)
@@ -12,3 +12,39 @@ $fallback = 'fa';
 
 $rtl = true;
 
+$namespaceNames = array(
+       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    => 'جرگه_گب',
+);
+
+$namespaceAliases = array(
+       // Aliases from old Persian (fa) namespace names
+       'ویژه' => NS_SPECIAL,
+       'بحث' => NS_TALK,
+       'کاربر' => NS_USER,
+       'بحث_کاربر' => NS_USER_TALK,
+       'بحث_$1' => NS_PROJECT_TALK,
+       'پرونده' => NS_FILE,
+       'بحث_پرونده' => NS_FILE_TALK,
+       'بحث_مدیاویکی' => NS_MEDIAWIKI_TALK,
+       'الگو' => NS_TEMPLATE,
+       'بحث_الگو' => NS_TEMPLATE_TALK,
+       'راهنما' => NS_HELP,
+       'بحث_راهنما' => NS_HELP_TALK,
+       'رده' => NS_CATEGORY,
+       'بحث_رده' => NS_CATEGORY_TALK,
+);
diff --git a/languages/messages/MessagesJbo.php b/languages/messages/MessagesJbo.php
new file mode 100644 (file)
index 0000000..6a3c573
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/** Lojban (lojban)
+ *
+ * To improve a translation please visit https://translatewiki.net
+ *
+ * @ingroup Language
+ * @file
+ *
+ */
+
+$namespaceNames = array(
+       NS_MEDIA            => 'velsku',
+       NS_SPECIAL          => 'rirci',
+       NS_TALK             => 'casnu',
+       NS_USER             => 'pilno',
+       NS_USER_TALK        => 'casnu_lo_pilno',
+       NS_PROJECT_TALK     => 'casnu_la_.$1.',
+       NS_FILE             => 'datnyvei',
+       NS_FILE_TALK        => 'casnu_lo_datnyvei',
+       NS_MEDIAWIKI        => 'medi\'auikis',
+       NS_MEDIAWIKI_TALK   => 'casnu_la_.medi\'auikis.',
+       NS_TEMPLATE         => 'termo\'a',
+       NS_TEMPLATE_TALK    => 'casnu_lo_termo\'a',
+       NS_HELP             => 'nundju',
+       NS_HELP_TALK        => 'casnu_lo_nundju',
+       NS_CATEGORY         => 'klesi',
+       NS_CATEGORY_TALK    => 'casnu_lo_klesi',
+);
index 0da2291..2099614 100644 (file)
@@ -146,6 +146,7 @@ $specialPageAliases = array(
        'Randompage'                => array( '임의문서' ),
        'RandomInCategory'          => array( '분류안의임의문서' ),
        'Randomredirect'            => array( '임의넘겨주기' ),
+       'Randomrootpage'            => array( '임의최상위문서', '임의루트문서' ),
        'Recentchanges'             => array( '최근바뀜' ),
        'Recentchangeslinked'       => array( '링크최근바뀜' ),
        'Redirect'                  => array( '넘겨주기' ),
index 6408c4d..2edd444 100644 (file)
@@ -58,7 +58,7 @@ $namespaceAliases = array(
        'Lietotāja_diskusija' => NS_USER_TALK,
 );
 
-$wgExtraGenderNamespaces = array(
+$namespaceGenderAliases = array(
        NS_USER => array( 'male' => 'Dalībnieks', 'female' => 'Dalībniece' ),
        NS_USER_TALK => array( 'male' => 'Dalībnieka_diskusija', 'female' => 'Dalībnieces_diskusija' )
 );
index 3c7b31d..d94518d 100644 (file)
@@ -166,6 +166,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Случајна', 'СлучајнаСтраница' ),
        'RandomInCategory'          => array( 'СлучајнаВоКатегорија' ),
        'Randomredirect'            => array( 'СлучајноПренасочување' ),
+       'Randomrootpage'            => array( 'СлучајнаОсновнаСтраница' ),
        'Recentchanges'             => array( 'СкорешниПромени' ),
        'Recentchangeslinked'       => array( 'ПоврзаниПромени' ),
        'Redirect'                  => array( 'Пренасочување' ),
index 27af255..9ac713e 100644 (file)
@@ -342,6 +342,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Willekeurig', 'WillekeurigePagina' ),
        'RandomInCategory'          => array( 'WillekeurigeUitCategorie' ),
        'Randomredirect'            => array( 'WillekeurigeDoorverwijzing' ),
+       'Randomrootpage'            => array( 'WillekeurigeHoofdpagina' ),
        'Recentchanges'             => array( 'RecenteWijzigingen' ),
        'Recentchangeslinked'       => array( 'RecenteWijzigingenGelinkt', 'VerwanteWijzigingen' ),
        'Redirect'                  => array( 'Doorverwijzen' ),
index 56ee88c..94743c9 100644 (file)
@@ -132,6 +132,7 @@ $specialPageAliases = array(
        'Randompage'                => array( 'Ngẫu_nhiên' ),
        'RandomInCategory'          => array( 'Ngẫu_nhiên_trong_thể_loại' ),
        'Randomredirect'            => array( 'Đổi_hướng_ngẫu_nhiên' ),
+       'Randomrootpage'            => array( 'Trang_gốc_ngẫu_nhiên' ),
        'Recentchanges'             => array( 'Thay_đổi_gần_đây' ),
        'Recentchangeslinked'       => array( 'Thay_đổi_liên_quan' ),
        'Redirect'                  => array( 'Đổi_hướng' ),
index 57c9a61..33af7af 100644 (file)
@@ -206,6 +206,7 @@ $specialPageAliases = array(
        'Randompage'                => array( '随机', '随机页面' ),
        'RandomInCategory'          => array( '分类内随机' ),
        'Randomredirect'            => array( '随机重定向', '随机重定向页' ),
+       'Randomrootpage'            => array( '随机根页面' ),
        'Recentchanges'             => array( '最近更改' ),
        'Recentchangeslinked'       => array( '最近链出更改', '相关更改' ),
        'Redirect'                  => array( '重定向' ),
index 1f6bcfa..20fd54a 100644 (file)
@@ -190,6 +190,7 @@ $specialPageAliases = array(
        'Randompage'                => array( '隨機頁面' ),
        'RandomInCategory'          => array( '隨機分類頁面', '於分類中隨機' ),
        'Randomredirect'            => array( '隨機重新導向', '隨機重定向頁面' ),
+       'Randomrootpage'            => array( '隨機根頁面' ),
        'Recentchanges'             => array( '最近變更', '最近更改' ),
        'Recentchangeslinked'       => array( '已連結的最近變更', '相關變更', '連出更改' ),
        'Redirect'                  => array( '重新導向', '重定向' ),
index 43c2faf..1d6a242 100644 (file)
--- a/load.php
+++ b/load.php
@@ -26,7 +26,6 @@ use MediaWiki\Logger\LoggerFactory;
 
 require __DIR__ . '/includes/WebStart.php';
 
-
 // URL safety checks
 if ( !$wgRequest->checkUrlExtension() ) {
        return;
index 7825ce9..291920b 100644 (file)
@@ -105,10 +105,13 @@ abstract class Maintenance {
 
        /**
         * Used by getDB() / setDB()
-        * @var DatabaseBase
+        * @var IDatabase
         */
        private $mDb = null;
 
+       /** @var float UNIX timestamp */
+       private $lastSlaveWait = 0.0;
+
        /**
         * Used when creating separate schema files.
         * @var resource
@@ -122,6 +125,19 @@ abstract class Maintenance {
         */
        private $config;
 
+       /**
+        * Used to read the options in the order they were passed.
+        * Useful for option chaining (Ex. dumpBackup.php). It will
+        * be an empty array if the options are passed in through
+        * loadParamsAndArgs( $self, $opts, $args ).
+        *
+        * This is an array of arrays where
+        * 0 => the option and 1 => parameter value.
+        *
+        * @var array
+        */
+       public $orderedOptions = array();
+
        /**
         * Default constructor. Children should call this *first* if implementing
         * their own constructors
@@ -184,15 +200,17 @@ abstract class Maintenance {
         * @param bool $required Is the param required?
         * @param bool $withArg Is an argument required with this option?
         * @param string $shortName Character to use as short name
+        * @param bool $multiOccurrence Can this option be passed multiple times?
         */
        protected function addOption( $name, $description, $required = false,
-               $withArg = false, $shortName = false
+               $withArg = false, $shortName = false, $multiOccurrence = false
        ) {
                $this->mParams[$name] = array(
                        'desc' => $description,
                        'require' => $required,
                        'withArg' => $withArg,
-                       'shortName' => $shortName
+                       'shortName' => $shortName,
+                       'multiOccurrence' => $multiOccurrence
                );
 
                if ( $shortName !== false ) {
@@ -210,7 +228,11 @@ abstract class Maintenance {
        }
 
        /**
-        * Get an option, or return the default
+        * Get an option, or return the default.
+        *
+        * If the option was added to support multiple occurrences,
+        * this will return an array.
+        *
         * @param string $name The name of the param
         * @param mixed $default Anything you want, default null
         * @return mixed
@@ -636,43 +658,16 @@ abstract class Maintenance {
        }
 
        /**
-        * Process command line arguments
-        * $mOptions becomes an array with keys set to the option names
-        * $mArgs becomes a zero-based array containing the non-option arguments
+        * Load params and arguments from a given array
+        * of command-line arguments
         *
-        * @param string $self The name of the script, if any
-        * @param array $opts An array of options, in form of key=>value
-        * @param array $args An array of command line arguments
+        * @since 1.27
+        * @param array $argv
         */
-       public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
-               # If we were given opts or args, set those and return early
-               if ( $self ) {
-                       $this->mSelf = $self;
-                       $this->mInputLoaded = true;
-               }
-               if ( $opts ) {
-                       $this->mOptions = $opts;
-                       $this->mInputLoaded = true;
-               }
-               if ( $args ) {
-                       $this->mArgs = $args;
-                       $this->mInputLoaded = true;
-               }
-
-               # If we've already loaded input (either by user values or from $argv)
-               # skip on loading it again. The array_shift() will corrupt values if
-               # it's run again and again
-               if ( $this->mInputLoaded ) {
-                       $this->loadSpecialVars();
-
-                       return;
-               }
-
-               global $argv;
-               $this->mSelf = array_shift( $argv );
-
+       public function loadWithArgv( $argv ) {
                $options = array();
                $args = array();
+               $this->orderedOptions = array();
 
                # Parse arguments
                for ( $arg = reset( $argv ); $arg !== false; $arg = next( $argv ) ) {
@@ -687,17 +682,14 @@ abstract class Maintenance {
                        } elseif ( substr( $arg, 0, 2 ) == '--' ) {
                                # Long options
                                $option = substr( $arg, 2 );
-                               if ( array_key_exists( $option, $options ) ) {
-                                       $this->error( "\nERROR: $option parameter given twice\n" );
-                                       $this->maybeHelp( true );
-                               }
                                if ( isset( $this->mParams[$option] ) && $this->mParams[$option]['withArg'] ) {
                                        $param = next( $argv );
                                        if ( $param === false ) {
                                                $this->error( "\nERROR: $option parameter needs a value after it\n" );
                                                $this->maybeHelp( true );
                                        }
-                                       $options[$option] = $param;
+
+                                       $this->setParam( $options, $option, $param );
                                } else {
                                        $bits = explode( '=', $option, 2 );
                                        if ( count( $bits ) > 1 ) {
@@ -706,7 +698,8 @@ abstract class Maintenance {
                                        } else {
                                                $param = 1;
                                        }
-                                       $options[$option] = $param;
+
+                                       $this->setParam( $options, $option, $param );
                                }
                        } elseif ( $arg == '-' ) {
                                # Lonely "-", often used to indicate stdin or stdout.
@@ -719,19 +712,16 @@ abstract class Maintenance {
                                        if ( !isset( $this->mParams[$option] ) && isset( $this->mShortParamsMap[$option] ) ) {
                                                $option = $this->mShortParamsMap[$option];
                                        }
-                                       if ( array_key_exists( $option, $options ) ) {
-                                               $this->error( "\nERROR: $option parameter given twice\n" );
-                                               $this->maybeHelp( true );
-                                       }
+
                                        if ( isset( $this->mParams[$option]['withArg'] ) && $this->mParams[$option]['withArg'] ) {
                                                $param = next( $argv );
                                                if ( $param === false ) {
                                                        $this->error( "\nERROR: $option parameter needs a value after it\n" );
                                                        $this->maybeHelp( true );
                                                }
-                                               $options[$option] = $param;
+                                               $this->setParam( $options, $option, $param );
                                        } else {
-                                               $options[$option] = 1;
+                                               $this->setParam( $options, $option, 1 );
                                        }
                                }
                        } else {
@@ -745,6 +735,75 @@ abstract class Maintenance {
                $this->mInputLoaded = true;
        }
 
+       /**
+        * Helper function used solely by loadParamsAndArgs
+        * to prevent code duplication
+        *
+        * This sets the param in the options array based on
+        * whether or not it can be specified multiple times.
+        *
+        * @since 1.27
+        * @param array $options
+        * @param string $option
+        * @param mixed $value
+        */
+       private function setParam( &$options, $option, $value ) {
+               $this->orderedOptions[] = array( $option, $value );
+
+               if ( isset( $this->mParams[$option] ) ) {
+                       $multi = $this->mParams[$option]['multiOccurrence'];
+                       $exists = array_key_exists( $option, $options );
+                       if ( $multi && $exists ) {
+                               $options[$option][] = $value;
+                       } elseif ( $multi ) {
+                               $options[$option] = array( $value );
+                       } elseif ( !$exists ) {
+                               $options[$option] = $value;
+                       } else {
+                               $this->error( "\nERROR: $option parameter given twice\n" );
+                               $this->maybeHelp( true );
+                       }
+               }
+       }
+
+       /**
+        * Process command line arguments
+        * $mOptions becomes an array with keys set to the option names
+        * $mArgs becomes a zero-based array containing the non-option arguments
+        *
+        * @param string $self The name of the script, if any
+        * @param array $opts An array of options, in form of key=>value
+        * @param array $args An array of command line arguments
+        */
+       public function loadParamsAndArgs( $self = null, $opts = null, $args = null ) {
+               # If we were given opts or args, set those and return early
+               if ( $self ) {
+                       $this->mSelf = $self;
+                       $this->mInputLoaded = true;
+               }
+               if ( $opts ) {
+                       $this->mOptions = $opts;
+                       $this->mInputLoaded = true;
+               }
+               if ( $args ) {
+                       $this->mArgs = $args;
+                       $this->mInputLoaded = true;
+               }
+
+               # If we've already loaded input (either by user values or from $argv)
+               # skip on loading it again. The array_shift() will corrupt values if
+               # it's run again and again
+               if ( $this->mInputLoaded ) {
+                       $this->loadSpecialVars();
+
+                       return;
+               }
+
+               global $argv;
+               $this->mSelf = $argv[0];
+               $this->loadWithArgv( array_slice( $argv, 1 ) );
+       }
+
        /**
         * Run some validation checks on the params, etc
         */
@@ -1026,7 +1085,7 @@ abstract class Maintenance {
        public function purgeRedundantText( $delete = true ) {
                # Data should come off the master, wrapped in a transaction
                $dbw = $this->getDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                # Get "active" text records from the revisions table
                $this->output( 'Searching for active text records in revisions table...' );
@@ -1069,7 +1128,7 @@ abstract class Maintenance {
                }
 
                # Done
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
        }
 
        /**
@@ -1085,7 +1144,10 @@ abstract class Maintenance {
         * If not set, wfGetDB() will be used.
         * This function has the same parameters as wfGetDB()
         *
-        * @return DatabaseBase
+        * @param integer $db DB index (DB_SLAVE/DB_MASTER)
+        * @param array $groups; default: empty array
+        * @param string|bool $wiki; default: current wiki
+        * @return IDatabase
         */
        protected function getDB( $db, $groups = array(), $wiki = false ) {
                if ( is_null( $this->mDb ) ) {
@@ -1098,12 +1160,60 @@ abstract class Maintenance {
        /**
         * Sets database object to be returned by getDB().
         *
-        * @param DatabaseBase $db Database object to be used
+        * @param IDatabase $db Database object to be used
         */
-       public function setDB( $db ) {
+       public function setDB( IDatabase $db ) {
                $this->mDb = $db;
        }
 
+       /**
+        * Begin a transcation on a DB
+        *
+        * This method makes it clear that begin() is called from a maintenance script,
+        * which has outermost scope. This is safe, unlike $dbw->begin() called in other places.
+        *
+        * @param IDatabase $dbw
+        * @param string $fname Caller name
+        * @since 1.27
+        */
+       protected function beginTransaction( IDatabase $dbw, $fname ) {
+               $dbw->begin( $fname );
+       }
+
+       /**
+        * Commit the transcation on a DB handle and wait for slaves 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
+        * @since 1.27
+        */
+       protected function commitTransaction( IDatabase $dbw, $fname ) {
+               $dbw->commit( $fname );
+
+               $ok = wfWaitForSlaves( $this->lastSlaveWait, false, '*', 30 );
+               $this->lastSlaveWait = microtime( true );
+
+               return $ok;
+       }
+
+       /**
+        * Rollback the transcation on a DB handle
+        *
+        * This method makes it clear that rollback() is called from a maintenance script,
+        * which has outermost scope. This is safe, unlike $dbw->rollback() called in other places.
+        *
+        * @param IDatabase $dbw
+        * @param string $fname Caller name
+        * @since 1.27
+        */
+       protected function rollbackTransaction( IDatabase $dbw, $fname ) {
+               $dbw->rollback( $fname );
+       }
+
        /**
         * Lock the search index
         * @param DatabaseBase &$db
diff --git a/maintenance/archives/patch-bot_passwords.sql b/maintenance/archives/patch-bot_passwords.sql
new file mode 100644 (file)
index 0000000..bd60ff7
--- /dev/null
@@ -0,0 +1,25 @@
+--
+-- This table contains a user's bot passwords: passwords that allow access to
+-- the account via the API with limited rights.
+--
+CREATE TABLE /*_*/bot_passwords (
+  -- Foreign key to user.user_id
+  bp_user int NOT NULL,
+
+  -- Application identifier
+  bp_app_id varbinary(32) NOT NULL,
+
+  -- Password hashes, like user.user_password
+  bp_password tinyblob NOT NULL,
+
+  -- Like user.user_token
+  bp_token binary(32) NOT NULL default '',
+
+  -- JSON blob for MWRestrictions
+  bp_restrictions blob NOT NULL,
+
+  -- Grants allowed to the account when authenticated with this bot-password
+  bp_grants blob NOT NULL,
+
+  PRIMARY KEY ( bp_user, bp_app_id )
+) /*$wgDBTableOptions*/;
index aeadc93..d5b98b5 100644 (file)
@@ -39,7 +39,7 @@ class UpdateLogging {
        public $minTs = false;
 
        function execute() {
-               $this->dbw = wfGetDB( DB_MASTER );
+               $this->dbw = $this->getDB( DB_MASTER );
                $logging = $this->dbw->tableName( 'logging' );
                $logging_1_10 = $this->dbw->tableName( 'logging_1_10' );
                $logging_pre_1_10 = $this->dbw->tableName( 'logging_pre_1_10' );
index fba6b92..a2ea554 100644 (file)
@@ -43,7 +43,7 @@ class AttachLatest extends Maintenance {
 
        public function execute() {
                $this->output( "Looking for pages with page_latest set to 0...\n" );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $conds = array( 'page_latest' => 0 );
                if ( $this->hasOption( 'regenerate-all' ) ) {
                        $conds = '';
index 6e1ddb4..9af9604 100644 (file)
  * @ingroup Dump Maintenance
  */
 
-/**
- * @ingroup Dump Maintenance
- */
-class DumpDBZip2Output extends DumpPipeOutput {
-       function __construct( $file ) {
-               parent::__construct( "dbzip2", $file );
-       }
-}
+require_once __DIR__ . '/Maintenance.php';
+require_once __DIR__ . '/../includes/export/DumpFilter.php';
 
 /**
  * @ingroup Dump Maintenance
  */
-class BackupDumper {
+class BackupDumper extends Maintenance {
        public $reporting = true;
        public $pages = null; // all pages
        public $skipHeader = false; // don't output <mediawiki> and <siteinfo>
@@ -67,7 +61,7 @@ class BackupDumper {
         *
         * @var DatabaseBase|null
         *
-        * @see self::setDb
+        * @see self::setDB
         */
        protected $forcedDb = null;
 
@@ -77,7 +71,11 @@ class BackupDumper {
        // @todo Unused?
        private $stubText = false; // include rev_text_id instead of text; for 2-pass dump
 
-       function __construct( $args ) {
+       /**
+        * @param array $args For backward compatibility
+        */
+       function __construct( $args = null ) {
+               parent::__construct();
                $this->stderr = fopen( "php://stderr", "wt" );
 
                // Built-in output and filter plugins
@@ -91,7 +89,25 @@ class BackupDumper {
                $this->registerFilter( 'notalk', 'DumpNotalkFilter' );
                $this->registerFilter( 'namespace', 'DumpNamespaceFilter' );
 
-               $this->sink = $this->processArgs( $args );
+               // These three can be specified multiple times
+               $this->addOption( 'plugin', 'Load a dump plugin class. Specify as <class>[:<file>].',
+                       false, true, false, true );
+               $this->addOption( 'output', 'Begin a filtered output stream; Specify as <type>:<file>. ' .
+                       '<type>s: file, gzip, bzip2, 7zip, dbzip2', false, true, false, true );
+               $this->addOption( 'filter', 'Add a filter on an output branch. Specify as ' .
+                       '<type>[:<options>]. <types>s: latest, notalk, namespace', false, true, false, true );
+               $this->addOption( 'report', 'Report position and speed after every n pages processed. ' .
+                       'Default: 100.', false, true );
+               $this->addOption( 'server', 'Force reading from MySQL server', false, true );
+               $this->addOption( '7ziplevel', '7zip compression level for all 7zip outputs. Used for ' .
+                       '-mx option to 7za command.', false, true );
+
+               if ( $args ) {
+                       // Args should be loaded and processed so that dump() can be called directly
+                       // instead of execute()
+                       $this->loadWithArgv( $args );
+                       $this->processOptions();
+               }
        }
 
        /**
@@ -125,83 +141,106 @@ class BackupDumper {
                call_user_func_array( $register, array( &$this ) );
        }
 
+       function execute() {
+               throw new MWException( 'execute() must be overridden in subclasses' );
+       }
+
        /**
-        * @param array $args
-        * @return array
+        * Processes arguments and sets $this->$sink accordingly
         */
-       function processArgs( $args ) {
+       function processOptions() {
                $sink = null;
                $sinks = array();
-               foreach ( $args as $arg ) {
-                       $matches = array();
-                       if ( preg_match( '/^--(.+?)(?:=(.+?)(?::(.+?))?)?$/', $arg, $matches ) ) {
-                               MediaWiki\suppressWarnings();
-                               list( /* $full */, $opt, $val, $param ) = $matches;
-                               MediaWiki\restoreWarnings();
-
-                               switch ( $opt ) {
-                                       case "plugin":
-                                               $this->loadPlugin( $val, $param );
-                                               break;
-                                       case "output":
-                                               if ( !is_null( $sink ) ) {
-                                                       $sinks[] = $sink;
-                                               }
-                                               if ( !isset( $this->outputTypes[$val] ) ) {
-                                                       $this->fatalError( "Unrecognized output sink type '$val'" );
-                                               }
-                                               $type = $this->outputTypes[$val];
-                                               $sink = new $type( $param );
-                                               break;
-                                       case "filter":
-                                               if ( is_null( $sink ) ) {
-                                                       $sink = new DumpOutput();
-                                               }
-                                               if ( !isset( $this->filterTypes[$val] ) ) {
-                                                       $this->fatalError( "Unrecognized filter type '$val'" );
-                                               }
-                                               $type = $this->filterTypes[$val];
-                                               $filter = new $type( $sink, $param );
-
-                                               // references are lame in php...
-                                               unset( $sink );
-                                               $sink = $filter;
-
-                                               break;
-                                       case "report":
-                                               $this->reportingInterval = intval( $val );
-                                               break;
-                                       case "server":
-                                               $this->server = $val;
-                                               break;
-                                       case "force-normal":
-                                               if ( !function_exists( 'utf8_normalize' ) ) {
-                                                       $this->fatalError( "UTF-8 normalization extension not loaded. " .
-                                                               "Install or remove --force-normal parameter to use slower code." );
-                                               }
-                                               break;
-                                       default:
-                                               $this->processOption( $opt, $val, $param );
-                               }
+
+               $options = $this->orderedOptions;
+               foreach ( $options as $arg ) {
+                       $opt = $arg[0];
+                       $param = $arg[1];
+
+                       switch ( $opt ) {
+                               case 'plugin':
+                                       $val = explode( ':', $param );
+
+                                       if ( count( $val ) === 1 ) {
+                                               $this->loadPlugin( $val[0] );
+                                       } elseif ( count( $val ) === 2 ) {
+                                               $this->loadPlugin( $val[0], $val[1] );
+                                       } else {
+                                               $this->fatalError( 'Invalid plugin parameter' );
+                                               return;
+                                       }
+
+                                       break;
+                               case 'output':
+                                       $split = explode( ':', $param, 2 );
+                                       if ( count( $split ) !== 2 ) {
+                                               $this->fatalError( 'Invalid output parameter' );
+                                       }
+                                       list( $type, $file ) = $split;
+                                       if ( !is_null( $sink ) ) {
+                                               $sinks[] = $sink;
+                                       }
+                                       if ( !isset( $this->outputTypes[$type] ) ) {
+                                               $this->fatalError( "Unrecognized output sink type '$type'" );
+                                       }
+                                       $class = $this->outputTypes[$type];
+                                       if ( $type === "7zip" ) {
+                                               $sink = new $class( $file, intval( $this->getOption( '7ziplevel' ) ) );
+                                       } else {
+                                               $sink = new $class( $file );
+                                       }
+
+                                       break;
+                               case 'filter':
+                                       if ( is_null( $sink ) ) {
+                                               $sink = new DumpOutput();
+                                       }
+
+                                       $split = explode( ':', $param );
+                                       $key = $split[0];
+
+                                       if ( !isset( $this->filterTypes[$key] ) ) {
+                                               $this->fatalError( "Unrecognized filter type '$key'" );
+                                       }
+
+                                       $type = $this->filterTypes[$key];
+
+                                       if ( count( $split ) === 1 ) {
+                                               $filter = new $type( $sink );
+                                       } elseif ( count( $split ) === 2 ) {
+                                               $filter = new $type( $sink, $split[1] );
+                                       } else {
+                                               $this->fatalError( 'Invalid filter parameter' );
+                                       }
+
+                                       // references are lame in php...
+                                       unset( $sink );
+                                       $sink = $filter;
+
+                                       break;
                        }
                }
 
+               if ( $this->hasOption( 'report' ) ) {
+                       $this->reportingInterval = intval( $this->getOption( 'report' ) );
+               }
+
+               if ( $this->hasOption( 'server' ) ) {
+                       $this->server = $this->getOption( 'server' );
+               }
+
                if ( is_null( $sink ) ) {
                        $sink = new DumpOutput();
                }
                $sinks[] = $sink;
 
                if ( count( $sinks ) > 1 ) {
-                       return new DumpMultiWriter( $sinks );
+                       $this->sink = new DumpMultiWriter( $sinks );
                } else {
-                       return $sink;
+                       $this->sink = $sink;
                }
        }
 
-       function processOption( $opt, $val, $param ) {
-               // extension point for subclasses to add options
-       }
-
        function dump( $history, $text = WikiExporter::TEXT ) {
                # Notice messages will foul up your XML output even if they're
                # relatively harmless.
@@ -298,7 +337,8 @@ class BackupDumper {
         * @param DatabaseBase|null $db (Optional) the database connection to use. If null, resort to
         *   use the globally provided ways to get database connections.
         */
-       function setDb( DatabaseBase $db = null ) {
+       function setDB( IDatabase $db = null ) {
+               parent::setDB( $db );
                $this->forcedDb = $db;
        }
 
@@ -371,12 +411,13 @@ class BackupDumper {
        }
 
        function progress( $string ) {
-               fwrite( $this->stderr, $string . "\n" );
+               if ( $this->reporting ) {
+                       fwrite( $this->stderr, $string . "\n" );
+               }
        }
 
        function fatalError( $msg ) {
-               $this->progress( "$msg\n" );
-               die( 1 );
+               $this->error( "$msg\n", 1 );
        }
 }
 
diff --git a/maintenance/backupTextPass.inc b/maintenance/backupTextPass.inc
deleted file mode 100644 (file)
index 0562333..0000000
+++ /dev/null
@@ -1,925 +0,0 @@
-<?php
-/**
- * BackupDumper that postprocesses XML dumps from dumpBackup.php to add page text
- *
- * Copyright (C) 2005 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 Maintenance
- */
-
-require_once __DIR__ . '/backup.inc';
-
-/**
- * @ingroup Maintenance
- */
-class TextPassDumper extends BackupDumper {
-       public $prefetch = null;
-
-       // when we spend more than maxTimeAllowed seconds on this run, we continue
-       // processing until we write out the next complete page, then save output file(s),
-       // rename it/them and open new one(s)
-       public $maxTimeAllowed = 0; // 0 = no limit
-
-       protected $input = "php://stdin";
-       protected $history = WikiExporter::FULL;
-       protected $fetchCount = 0;
-       protected $prefetchCount = 0;
-       protected $prefetchCountLast = 0;
-       protected $fetchCountLast = 0;
-
-       protected $maxFailures = 5;
-       protected $maxConsecutiveFailedTextRetrievals = 200;
-       protected $failureTimeout = 5; // Seconds to sleep after db failure
-
-       protected $bufferSize = 524288; // In bytes. Maximum size to read from the stub in on go.
-
-       protected $php = "php";
-       protected $spawn = false;
-
-       /**
-        * @var bool|resource
-        */
-       protected $spawnProc = false;
-
-       /**
-        * @var bool|resource
-        */
-       protected $spawnWrite = false;
-
-       /**
-        * @var bool|resource
-        */
-       protected $spawnRead = false;
-
-       /**
-        * @var bool|resource
-        */
-       protected $spawnErr = false;
-
-       protected $xmlwriterobj = false;
-
-       protected $timeExceeded = false;
-       protected $firstPageWritten = false;
-       protected $lastPageWritten = false;
-       protected $checkpointJustWritten = false;
-       protected $checkpointFiles = array();
-
-       /**
-        * @var DatabaseBase
-        */
-       protected $db;
-
-       /**
-        * Drop the database connection $this->db and try to get a new one.
-        *
-        * This function tries to get a /different/ connection if this is
-        * possible. Hence, (if this is possible) it switches to a different
-        * failover upon each call.
-        *
-        * This function resets $this->lb and closes all connections on it.
-        *
-        * @throws MWException
-        */
-       function rotateDb() {
-               // Cleaning up old connections
-               if ( isset( $this->lb ) ) {
-                       $this->lb->closeAll();
-                       unset( $this->lb );
-               }
-
-               if ( $this->forcedDb !== null ) {
-                       $this->db = $this->forcedDb;
-
-                       return;
-               }
-
-               if ( isset( $this->db ) && $this->db->isOpen() ) {
-                       throw new MWException( 'DB is set and has not been closed by the Load Balancer' );
-               }
-
-               unset( $this->db );
-
-               // Trying to set up new connection.
-               // We do /not/ retry upon failure, but delegate to encapsulating logic, to avoid
-               // individually retrying at different layers of code.
-
-               // 1. The LoadBalancer.
-               try {
-                       $this->lb = wfGetLBFactory()->newMainLB();
-               } catch ( Exception $e ) {
-                       throw new MWException( __METHOD__
-                               . " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" );
-               }
-
-               // 2. The Connection, through the load balancer.
-               try {
-                       $this->db = $this->lb->getConnection( DB_SLAVE, 'dump' );
-               } catch ( Exception $e ) {
-                       throw new MWException( __METHOD__
-                               . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
-               }
-       }
-
-       function initProgress( $history = WikiExporter::FULL ) {
-               parent::initProgress();
-               $this->timeOfCheckpoint = $this->startTime;
-       }
-
-       function dump( $history, $text = WikiExporter::TEXT ) {
-               // Notice messages will foul up your XML output even if they're
-               // relatively harmless.
-               if ( ini_get( 'display_errors' ) ) {
-                       ini_set( 'display_errors', 'stderr' );
-               }
-
-               $this->initProgress( $this->history );
-
-               // We are trying to get an initial database connection to avoid that the
-               // first try of this request's first call to getText fails. However, if
-               // obtaining a good DB connection fails it's not a serious issue, as
-               // getText does retry upon failure and can start without having a working
-               // DB connection.
-               try {
-                       $this->rotateDb();
-               } catch ( Exception $e ) {
-                       // We do not even count this as failure. Just let eventual
-                       // watchdogs know.
-                       $this->progress( "Getting initial DB connection failed (" .
-                               $e->getMessage() . ")" );
-               }
-
-               $this->egress = new ExportProgressFilter( $this->sink, $this );
-
-               // it would be nice to do it in the constructor, oh well. need egress set
-               $this->finalOptionCheck();
-
-               // we only want this so we know how to close a stream :-P
-               $this->xmlwriterobj = new XmlDumpWriter();
-
-               $input = fopen( $this->input, "rt" );
-               $this->readDump( $input );
-
-               if ( $this->spawnProc ) {
-                       $this->closeSpawn();
-               }
-
-               $this->report( true );
-       }
-
-       function processOption( $opt, $val, $param ) {
-               global $IP;
-               $url = $this->processFileOpt( $val, $param );
-
-               switch ( $opt ) {
-                       case 'buffersize':
-                               // Lower bound for xml reading buffer size is 4 KB
-                               $this->bufferSize = max( intval( $val ), 4 * 1024 );
-                               break;
-                       case 'prefetch':
-                               require_once "$IP/maintenance/backupPrefetch.inc";
-                               $this->prefetch = new BaseDump( $url );
-                               break;
-                       case 'stub':
-                               $this->input = $url;
-                               break;
-                       case 'maxtime':
-                               $this->maxTimeAllowed = intval( $val ) * 60;
-                               break;
-                       case 'checkpointfile':
-                               $this->checkpointFiles[] = $val;
-                               break;
-                       case 'current':
-                               $this->history = WikiExporter::CURRENT;
-                               break;
-                       case 'full':
-                               $this->history = WikiExporter::FULL;
-                               break;
-                       case 'spawn':
-                               $this->spawn = true;
-                               if ( $val ) {
-                                       $this->php = $val;
-                               }
-                               break;
-               }
-       }
-
-       function processFileOpt( $val, $param ) {
-               $fileURIs = explode( ';', $param );
-               foreach ( $fileURIs as $URI ) {
-                       switch ( $val ) {
-                               case "file":
-                                       $newURI = $URI;
-                                       break;
-                               case "gzip":
-                                       $newURI = "compress.zlib://$URI";
-                                       break;
-                               case "bzip2":
-                                       $newURI = "compress.bzip2://$URI";
-                                       break;
-                               case "7zip":
-                                       $newURI = "mediawiki.compress.7z://$URI";
-                                       break;
-                               default:
-                                       $newURI = $URI;
-                       }
-                       $newFileURIs[] = $newURI;
-               }
-               $val = implode( ';', $newFileURIs );
-
-               return $val;
-       }
-
-       /**
-        * Overridden to include prefetch ratio if enabled.
-        */
-       function showReport() {
-               if ( !$this->prefetch ) {
-                       parent::showReport();
-
-                       return;
-               }
-
-               if ( $this->reporting ) {
-                       $now = wfTimestamp( TS_DB );
-                       $nowts = microtime( true );
-                       $deltaAll = $nowts - $this->startTime;
-                       $deltaPart = $nowts - $this->lastTime;
-                       $this->pageCountPart = $this->pageCount - $this->pageCountLast;
-                       $this->revCountPart = $this->revCount - $this->revCountLast;
-
-                       if ( $deltaAll ) {
-                               $portion = $this->revCount / $this->maxCount;
-                               $eta = $this->startTime + $deltaAll / $portion;
-                               $etats = wfTimestamp( TS_DB, intval( $eta ) );
-                               if ( $this->fetchCount ) {
-                                       $fetchRate = 100.0 * $this->prefetchCount / $this->fetchCount;
-                               } else {
-                                       $fetchRate = '-';
-                               }
-                               $pageRate = $this->pageCount / $deltaAll;
-                               $revRate = $this->revCount / $deltaAll;
-                       } else {
-                               $pageRate = '-';
-                               $revRate = '-';
-                               $etats = '-';
-                               $fetchRate = '-';
-                       }
-                       if ( $deltaPart ) {
-                               if ( $this->fetchCountLast ) {
-                                       $fetchRatePart = 100.0 * $this->prefetchCountLast / $this->fetchCountLast;
-                               } else {
-                                       $fetchRatePart = '-';
-                               }
-                               $pageRatePart = $this->pageCountPart / $deltaPart;
-                               $revRatePart = $this->revCountPart / $deltaPart;
-                       } else {
-                               $fetchRatePart = '-';
-                               $pageRatePart = '-';
-                               $revRatePart = '-';
-                       }
-                       $this->progress( sprintf(
-                               "%s: %s (ID %d) %d pages (%0.1f|%0.1f/sec all|curr), "
-                                       . "%d revs (%0.1f|%0.1f/sec all|curr), %0.1f%%|%0.1f%% "
-                                       . "prefetched (all|curr), ETA %s [max %d]",
-                               $now, wfWikiID(), $this->ID, $this->pageCount, $pageRate,
-                               $pageRatePart, $this->revCount, $revRate, $revRatePart,
-                               $fetchRate, $fetchRatePart, $etats, $this->maxCount
-                       ) );
-                       $this->lastTime = $nowts;
-                       $this->revCountLast = $this->revCount;
-                       $this->prefetchCountLast = $this->prefetchCount;
-                       $this->fetchCountLast = $this->fetchCount;
-               }
-       }
-
-       function setTimeExceeded() {
-               $this->timeExceeded = true;
-       }
-
-       function checkIfTimeExceeded() {
-               if ( $this->maxTimeAllowed
-                       && ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed )
-               ) {
-                       return true;
-               }
-
-               return false;
-       }
-
-       function finalOptionCheck() {
-               if ( ( $this->checkpointFiles && !$this->maxTimeAllowed )
-                       || ( $this->maxTimeAllowed && !$this->checkpointFiles )
-               ) {
-                       throw new MWException( "Options checkpointfile and maxtime must be specified together.\n" );
-               }
-               foreach ( $this->checkpointFiles as $checkpointFile ) {
-                       $count = substr_count( $checkpointFile, "%s" );
-                       if ( $count != 2 ) {
-                               throw new MWException( "Option checkpointfile must contain two '%s' "
-                                       . "for substitution of first and last pageids, count is $count instead, "
-                                       . "file is $checkpointFile.\n" );
-                       }
-               }
-
-               if ( $this->checkpointFiles ) {
-                       $filenameList = (array)$this->egress->getFilenames();
-                       if ( count( $filenameList ) != count( $this->checkpointFiles ) ) {
-                               throw new MWException( "One checkpointfile must be specified "
-                                       . "for each output option, if maxtime is used.\n" );
-                       }
-               }
-       }
-
-       /**
-        * @throws MWException Failure to parse XML input
-        * @param string $input
-        * @return bool
-        */
-       function readDump( $input ) {
-               $this->buffer = "";
-               $this->openElement = false;
-               $this->atStart = true;
-               $this->state = "";
-               $this->lastName = "";
-               $this->thisPage = 0;
-               $this->thisRev = 0;
-               $this->thisRevModel = null;
-               $this->thisRevFormat = null;
-
-               $parser = xml_parser_create( "UTF-8" );
-               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
-
-               xml_set_element_handler(
-                       $parser,
-                       array( &$this, 'startElement' ),
-                       array( &$this, 'endElement' )
-               );
-               xml_set_character_data_handler( $parser, array( &$this, 'characterData' ) );
-
-               $offset = 0; // for context extraction on error reporting
-               do {
-                       if ( $this->checkIfTimeExceeded() ) {
-                               $this->setTimeExceeded();
-                       }
-                       $chunk = fread( $input, $this->bufferSize );
-                       if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) {
-                               wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" );
-
-                               $byte = xml_get_current_byte_index( $parser );
-                               $msg = wfMessage( 'xml-error-string',
-                                       'XML import parse failure',
-                                       xml_get_current_line_number( $parser ),
-                                       xml_get_current_column_number( $parser ),
-                                       $byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte - $offset, 16 ) . '"' ) ),
-                                       xml_error_string( xml_get_error_code( $parser ) ) )->escaped();
-
-                               xml_parser_free( $parser );
-
-                               throw new MWException( $msg );
-                       }
-                       $offset += strlen( $chunk );
-               } while ( $chunk !== false && !feof( $input ) );
-               if ( $this->maxTimeAllowed ) {
-                       $filenameList = (array)$this->egress->getFilenames();
-                       // we wrote some stuff after last checkpoint that needs renamed
-                       if ( file_exists( $filenameList[0] ) ) {
-                               $newFilenames = array();
-                               # we might have just written the header and footer and had no
-                               # pages or revisions written... perhaps they were all deleted
-                               # there's no pageID 0 so we use that. the caller is responsible
-                               # for deciding what to do with a file containing only the
-                               # siteinfo information and the mw tags.
-                               if ( !$this->firstPageWritten ) {
-                                       $firstPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
-                                       $lastPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
-                               } else {
-                                       $firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
-                                       $lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
-                               }
-
-                               $filenameCount = count( $filenameList );
-                               for ( $i = 0; $i < $filenameCount; $i++ ) {
-                                       $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
-                                       $fileinfo = pathinfo( $filenameList[$i] );
-                                       $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
-                               }
-                               $this->egress->closeAndRename( $newFilenames );
-                       }
-               }
-               xml_parser_free( $parser );
-
-               return true;
-       }
-
-       /**
-        * Applies applicable export transformations to $text.
-        *
-        * @param string $text
-        * @param string $model
-        * @param string|null $format
-        *
-        * @return string
-        */
-       private function exportTransform( $text, $model, $format = null ) {
-               try {
-                       $handler = ContentHandler::getForModelID( $model );
-                       $text = $handler->exportTransform( $text, $format );
-               }
-               catch ( MWException $ex ) {
-                       $this->progress(
-                               "Unable to apply export transformation for content model '$model': " .
-                               $ex->getMessage()
-                       );
-               }
-
-               return $text;
-       }
-
-       /**
-        * Tries to get the revision text for a revision id.
-        * Export transformations are applied if the content model can is given or can be
-        * determined from the database.
-        *
-        * Upon errors, retries (Up to $this->maxFailures tries each call).
-        * If still no good revision get could be found even after this retrying, "" is returned.
-        * If no good revision text could be returned for
-        * $this->maxConsecutiveFailedTextRetrievals consecutive calls to getText, MWException
-        * is thrown.
-        *
-        * @param string $id The revision id to get the text for
-        * @param string|bool|null $model The content model used to determine
-        *  applicable export transformations.
-        *  If $model is null, it will be determined from the database.
-        * @param string|null $format The content format used when applying export transformations.
-        *
-        * @throws MWException
-        * @return string The revision text for $id, or ""
-        */
-       function getText( $id, $model = null, $format = null ) {
-               global $wgContentHandlerUseDB;
-
-               $prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch.
-               $text = false; // The candidate for a good text. false if no proper value.
-               $failures = 0; // The number of times, this invocation of getText already failed.
-
-               // The number of times getText failed without yielding a good text in between.
-               static $consecutiveFailedTextRetrievals = 0;
-
-               $this->fetchCount++;
-
-               // To allow to simply return on success and do not have to worry about book keeping,
-               // we assume, this fetch works (possible after some retries). Nevertheless, we koop
-               // the old value, so we can restore it, if problems occur (See after the while loop).
-               $oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals;
-               $consecutiveFailedTextRetrievals = 0;
-
-               if ( $model === null && $wgContentHandlerUseDB ) {
-                       $row = $this->db->selectRow(
-                               'revision',
-                               array( 'rev_content_model', 'rev_content_format' ),
-                               array( 'rev_id' => $this->thisRev ),
-                               __METHOD__
-                       );
-
-                       if ( $row ) {
-                               $model = $row->rev_content_model;
-                               $format = $row->rev_content_format;
-                       }
-               }
-
-               if ( $model === null || $model === '' ) {
-                       $model = false;
-               }
-
-               while ( $failures < $this->maxFailures ) {
-
-                       // As soon as we found a good text for the $id, we will return immediately.
-                       // Hence, if we make it past the try catch block, we know that we did not
-                       // find a good text.
-
-                       try {
-                               // Step 1: Get some text (or reuse from previous iteratuon if checking
-                               //         for plausibility failed)
-
-                               // Trying to get prefetch, if it has not been tried before
-                               if ( $text === false && isset( $this->prefetch ) && $prefetchNotTried ) {
-                                       $prefetchNotTried = false;
-                                       $tryIsPrefetch = true;
-                                       $text = $this->prefetch->prefetch( intval( $this->thisPage ),
-                                               intval( $this->thisRev ) );
-
-                                       if ( $text === null ) {
-                                               $text = false;
-                                       }
-
-                                       if ( is_string( $text ) && $model !== false ) {
-                                               // Apply export transformation to text coming from an old dump.
-                                               // The purpose of this transformation is to convert up from legacy
-                                               // formats, which may still be used in the older dump that is used
-                                               // for pre-fetching. Applying the transformation again should not
-                                               // interfere with content that is already in the correct form.
-                                               $text = $this->exportTransform( $text, $model, $format );
-                                       }
-                               }
-
-                               if ( $text === false ) {
-                                       // Fallback to asking the database
-                                       $tryIsPrefetch = false;
-                                       if ( $this->spawn ) {
-                                               $text = $this->getTextSpawned( $id );
-                                       } else {
-                                               $text = $this->getTextDb( $id );
-                                       }
-
-                                       if ( $text !== false && $model !== false ) {
-                                               // Apply export transformation to text coming from the database.
-                                               // Prefetched text should already have transformations applied.
-                                               $text = $this->exportTransform( $text, $model, $format );
-                                       }
-
-                                       // No more checks for texts from DB for now.
-                                       // If we received something that is not false,
-                                       // We treat it as good text, regardless of whether it actually is or is not
-                                       if ( $text !== false ) {
-                                               return $text;
-                                       }
-                               }
-
-                               if ( $text === false ) {
-                                       throw new MWException( "Generic error while obtaining text for id " . $id );
-                               }
-
-                               // We received a good candidate for the text of $id via some method
-
-                               // Step 2: Checking for plausibility and return the text if it is
-                               //         plausible
-                               $revID = intval( $this->thisRev );
-                               if ( !isset( $this->db ) ) {
-                                       throw new MWException( "No database available" );
-                               }
-
-                               if ( $model !== CONTENT_MODEL_WIKITEXT ) {
-                                       $revLength = strlen( $text );
-                               } else {
-                                       $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
-                               }
-
-                               if ( strlen( $text ) == $revLength ) {
-                                       if ( $tryIsPrefetch ) {
-                                               $this->prefetchCount++;
-                                       }
-
-                                       return $text;
-                               }
-
-                               $text = false;
-                               throw new MWException( "Received text is unplausible for id " . $id );
-                       } catch ( Exception $e ) {
-                               $msg = "getting/checking text " . $id . " failed (" . $e->getMessage() . ")";
-                               if ( $failures + 1 < $this->maxFailures ) {
-                                       $msg .= " (Will retry " . ( $this->maxFailures - $failures - 1 ) . " more times)";
-                               }
-                               $this->progress( $msg );
-                       }
-
-                       // Something went wrong; we did not a text that was plausible :(
-                       $failures++;
-
-                       // A failure in a prefetch hit does not warrant resetting db connection etc.
-                       if ( !$tryIsPrefetch ) {
-                               // After backing off for some time, we try to reboot the whole process as
-                               // much as possible to not carry over failures from one part to the other
-                               // parts
-                               sleep( $this->failureTimeout );
-                               try {
-                                       $this->rotateDb();
-                                       if ( $this->spawn ) {
-                                               $this->closeSpawn();
-                                               $this->openSpawn();
-                                       }
-                               } catch ( Exception $e ) {
-                                       $this->progress( "Rebooting getText infrastructure failed (" . $e->getMessage() . ")" .
-                                               " Trying to continue anyways" );
-                               }
-                       }
-               }
-
-               // Retirieving a good text for $id failed (at least) maxFailures times.
-               // We abort for this $id.
-
-               // Restoring the consecutive failures, and maybe aborting, if the dump
-               // is too broken.
-               $consecutiveFailedTextRetrievals = $oldConsecutiveFailedTextRetrievals + 1;
-               if ( $consecutiveFailedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals ) {
-                       throw new MWException( "Graceful storage failure" );
-               }
-
-               return "";
-       }
-
-       /**
-        * May throw a database error if, say, the server dies during query.
-        * @param int $id
-        * @return bool|string
-        * @throws MWException
-        */
-       private function getTextDb( $id ) {
-               global $wgContLang;
-               if ( !isset( $this->db ) ) {
-                       throw new MWException( __METHOD__ . "No database available" );
-               }
-               $row = $this->db->selectRow( 'text',
-                       array( 'old_text', 'old_flags' ),
-                       array( 'old_id' => $id ),
-                       __METHOD__ );
-               $text = Revision::getRevisionText( $row );
-               if ( $text === false ) {
-                       return false;
-               }
-               $stripped = str_replace( "\r", "", $text );
-               $normalized = $wgContLang->normalize( $stripped );
-
-               return $normalized;
-       }
-
-       private function getTextSpawned( $id ) {
-               MediaWiki\suppressWarnings();
-               if ( !$this->spawnProc ) {
-                       // First time?
-                       $this->openSpawn();
-               }
-               $text = $this->getTextSpawnedOnce( $id );
-               MediaWiki\restoreWarnings();
-
-               return $text;
-       }
-
-       function openSpawn() {
-               global $IP;
-
-               if ( file_exists( "$IP/../multiversion/MWScript.php" ) ) {
-                       $cmd = implode( " ",
-                               array_map( 'wfEscapeShellArg',
-                                       array(
-                                               $this->php,
-                                               "$IP/../multiversion/MWScript.php",
-                                               "fetchText.php",
-                                               '--wiki', wfWikiID() ) ) );
-               } else {
-                       $cmd = implode( " ",
-                               array_map( 'wfEscapeShellArg',
-                                       array(
-                                               $this->php,
-                                               "$IP/maintenance/fetchText.php",
-                                               '--wiki', wfWikiID() ) ) );
-               }
-               $spec = array(
-                       0 => array( "pipe", "r" ),
-                       1 => array( "pipe", "w" ),
-                       2 => array( "file", "/dev/null", "a" ) );
-               $pipes = array();
-
-               $this->progress( "Spawning database subprocess: $cmd" );
-               $this->spawnProc = proc_open( $cmd, $spec, $pipes );
-               if ( !$this->spawnProc ) {
-                       $this->progress( "Subprocess spawn failed." );
-
-                       return false;
-               }
-               list(
-                       $this->spawnWrite, // -> stdin
-                       $this->spawnRead, // <- stdout
-               ) = $pipes;
-
-               return true;
-       }
-
-       private function closeSpawn() {
-               MediaWiki\suppressWarnings();
-               if ( $this->spawnRead ) {
-                       fclose( $this->spawnRead );
-               }
-               $this->spawnRead = false;
-               if ( $this->spawnWrite ) {
-                       fclose( $this->spawnWrite );
-               }
-               $this->spawnWrite = false;
-               if ( $this->spawnErr ) {
-                       fclose( $this->spawnErr );
-               }
-               $this->spawnErr = false;
-               if ( $this->spawnProc ) {
-                       pclose( $this->spawnProc );
-               }
-               $this->spawnProc = false;
-               MediaWiki\restoreWarnings();
-       }
-
-       private function getTextSpawnedOnce( $id ) {
-               global $wgContLang;
-
-               $ok = fwrite( $this->spawnWrite, "$id\n" );
-               // $this->progress( ">> $id" );
-               if ( !$ok ) {
-                       return false;
-               }
-
-               $ok = fflush( $this->spawnWrite );
-               // $this->progress( ">> [flush]" );
-               if ( !$ok ) {
-                       return false;
-               }
-
-               // check that the text id they are sending is the one we asked for
-               // this avoids out of sync revision text errors we have encountered in the past
-               $newId = fgets( $this->spawnRead );
-               if ( $newId === false ) {
-                       return false;
-               }
-               if ( $id != intval( $newId ) ) {
-                       return false;
-               }
-
-               $len = fgets( $this->spawnRead );
-               // $this->progress( "<< " . trim( $len ) );
-               if ( $len === false ) {
-                       return false;
-               }
-
-               $nbytes = intval( $len );
-               // actual error, not zero-length text
-               if ( $nbytes < 0 ) {
-                       return false;
-               }
-
-               $text = "";
-
-               // Subprocess may not send everything at once, we have to loop.
-               while ( $nbytes > strlen( $text ) ) {
-                       $buffer = fread( $this->spawnRead, $nbytes - strlen( $text ) );
-                       if ( $buffer === false ) {
-                               break;
-                       }
-                       $text .= $buffer;
-               }
-
-               $gotbytes = strlen( $text );
-               if ( $gotbytes != $nbytes ) {
-                       $this->progress( "Expected $nbytes bytes from database subprocess, got $gotbytes " );
-
-                       return false;
-               }
-
-               // Do normalization in the dump thread...
-               $stripped = str_replace( "\r", "", $text );
-               $normalized = $wgContLang->normalize( $stripped );
-
-               return $normalized;
-       }
-
-       function startElement( $parser, $name, $attribs ) {
-               $this->checkpointJustWritten = false;
-
-               $this->clearOpenElement( null );
-               $this->lastName = $name;
-
-               if ( $name == 'revision' ) {
-                       $this->state = $name;
-                       $this->egress->writeOpenPage( null, $this->buffer );
-                       $this->buffer = "";
-               } elseif ( $name == 'page' ) {
-                       $this->state = $name;
-                       if ( $this->atStart ) {
-                               $this->egress->writeOpenStream( $this->buffer );
-                               $this->buffer = "";
-                               $this->atStart = false;
-                       }
-               }
-
-               if ( $name == "text" && isset( $attribs['id'] ) ) {
-                       $id = $attribs['id'];
-                       $model = trim( $this->thisRevModel );
-                       $format = trim( $this->thisRevFormat );
-
-                       $model = $model === '' ? null : $model;
-                       $format = $format === '' ? null : $format;
-
-                       $text = $this->getText( $id, $model, $format );
-                       $this->openElement = array( $name, array( 'xml:space' => 'preserve' ) );
-                       if ( strlen( $text ) > 0 ) {
-                               $this->characterData( $parser, $text );
-                       }
-               } else {
-                       $this->openElement = array( $name, $attribs );
-               }
-       }
-
-       function endElement( $parser, $name ) {
-               $this->checkpointJustWritten = false;
-
-               if ( $this->openElement ) {
-                       $this->clearOpenElement( "" );
-               } else {
-                       $this->buffer .= "</$name>";
-               }
-
-               if ( $name == 'revision' ) {
-                       $this->egress->writeRevision( null, $this->buffer );
-                       $this->buffer = "";
-                       $this->thisRev = "";
-                       $this->thisRevModel = null;
-                       $this->thisRevFormat = null;
-               } elseif ( $name == 'page' ) {
-                       if ( !$this->firstPageWritten ) {
-                               $this->firstPageWritten = trim( $this->thisPage );
-                       }
-                       $this->lastPageWritten = trim( $this->thisPage );
-                       if ( $this->timeExceeded ) {
-                               $this->egress->writeClosePage( $this->buffer );
-                               // nasty hack, we can't just write the chardata after the
-                               // page tag, it will include leading blanks from the next line
-                               $this->egress->sink->write( "\n" );
-
-                               $this->buffer = $this->xmlwriterobj->closeStream();
-                               $this->egress->writeCloseStream( $this->buffer );
-
-                               $this->buffer = "";
-                               $this->thisPage = "";
-                               // this could be more than one file if we had more than one output arg
-
-                               $filenameList = (array)$this->egress->getFilenames();
-                               $newFilenames = array();
-                               $firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
-                               $lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
-                               $filenamesCount = count( $filenameList );
-                               for ( $i = 0; $i < $filenamesCount; $i++ ) {
-                                       $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
-                                       $fileinfo = pathinfo( $filenameList[$i] );
-                                       $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
-                               }
-                               $this->egress->closeRenameAndReopen( $newFilenames );
-                               $this->buffer = $this->xmlwriterobj->openStream();
-                               $this->timeExceeded = false;
-                               $this->timeOfCheckpoint = $this->lastTime;
-                               $this->firstPageWritten = false;
-                               $this->checkpointJustWritten = true;
-                       } else {
-                               $this->egress->writeClosePage( $this->buffer );
-                               $this->buffer = "";
-                               $this->thisPage = "";
-                       }
-               } elseif ( $name == 'mediawiki' ) {
-                       $this->egress->writeCloseStream( $this->buffer );
-                       $this->buffer = "";
-               }
-       }
-
-       function characterData( $parser, $data ) {
-               $this->clearOpenElement( null );
-               if ( $this->lastName == "id" ) {
-                       if ( $this->state == "revision" ) {
-                               $this->thisRev .= $data;
-                       } elseif ( $this->state == "page" ) {
-                               $this->thisPage .= $data;
-                       }
-               } elseif ( $this->lastName == "model" ) {
-                       $this->thisRevModel .= $data;
-               } elseif ( $this->lastName == "format" ) {
-                       $this->thisRevFormat .= $data;
-               }
-
-               // have to skip the newline left over from closepagetag line of
-               // end of checkpoint files. nasty hack!!
-               if ( $this->checkpointJustWritten ) {
-                       if ( $data[0] == "\n" ) {
-                               $data = substr( $data, 1 );
-                       }
-                       $this->checkpointJustWritten = false;
-               }
-               $this->buffer .= htmlspecialchars( $data );
-       }
-
-       function clearOpenElement( $style ) {
-               if ( $this->openElement ) {
-                       $this->buffer .= Xml::element( $this->openElement[0], $this->openElement[1], $style );
-                       $this->openElement = false;
-               }
-       }
-}
index 3f8a899..f3e2db0 100644 (file)
@@ -22,7 +22,6 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  * http://www.gnu.org/copyleft/gpl.html
  *
- * @todo Report PHP version, OS ..
  * @file
  * @ingroup Benchmark
  */
@@ -39,7 +38,7 @@ abstract class Benchmarker extends Maintenance {
 
        public function __construct() {
                parent::__construct();
-               $this->addOption( 'count', "How many time to run a benchmark", false, true );
+               $this->addOption( 'count', "How many times to run a benchmark", false, true );
        }
 
        public function bench( array $benchs ) {
@@ -76,7 +75,13 @@ abstract class Benchmarker extends Maintenance {
        }
 
        public function getFormattedResults() {
-               $ret = '';
+               $ret = sprintf( "Running PHP version %s (%s) on %s %s %s\n\n",
+                       phpversion(),
+                       php_uname( 'm' ),
+                       php_uname( 's' ),
+                       php_uname( 'r' ),
+                       php_uname( 'v' )
+               );
                foreach ( $this->results as $res ) {
                        // show function with args
                        $ret .= sprintf( "%s times: function %s(%s) :\n",
index 8ae4f03..572c548 100644 (file)
@@ -35,7 +35,7 @@ class BenchmarkDeleteTruncate extends Benchmarker {
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $test = $dbw->tableName( 'test' );
                $dbw->query( "CREATE TABLE IF NOT EXISTS /*_*/$test (
index ce38dad..b850e63 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 = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                $id = $dbr->selectField(
                        array( 'revision', 'page' ),
index fec9291..500fc35 100644 (file)
@@ -36,7 +36,7 @@ class CheckBadRedirects extends Maintenance {
 
        public function execute() {
                $this->output( "Fetching redirects...\n" );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $result = $dbr->select(
                        array( 'page' ),
                        array( 'page_namespace', 'page_title', 'page_latest' ),
index 0364db2..9761927 100644 (file)
@@ -37,7 +37,7 @@ class CheckImages extends Maintenance {
 
        public function execute() {
                $start = '';
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                $numImages = 0;
                $numGood = 0;
index a64bc49..6f4d170 100644 (file)
@@ -40,7 +40,7 @@ class CheckUsernames extends Maintenance {
        }
 
        function execute() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                $maxUserId = 0;
                do {
index 2dbf8bc..f1467d5 100644 (file)
@@ -46,7 +46,7 @@ class CleanupAncientTables extends Maintenance {
                        );
                }
 
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
                $ancientTables = array(
                        'blobs', // 1.4
                        'brokenlinks', // 1.4
index 1736203..437abe9 100644 (file)
@@ -38,7 +38,7 @@ class CleanupBlocks extends Maintenance {
        }
 
        public function execute() {
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
 
                $max = $db->selectField( 'ipblocks', 'MAX(ipb_user)' );
 
index 1bd0217..ab2d808 100644 (file)
@@ -102,7 +102,7 @@ class ImageCleanup extends TableCleanup {
                        $this->output( "DRY RUN: would delete bogus row '$name'\n" );
                } else {
                        $this->output( "deleting bogus row '$name'\n" );
-                       $db = wfGetDB( DB_MASTER );
+                       $db = $this->getDB( DB_MASTER );
                        $db->delete( 'image',
                                array( 'img_name' => $name ),
                                __METHOD__ );
@@ -139,7 +139,7 @@ class ImageCleanup extends TableCleanup {
                        return;
                }
 
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
 
                /*
                 * To prevent key collisions in the update() statements below,
@@ -167,7 +167,7 @@ class ImageCleanup extends TableCleanup {
                } else {
                        $this->output( "renaming $path to $finalPath\n" );
                        // @todo FIXME: Should this use File::move()?
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        $db->update( 'image',
                                array( 'img_name' => $final ),
                                array( 'img_name' => $orig ),
@@ -184,16 +184,16 @@ class ImageCleanup extends TableCleanup {
                        if ( !file_exists( $dir ) ) {
                                if ( !wfMkdirParents( $dir, null, __METHOD__ ) ) {
                                        $this->output( "RENAME FAILED, COULD NOT CREATE $dir" );
-                                       $db->rollback( __METHOD__ );
+                                       $this->rollbackTransaction( $db, __METHOD__ );
 
                                        return;
                                }
                        }
                        if ( rename( $path, $finalPath ) ) {
-                               $db->commit( __METHOD__ );
+                               $this->commitTransaction( $db, __METHOD__ );
                        } else {
                                $this->error( "RENAME FAILED" );
-                               $db->rollback( __METHOD__ );
+                               $this->rollbackTransaction( $db, __METHOD__ );
                        }
                }
        }
index 4e19b79..5253ab3 100644 (file)
@@ -34,8 +34,8 @@ class CleanupPreferences extends Maintenance {
        public function execute() {
                global $wgHiddenPrefs;
 
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
                foreach ( $wgHiddenPrefs as $item ) {
                        $dbw->delete(
                                'user_properties',
@@ -43,7 +43,7 @@ class CleanupPreferences extends Maintenance {
                                __METHOD__
                        );
                };
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                $this->output( "Finished!\n" );
        }
 }
index 68f57a3..810fad9 100644 (file)
@@ -39,7 +39,7 @@ class CleanupRemovedModules extends Maintenance {
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $rl = new ResourceLoader( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) );
                $moduleNames = $rl->getModuleNames();
                $moduleList = implode( ', ', array_map( array( $dbw, 'addQuotes' ), $moduleNames ) );
index 44d5810..b43ce81 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 = wfGetDB( DB_SLAVE, array(), $wikiID );
+                               $dbr = $this->getDB( DB_SLAVE, array(), $wikiID );
 
                                $count = $dbr->selectField( 'externallinks', 'COUNT(*)',
                                        array( 'el_index' . $dbr->buildLike( $like ) ), __METHOD__ );
@@ -83,7 +83,7 @@ class CleanupSpam extends Maintenance {
                } else {
                        // Clean up spam on this wiki
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_SLAVE );
                        $res = $dbr->select( 'externallinks', array( 'DISTINCT el_from' ),
                                array( 'el_index' . $dbr->buildLike( $like ) ), __METHOD__ );
                        $count = $dbr->numRows( $res );
@@ -120,8 +120,8 @@ class CleanupSpam extends Maintenance {
                        // This happens e.g. when a link comes from a template rather than the page itself
                        $this->output( "False match\n" );
                } else {
-                       $dbw = wfGetDB( DB_MASTER );
-                       $dbw->begin( __METHOD__ );
+                       $dbw = $this->getDB( DB_MASTER );
+                       $this->beginTransaction( $dbw, __METHOD__ );
                        $page = WikiPage::factory( $title );
                        if ( $rev ) {
                                // Revert to this revision
@@ -151,7 +151,7 @@ class CleanupSpam extends Maintenance {
                                        wfMessage( 'spam_blanking', $domain )->inContentLanguage()->text()
                                );
                        }
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                }
        }
 }
index 8368c84..0ddaf33 100644 (file)
@@ -106,7 +106,7 @@ class TableCleanup extends Maintenance {
         * @throws MWException
         */
        public function runTable( $params ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                if ( array_diff( array_keys( $params ),
                        array( 'table', 'conds', 'index', 'callback' ) )
@@ -172,4 +172,3 @@ class TableCleanup extends Maintenance {
                return sprintf( "\\x%02x", ord( $matches[1] ) );
        }
 }
-
index 1eba303..07df1b1 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 = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $row = $dbr->selectRow( 'image', array( 'img_name' ), array( 'img_name' => $name ), __METHOD__ );
 
                return $row !== false;
@@ -118,7 +118,7 @@ class TitleCleanup extends TableCleanup {
                } else {
                        $this->output( "renaming $row->page_id ($row->page_namespace," .
                                "'$row->page_title') to ($row->page_namespace,'$dest')\n" );
-                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw = $this->getDB( DB_MASTER );
                        $dbw->update( 'page',
                                array( 'page_title' => $dest ),
                                array( 'page_id' => $row->page_id ),
@@ -171,7 +171,7 @@ class TitleCleanup extends TableCleanup {
                } else {
                        $this->output( "renaming $row->page_id ($row->page_namespace," .
                                "'$row->page_title') to ($ns,'$dest')\n" );
-                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw = $this->getDB( DB_MASTER );
                        $dbw->update( 'page',
                                array(
                                        'page_namespace' => $ns,
index eb7d7b1..16f7b61 100644 (file)
@@ -77,7 +77,7 @@ class WatchlistCleanup extends TableCleanup {
 
        private function removeWatch( $row ) {
                if ( !$this->dryrun && $this->hasOption( 'fix' ) ) {
-                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw = $this->getDB( DB_MASTER );
                        $dbw->delete(
                                'watchlist', array(
                                'wl_user' => $row->wl_user,
index 80c9004..6a6527f 100644 (file)
@@ -37,7 +37,7 @@ class ClearInterwikiCache extends Maintenance {
 
        public function execute() {
                global $wgLocalDatabases, $wgMemc;
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $res = $dbr->select( 'interwiki', array( 'iw_prefix' ), false );
                $prefixes = array();
                foreach ( $res as $row ) {
index 88776f4..1a05907 100644 (file)
@@ -63,4 +63,3 @@ class CommandLineInc extends Maintenance {
 
 $maintClass = 'CommandLineInc';
 require RUN_MAINTENANCE_IF_MAIN;
-
index 608605c..3113533 100644 (file)
@@ -116,6 +116,13 @@ class ConvertExtensionToRegistration extends Maintenance {
                        }
                }
 
+               // check, if the extension requires composer libraries
+               if ( $this->needsComposerAutoloader( dirname( $this->getArg( 0 ) ) ) ) {
+                       // set the load composer autoloader automatically property
+                       $this->output( "Detected composer dependencies, setting 'load_composer_autoloader' to true.\n" );
+                       $this->json['load_composer_autoloader'] = true;
+               }
+
                // Move some keys to the top
                $out = array();
                foreach ( $this->promote as $key ) {
@@ -144,6 +151,12 @@ class ConvertExtensionToRegistration extends Maintenance {
                                        "Please move your extension function somewhere else.", 1
                                );
                        }
+                       // check if $func exists in the global scope
+                       if ( function_exists( $func ) ) {
+                               $this->error( "Error: Global functions cannot be converted to JSON. " .
+                                       "Please move your extension function ($func) into a class.", 1
+                               );
+                       }
                }
 
                $this->json[$realName] = $value;
@@ -210,6 +223,12 @@ class ConvertExtensionToRegistration extends Maintenance {
                                                "Please move the handler for $hookName somewhere else.", 1
                                        );
                                }
+                               // Check if $func exists in the global scope
+                               if ( function_exists( $func ) ) {
+                                       $this->error( "Error: Global functions cannot be converted to JSON. " .
+                                               "Please move the handler for $hookName inside a class.", 1
+                                       );
+                               }
                        }
                }
                $this->json[$realName] = $value;
@@ -246,6 +265,19 @@ class ConvertExtensionToRegistration extends Maintenance {
                        $this->json['ResourceFileModulePaths'] = $defaults;
                }
        }
+
+       protected function needsComposerAutoloader( $path ) {
+               $path .= '/composer.json';
+               if ( file_exists( $path ) ) {
+                       // assume, that the composer.json file is in the root of the extension path
+                       $composerJson = new ComposerJson( $path );
+                       // check, if there are some dependencies in the require section
+                       if ( $composerJson->getRequiredDependencies() ) {
+                               return true;
+                       }
+               }
+               return false;
+       }
 }
 
 $maintClass = 'ConvertExtensionToRegistration';
index c3ad46a..15ca14b 100644 (file)
@@ -66,7 +66,7 @@ class ConvertLinks extends Maintenance {
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $type = $dbw->getType();
                if ( $type != 'mysql' ) {
@@ -267,7 +267,7 @@ class ConvertLinks extends Maintenance {
        }
 
        private function createTempTable() {
-               $dbConn = wfGetDB( DB_MASTER );
+               $dbConn = $this->getDB( DB_MASTER );
 
                if ( !( $dbConn->isOpen() ) ) {
                        $this->output( "Opening connection to database failed.\n" );
index 1542a8c..11768c8 100644 (file)
@@ -41,7 +41,7 @@ class ConvertUserOptions extends Maintenance {
        public function execute() {
                $this->output( "...batch conversion of user_options: " );
                $id = 0;
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                if ( !$dbw->fieldExists( 'user', 'user_options', __METHOD__ ) ) {
                        $this->output( "nothing to migrate. " );
index bd8ca10..94ebf87 100644 (file)
@@ -47,7 +47,7 @@ class DeleteArchivedFiles extends Maintenance {
 
                # Data should come off the master, wrapped in a transaction
                $dbw = $this->getDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $this->beginTransaction( $dbw, __METHOD__ );
                $repo = RepoGroup::singleton()->getLocalRepo();
 
                # Get "active" revisions from the filearchive table
@@ -113,7 +113,7 @@ class DeleteArchivedFiles extends Maintenance {
                        $dbw->delete( 'filearchive', array( 'fa_id' => $id ), __METHOD__ );
                }
 
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                $this->output( "Done! [$count file(s)]\n" );
        }
 }
index a3b1561..6c89e67 100644 (file)
@@ -80,7 +80,7 @@ class DeleteBatch extends Maintenance {
                        $this->error( "Unable to read file, exiting", true );
                }
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                # Handle each entry
                // @codingStandardsIgnoreStart Ignore Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed
index a1c0f61..a5c6199 100644 (file)
@@ -41,7 +41,7 @@ class DeleteDefaultMessages extends Maintenance {
                global $wgUser;
 
                $this->output( "Checking existence of old default messages..." );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $res = $dbr->select( array( 'page', 'revision' ),
                        array( 'page_namespace', 'page_title' ),
                        array(
@@ -69,7 +69,7 @@ class DeleteDefaultMessages extends Maintenance {
 
                # Handle deletion
                $this->output( "\n...deleting old default messages (this may take a long time!)...", 'msg' );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                foreach ( $res as $row ) {
                        wfWaitForSlaves();
index de5c5e2..e7bb866 100644 (file)
@@ -174,7 +174,7 @@ class DeleteEqualMessages extends Maintenance {
 
                // Handle deletion
                $this->output( "\n...deleting equal messages (this may take a long time!)..." );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                foreach ( $messageInfo['results'] as $result ) {
                        wfWaitForSlaves();
                        $dbw->ping();
index 847d863..f411148 100644 (file)
@@ -45,8 +45,8 @@ class DeleteOldRevisions extends Maintenance {
        function doDelete( $delete = false, $args = array() ) {
 
                # Data should come off the master, wrapped in a transaction
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $pageConds = array();
                $revConds = array();
@@ -92,7 +92,7 @@ class DeleteOldRevisions extends Maintenance {
 
                # This bit's done
                # Purge redundant text records
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                if ( $delete ) {
                        $this->purgeRedundantText( true );
                }
index 7f1ffe4..3d5c1a4 100644 (file)
@@ -43,8 +43,8 @@ class DeleteOrphanedRevisions extends Maintenance {
 
                $report = $this->hasOption( 'report' );
 
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
                list( $page, $revision ) = $dbw->tableNamesN( 'page', 'revision' );
 
                # Find all the orphaned revisions
@@ -63,7 +63,7 @@ class DeleteOrphanedRevisions extends Maintenance {
 
                # Nothing to do?
                if ( $report || $count == 0 ) {
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                        exit( 0 );
                }
 
@@ -73,7 +73,7 @@ class DeleteOrphanedRevisions extends Maintenance {
                $this->output( "done.\n" );
 
                # Close the transaction and call the script to purge unused text records
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
                $this->purgeRedundantText( true );
        }
 
index 818ee36..6cda784 100644 (file)
@@ -43,7 +43,7 @@ class DeleteRevision extends Maintenance {
 
                $this->output( "Deleting revision(s) " . implode( ',', $this->mArgs ) .
                        " from " . wfWikiID() . "...\n" );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $affected = 0;
                foreach ( $this->mArgs as $revID ) {
index a097622..f9bb416 100644 (file)
@@ -39,10 +39,10 @@ class DeleteSelfExternals extends Maintenance {
        public function execute() {
                global $wgServer;
                $this->output( "Deleting self externals from $wgServer\n" );
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
                while ( 1 ) {
                        wfWaitForSlaves();
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
                        $q = $db->limitResult( "DELETE /* deleteSelfExternals */ FROM externallinks WHERE el_to"
                                . $db->buildLike( $wgServer . '/', $db->anyString() ), $this->mBatchSize );
                        $this->output( "Deleting a batch\n" );
index e66b729..3a844bc 100644 (file)
@@ -113,5 +113,5 @@ wfLogProfilingData();
 
 // Commit and close up!
 $factory = wfGetLBFactory();
-$factory->commitMasterChanges();
+$factory->commitMasterChanges( 'doMaintenance' );
 $factory->shutdown();
index 18c78dc..18737a4 100644 (file)
  * @ingroup Dump Maintenance
  */
 
-$originalDir = getcwd();
-
-$optionsWithArgs = array( 'pagelist', 'start', 'end', 'revstart', 'revend' );
-
-require_once __DIR__ . '/commandLine.inc';
 require_once __DIR__ . '/backup.inc';
 
-$dumper = new BackupDumper( $argv );
+class DumpBackup extends BackupDumper {
+       function __construct( $args = null ) {
+               parent::__construct();
 
-if ( isset( $options['quiet'] ) ) {
-       $dumper->reporting = false;
-}
-
-if ( isset( $options['pagelist'] ) ) {
-       $olddir = getcwd();
-       chdir( $originalDir );
-       $pages = file( $options['pagelist'] );
-       chdir( $olddir );
-       if ( $pages === false ) {
-               echo "Unable to open file {$options['pagelist']}\n";
-               die( 1 );
-       }
-       $pages = array_map( 'trim', $pages );
-       $dumper->pages = array_filter( $pages, create_function( '$x', 'return $x !== "";' ) );
-}
-
-if ( isset( $options['start'] ) ) {
-       $dumper->startId = intval( $options['start'] );
-}
-if ( isset( $options['end'] ) ) {
-       $dumper->endId = intval( $options['end'] );
-}
-
-if ( isset( $options['revstart'] ) ) {
-       $dumper->revStartId = intval( $options['revstart'] );
-}
-if ( isset( $options['revend'] ) ) {
-       $dumper->revEndId = intval( $options['revend'] );
-}
-$dumper->skipHeader = isset( $options['skip-header'] );
-$dumper->skipFooter = isset( $options['skip-footer'] );
-$dumper->dumpUploads = isset( $options['uploads'] );
-$dumper->dumpUploadFileContents = isset( $options['include-files'] );
-
-$textMode = isset( $options['stub'] ) ? WikiExporter::STUB : WikiExporter::TEXT;
-
-if ( isset( $options['full'] ) ) {
-       $dumper->dump( WikiExporter::FULL, $textMode );
-} elseif ( isset( $options['current'] ) ) {
-       $dumper->dump( WikiExporter::CURRENT, $textMode );
-} elseif ( isset( $options['stable'] ) ) {
-       $dumper->dump( WikiExporter::STABLE, $textMode );
-} elseif ( isset( $options['logs'] ) ) {
-       $dumper->dump( WikiExporter::LOGS );
-} elseif ( isset( $options['revrange'] ) ) {
-       $dumper->dump( WikiExporter::RANGE, $textMode );
-} else {
-       $dumper->progress( <<<ENDS
+               $this->mDescription = <<<TEXT
 This script dumps the wiki page or logging database into an
 XML interchange wrapper format for export or backup.
 
 XML output is sent to stdout; progress reports are sent to stderr.
 
 WARNING: this is not a full database dump! It is merely for public export
-                of your wiki. For full backup, see our online help at:
+         of your wiki. For full backup, see our online help at:
          https://www.mediawiki.org/wiki/Backup
+TEXT;
+               $this->stderr = fopen( "php://stderr", "wt" );
+               // Actions
+               $this->addOption( 'full', 'Dump all revisions of every page' );
+               $this->addOption( 'current', 'Dump only the latest revision of every page.' );
+               $this->addOption( 'logs', 'Dump all log events' );
+               $this->addOption( 'stable', 'Dump stable versions of pages' );
+               $this->addOption( 'revrange', 'Dump range of revisions specified by revstart and ' .
+                       'revend parameters' );
+               $this->addOption( 'pagelist',
+                       'Dump only pages included in the file', false, true );
+               // Options
+               $this->addOption( 'start', 'Start from page_id or log_id', false, true );
+               $this->addOption( 'end', 'Stop before page_id or log_id n (exclusive)', false, true );
+               $this->addOption( 'revstart', 'Start from rev_id', false, true );
+               $this->addOption( 'revend', 'Stop before rev_id n (exclusive)', false, true );
+               $this->addOption( 'skip-header', 'Don\'t output the <mediawiki> header' );
+               $this->addOption( 'skip-footer', 'Don\'t output the </mediawiki> footer' );
+               $this->addOption( 'stub', 'Don\'t perform old_text lookups; for 2-pass dump' );
+               $this->addOption( 'uploads', 'Include upload records without files' );
+               $this->addOption( 'include-files', 'Include files within the XML stream' );
+
+               if ( $args ) {
+                       $this->loadWithArgv( $args );
+                       $this->processOptions();
+               }
+       }
 
-Usage: php dumpBackup.php <action> [<options>]
-Actions:
-  --full      Dump all revisions of every page.
-  --current   Dump only the latest revision of every page.
-  --logs      Dump all log events.
-  --stable    Stable versions of pages?
-  --pagelist=<file>
-                         Where <file> is a list of page titles to be dumped
-  --revrange  Dump specified range of revisions, requires
-              revstart and revend options.
-Options:
-  --quiet     Don't dump status reports to stderr.
-  --report=n  Report position and speed after every n pages processed.
-                         (Default: 100)
-  --server=h  Force reading from MySQL server h
-  --start=n   Start from page_id or log_id n
-  --end=n     Stop before page_id or log_id n (exclusive)
-  --revstart=n  Start from rev_id n
-  --revend=n    Stop before rev_id n (exclusive)
-  --skip-header Don't output the <mediawiki> header
-  --skip-footer Don't output the </mediawiki> footer
-  --stub      Don't perform old_text lookups; for 2-pass dump
-  --uploads   Include upload records without files
-  --include-files Include files within the XML stream
-  --conf=<file> Use the specified configuration file (LocalSettings.php)
-
-  --wiki=<wiki>  Only back up the specified <wiki>
-
-Fancy stuff: (Works? Add examples please.)
-  --plugin=<class>[:<file>]   Load a dump plugin class
-  --output=<type>:<file>      Begin a filtered output stream;
-                              <type>s: file, gzip, bzip2, 7zip
-  --filter=<type>[:<options>] Add a filter on an output branch
-
-ENDS
-       );
+       function execute() {
+               $this->processOptions();
+
+               $textMode = $this->hasOption( 'stub' ) ? WikiExporter::STUB : WikiExporter::TEXT;
+
+               if ( $this->hasOption( 'full' ) ) {
+                       $this->dump( WikiExporter::FULL, $textMode );
+               } elseif ( $this->hasOption( 'current' ) ) {
+                       $this->dump( WikiExporter::CURRENT, $textMode );
+               } elseif ( $this->hasOption( 'stable' ) ) {
+                       $this->dump( WikiExporter::STABLE, $textMode );
+               } elseif ( $this->hasOption( 'logs' ) ) {
+                       $this->dump( WikiExporter::LOGS );
+               } elseif ( $this->hasOption( 'revrange' ) ) {
+                       $this->dump( WikiExporter::RANGE, $textMode );
+               } else {
+                       $this->error( 'No valid action specified.', 1 );
+               }
+       }
+
+       function processOptions() {
+               parent::processOptions();
+
+               // Evaluate options specific to this class
+               $this->reporting = !$this->hasOption( 'quiet' );
+
+               if ( $this->hasOption( 'pagelist' ) ) {
+                       $filename = $this->getOption( 'pagelist' );
+                       $pages = file( $filename );
+                       if ( $pages === false ) {
+                               $this->fatalError( "Unable to open file {$filename}\n" );
+                       }
+                       $pages = array_map( 'trim', $pages );
+                       $this->pages = array_filter( $pages, create_function( '$x', 'return $x !== "";' ) );
+               }
+
+               if ( $this->hasOption( 'start' ) ) {
+                       $this->startId = intval( $this->getOption( 'start' ) );
+               }
+
+               if ( $this->hasOption( 'end' ) ) {
+                       $this->endId = intval( $this->getOption( 'end' ) );
+               }
+
+               if ( $this->hasOption( 'revstart' ) ) {
+                       $this->revStartId = intval( $this->getOption( 'revstart' ) );
+               }
+
+               if ( $this->hasOption( 'revend' ) ) {
+                       $this->revEndId = intval( $this->getOption( 'revend' ) );
+               }
+
+               $this->skipHeader = $this->hasOption( 'skip-header' );
+               $this->skipFooter = $this->hasOption( 'skip-footer' );
+               $this->dumpUploads = $this->hasOption( 'uploads' );
+               $this->dumpUploadFileContents = $this->hasOption( 'include-files' );
+       }
 }
+
+$maintClass = 'DumpBackup';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 888c2dc..74b500a 100644 (file)
@@ -44,7 +44,7 @@ class DumpLinks extends Maintenance {
        }
 
        public function execute() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $result = $dbr->select( array( 'pagelinks', 'page' ),
                        array(
                                'page_id',
index bde5a07..7511392 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Script that postprocesses XML dumps from dumpBackup.php to add page text
+ * BackupDumper that postprocesses XML dumps from dumpBackup.php to add page text
  *
  * Copyright (C) 2005 Brion Vibber <brion@pobox.com>
  * https://www.mediawiki.org/
  * @ingroup Maintenance
  */
 
-$originalDir = getcwd();
+require_once __DIR__ . '/backup.inc';
+require_once __DIR__ . '/../includes/export/WikiExporter.php';
 
-require_once __DIR__ . '/commandLine.inc';
-require_once __DIR__ . '/backupTextPass.inc';
+/**
+ * @ingroup Maintenance
+ */
+class TextPassDumper extends BackupDumper {
+       public $prefetch = null;
+
+       // when we spend more than maxTimeAllowed seconds on this run, we continue
+       // processing until we write out the next complete page, then save output file(s),
+       // rename it/them and open new one(s)
+       public $maxTimeAllowed = 0; // 0 = no limit
+
+       protected $input = "php://stdin";
+       protected $history = WikiExporter::FULL;
+       protected $fetchCount = 0;
+       protected $prefetchCount = 0;
+       protected $prefetchCountLast = 0;
+       protected $fetchCountLast = 0;
+
+       protected $maxFailures = 5;
+       protected $maxConsecutiveFailedTextRetrievals = 200;
+       protected $failureTimeout = 5; // Seconds to sleep after db failure
+
+       protected $bufferSize = 524288; // In bytes. Maximum size to read from the stub in on go.
+
+       protected $php = "php";
+       protected $spawn = false;
+
+       /**
+        * @var bool|resource
+        */
+       protected $spawnProc = false;
 
-$dumper = new TextPassDumper( $argv );
+       /**
+        * @var bool|resource
+        */
+       protected $spawnWrite = false;
 
-if ( !isset( $options['help'] ) ) {
-       $dumper->dump( true );
-} else {
-       $dumper->progress( <<<ENDS
+       /**
+        * @var bool|resource
+        */
+       protected $spawnRead = false;
+
+       /**
+        * @var bool|resource
+        */
+       protected $spawnErr = false;
+
+       protected $xmlwriterobj = false;
+
+       protected $timeExceeded = false;
+       protected $firstPageWritten = false;
+       protected $lastPageWritten = false;
+       protected $checkpointJustWritten = false;
+       protected $checkpointFiles = array();
+
+       /**
+        * @var DatabaseBase
+        */
+       protected $db;
+
+       /**
+        * @param array $args For backward compatibility
+        */
+       function __construct( $args = null ) {
+               parent::__construct();
+
+               $this->mDescription = <<<TEXT
 This script postprocesses XML dumps from dumpBackup.php to add
 page text which was stubbed out (using --stub).
 
 XML input is accepted on stdin.
 XML output is sent to stdout; progress reports are sent to stderr.
+TEXT;
+               $this->stderr = fopen( "php://stderr", "wt" );
+
+               $this->addOption( 'stub', 'To load a compressed stub dump instead of stdin. ' .
+                       'Specify as --stub=<type>:<file>.', false, true );
+               $this->addOption( 'prefetch', 'Use a prior dump file as a text source, to savepressure on the ' .
+                       'database. (Requires the XMLReader extension). Specify as --prefetch=<type>:<file>',
+                       false, true );
+               $this->addOption( 'maxtime', 'Write out checkpoint file after this many minutes (writing' .
+                       'out complete page, closing xml file properly, and opening new one' .
+                       'with header).  This option requires the checkpointfile option.', false, true );
+               $this->addOption( 'checkpointfile', 'Use this string for checkpoint filenames,substituting ' .
+                       'first pageid written for the first %s (required) and the last pageid written for the ' .
+                       'second %s if it exists.', false, true, false, true ); // This can be specified multiple times
+               $this->addOption( 'quiet', 'Don\'t dump status reports to stderr.' );
+               $this->addOption( 'current', 'Base ETA on number of pages in database instead of all revisions' );
+               $this->addOption( 'spawn', 'Spawn a subprocess for loading text records' );
+               $this->addOption( 'buffersize', 'Buffer size in bytes to use for reading the stub. ' .
+                       '(Default: 512KB, Minimum: 4KB)', false, true );
+
+               if ( $args ) {
+                       $this->loadWithArgv( $args );
+                       $this->processOptions();
+               }
+       }
+
+       function execute() {
+               $this->processOptions();
+               $this->dump( true );
+       }
+
+       function processOptions() {
+               global $IP;
+
+               parent::processOptions();
+
+               if ( $this->hasOption( 'buffersize' ) ) {
+                       $this->bufferSize = max( intval( $this->getOption( 'buffersize' ) ), 4 * 1024 );
+               }
+
+               if ( $this->hasOption( 'prefetch' ) ) {
+                       require_once "$IP/maintenance/backupPrefetch.inc";
+                       $url = $this->processFileOpt( $this->getOption( 'prefetch' ) );
+                       $this->prefetch = new BaseDump( $url );
+               }
+
+               if ( $this->hasOption( 'stub' ) ) {
+                       $this->input = $this->processFileOpt( $this->getOption( 'stub' ) );
+               }
+
+               if ( $this->hasOption( 'maxtime' ) ) {
+                       $this->maxTimeAllowed = intval( $this->getOption( 'maxtime' ) ) * 60;
+               }
+
+               if ( $this->hasOption( 'checkpointfile' ) ) {
+                       $this->checkpointFiles = $this->getOption( 'checkpointfile' );
+               }
+
+               if ( $this->hasOption( 'current' ) ) {
+                       $this->history = WikiExporter::CURRENT;
+               }
+
+               if ( $this->hasOption( 'full' ) ) {
+                       $this->history = WikiExporter::FULL;
+               }
+
+               if ( $this->hasOption( 'spawn' ) ) {
+                       $this->spawn = true;
+                       $val = $this->getOption( 'spawn' );
+                       if ( $val !== 1 ) {
+                               $this->php = $val;
+                       }
+               }
+       }
+
+       /**
+        * Drop the database connection $this->db and try to get a new one.
+        *
+        * This function tries to get a /different/ connection if this is
+        * possible. Hence, (if this is possible) it switches to a different
+        * failover upon each call.
+        *
+        * This function resets $this->lb and closes all connections on it.
+        *
+        * @throws MWException
+        */
+       function rotateDb() {
+               // Cleaning up old connections
+               if ( isset( $this->lb ) ) {
+                       $this->lb->closeAll();
+                       unset( $this->lb );
+               }
+
+               if ( $this->forcedDb !== null ) {
+                       $this->db = $this->forcedDb;
+
+                       return;
+               }
+
+               if ( isset( $this->db ) && $this->db->isOpen() ) {
+                       throw new MWException( 'DB is set and has not been closed by the Load Balancer' );
+               }
+
+               unset( $this->db );
+
+               // Trying to set up new connection.
+               // We do /not/ retry upon failure, but delegate to encapsulating logic, to avoid
+               // individually retrying at different layers of code.
+
+               // 1. The LoadBalancer.
+               try {
+                       $this->lb = wfGetLBFactory()->newMainLB();
+               } catch ( Exception $e ) {
+                       throw new MWException( __METHOD__
+                               . " rotating DB failed to obtain new load balancer (" . $e->getMessage() . ")" );
+               }
+
+               // 2. The Connection, through the load balancer.
+               try {
+                       $this->db = $this->lb->getConnection( DB_SLAVE, 'dump' );
+               } catch ( Exception $e ) {
+                       throw new MWException( __METHOD__
+                               . " rotating DB failed to obtain new database (" . $e->getMessage() . ")" );
+               }
+       }
+
+       function initProgress( $history = WikiExporter::FULL ) {
+               parent::initProgress();
+               $this->timeOfCheckpoint = $this->startTime;
+       }
+
+       function dump( $history, $text = WikiExporter::TEXT ) {
+               // Notice messages will foul up your XML output even if they're
+               // relatively harmless.
+               if ( ini_get( 'display_errors' ) ) {
+                       ini_set( 'display_errors', 'stderr' );
+               }
+
+               $this->initProgress( $this->history );
+
+               // We are trying to get an initial database connection to avoid that the
+               // first try of this request's first call to getText fails. However, if
+               // obtaining a good DB connection fails it's not a serious issue, as
+               // getText does retry upon failure and can start without having a working
+               // DB connection.
+               try {
+                       $this->rotateDb();
+               } catch ( Exception $e ) {
+                       // We do not even count this as failure. Just let eventual
+                       // watchdogs know.
+                       $this->progress( "Getting initial DB connection failed (" .
+                               $e->getMessage() . ")" );
+               }
+
+               $this->egress = new ExportProgressFilter( $this->sink, $this );
+
+               // it would be nice to do it in the constructor, oh well. need egress set
+               $this->finalOptionCheck();
+
+               // we only want this so we know how to close a stream :-P
+               $this->xmlwriterobj = new XmlDumpWriter();
+
+               $input = fopen( $this->input, "rt" );
+               $this->readDump( $input );
+
+               if ( $this->spawnProc ) {
+                       $this->closeSpawn();
+               }
+
+               $this->report( true );
+       }
+
+       function processFileOpt( $opt ) {
+               $split = explode( ':', $opt, 2 );
+               $val = $split[0];
+               $param = '';
+               if ( count( $split ) === 2 ) {
+                       $param = $split[1];
+               }
+               $fileURIs = explode( ';', $param );
+               foreach ( $fileURIs as $URI ) {
+                       switch ( $val ) {
+                               case "file":
+                                       $newURI = $URI;
+                                       break;
+                               case "gzip":
+                                       $newURI = "compress.zlib://$URI";
+                                       break;
+                               case "bzip2":
+                                       $newURI = "compress.bzip2://$URI";
+                                       break;
+                               case "7zip":
+                                       $newURI = "mediawiki.compress.7z://$URI";
+                                       break;
+                               default:
+                                       $newURI = $URI;
+                       }
+                       $newFileURIs[] = $newURI;
+               }
+               $val = implode( ';', $newFileURIs );
+
+               return $val;
+       }
+
+       /**
+        * Overridden to include prefetch ratio if enabled.
+        */
+       function showReport() {
+               if ( !$this->prefetch ) {
+                       parent::showReport();
+
+                       return;
+               }
+
+               if ( $this->reporting ) {
+                       $now = wfTimestamp( TS_DB );
+                       $nowts = microtime( true );
+                       $deltaAll = $nowts - $this->startTime;
+                       $deltaPart = $nowts - $this->lastTime;
+                       $this->pageCountPart = $this->pageCount - $this->pageCountLast;
+                       $this->revCountPart = $this->revCount - $this->revCountLast;
+
+                       if ( $deltaAll ) {
+                               $portion = $this->revCount / $this->maxCount;
+                               $eta = $this->startTime + $deltaAll / $portion;
+                               $etats = wfTimestamp( TS_DB, intval( $eta ) );
+                               if ( $this->fetchCount ) {
+                                       $fetchRate = 100.0 * $this->prefetchCount / $this->fetchCount;
+                               } else {
+                                       $fetchRate = '-';
+                               }
+                               $pageRate = $this->pageCount / $deltaAll;
+                               $revRate = $this->revCount / $deltaAll;
+                       } else {
+                               $pageRate = '-';
+                               $revRate = '-';
+                               $etats = '-';
+                               $fetchRate = '-';
+                       }
+                       if ( $deltaPart ) {
+                               if ( $this->fetchCountLast ) {
+                                       $fetchRatePart = 100.0 * $this->prefetchCountLast / $this->fetchCountLast;
+                               } else {
+                                       $fetchRatePart = '-';
+                               }
+                               $pageRatePart = $this->pageCountPart / $deltaPart;
+                               $revRatePart = $this->revCountPart / $deltaPart;
+                       } else {
+                               $fetchRatePart = '-';
+                               $pageRatePart = '-';
+                               $revRatePart = '-';
+                       }
+                       $this->progress( sprintf(
+                               "%s: %s (ID %d) %d pages (%0.1f|%0.1f/sec all|curr), "
+                                       . "%d revs (%0.1f|%0.1f/sec all|curr), %0.1f%%|%0.1f%% "
+                                       . "prefetched (all|curr), ETA %s [max %d]",
+                               $now, wfWikiID(), $this->ID, $this->pageCount, $pageRate,
+                               $pageRatePart, $this->revCount, $revRate, $revRatePart,
+                               $fetchRate, $fetchRatePart, $etats, $this->maxCount
+                       ) );
+                       $this->lastTime = $nowts;
+                       $this->revCountLast = $this->revCount;
+                       $this->prefetchCountLast = $this->prefetchCount;
+                       $this->fetchCountLast = $this->fetchCount;
+               }
+       }
+
+       function setTimeExceeded() {
+               $this->timeExceeded = true;
+       }
+
+       function checkIfTimeExceeded() {
+               if ( $this->maxTimeAllowed
+                       && ( $this->lastTime - $this->timeOfCheckpoint > $this->maxTimeAllowed )
+               ) {
+                       return true;
+               }
+
+               return false;
+       }
+
+       function finalOptionCheck() {
+               if ( ( $this->checkpointFiles && !$this->maxTimeAllowed )
+                       || ( $this->maxTimeAllowed && !$this->checkpointFiles )
+               ) {
+                       throw new MWException( "Options checkpointfile and maxtime must be specified together.\n" );
+               }
+               foreach ( $this->checkpointFiles as $checkpointFile ) {
+                       $count = substr_count( $checkpointFile, "%s" );
+                       if ( $count != 2 ) {
+                               throw new MWException( "Option checkpointfile must contain two '%s' "
+                                       . "for substitution of first and last pageids, count is $count instead, "
+                                       . "file is $checkpointFile.\n" );
+                       }
+               }
+
+               if ( $this->checkpointFiles ) {
+                       $filenameList = (array)$this->egress->getFilenames();
+                       if ( count( $filenameList ) != count( $this->checkpointFiles ) ) {
+                               throw new MWException( "One checkpointfile must be specified "
+                                       . "for each output option, if maxtime is used.\n" );
+                       }
+               }
+       }
+
+       /**
+        * @throws MWException Failure to parse XML input
+        * @param string $input
+        * @return bool
+        */
+       function readDump( $input ) {
+               $this->buffer = "";
+               $this->openElement = false;
+               $this->atStart = true;
+               $this->state = "";
+               $this->lastName = "";
+               $this->thisPage = 0;
+               $this->thisRev = 0;
+               $this->thisRevModel = null;
+               $this->thisRevFormat = null;
+
+               $parser = xml_parser_create( "UTF-8" );
+               xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+
+               xml_set_element_handler(
+                       $parser,
+                       array( &$this, 'startElement' ),
+                       array( &$this, 'endElement' )
+               );
+               xml_set_character_data_handler( $parser, array( &$this, 'characterData' ) );
+
+               $offset = 0; // for context extraction on error reporting
+               do {
+                       if ( $this->checkIfTimeExceeded() ) {
+                               $this->setTimeExceeded();
+                       }
+                       $chunk = fread( $input, $this->bufferSize );
+                       if ( !xml_parse( $parser, $chunk, feof( $input ) ) ) {
+                               wfDebug( "TextDumpPass::readDump encountered XML parsing error\n" );
+
+                               $byte = xml_get_current_byte_index( $parser );
+                               $msg = wfMessage( 'xml-error-string',
+                                       'XML import parse failure',
+                                       xml_get_current_line_number( $parser ),
+                                       xml_get_current_column_number( $parser ),
+                                       $byte . ( is_null( $chunk ) ? null : ( '; "' . substr( $chunk, $byte - $offset, 16 ) . '"' ) ),
+                                       xml_error_string( xml_get_error_code( $parser ) ) )->escaped();
+
+                               xml_parser_free( $parser );
+
+                               throw new MWException( $msg );
+                       }
+                       $offset += strlen( $chunk );
+               } while ( $chunk !== false && !feof( $input ) );
+               if ( $this->maxTimeAllowed ) {
+                       $filenameList = (array)$this->egress->getFilenames();
+                       // we wrote some stuff after last checkpoint that needs renamed
+                       if ( file_exists( $filenameList[0] ) ) {
+                               $newFilenames = array();
+                               # we might have just written the header and footer and had no
+                               # pages or revisions written... perhaps they were all deleted
+                               # there's no pageID 0 so we use that. the caller is responsible
+                               # for deciding what to do with a file containing only the
+                               # siteinfo information and the mw tags.
+                               if ( !$this->firstPageWritten ) {
+                                       $firstPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
+                                       $lastPageID = str_pad( 0, 9, "0", STR_PAD_LEFT );
+                               } else {
+                                       $firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
+                                       $lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
+                               }
+
+                               $filenameCount = count( $filenameList );
+                               for ( $i = 0; $i < $filenameCount; $i++ ) {
+                                       $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
+                                       $fileinfo = pathinfo( $filenameList[$i] );
+                                       $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
+                               }
+                               $this->egress->closeAndRename( $newFilenames );
+                       }
+               }
+               xml_parser_free( $parser );
+
+               return true;
+       }
+
+       /**
+        * Applies applicable export transformations to $text.
+        *
+        * @param string $text
+        * @param string $model
+        * @param string|null $format
+        *
+        * @return string
+        */
+       private function exportTransform( $text, $model, $format = null ) {
+               try {
+                       $handler = ContentHandler::getForModelID( $model );
+                       $text = $handler->exportTransform( $text, $format );
+               }
+               catch ( MWException $ex ) {
+                       $this->progress(
+                               "Unable to apply export transformation for content model '$model': " .
+                               $ex->getMessage()
+                       );
+               }
 
-Usage: php dumpTextPass.php [<options>]
-Options:
-  --stub=<type>:<file> To load a compressed stub dump instead of stdin
-  --prefetch=<type>:<file> Use a prior dump file as a text source, to save
-                         pressure on the database.
-                         (Requires the XMLReader extension)
-  --maxtime=<minutes> Write out checkpoint file after this many minutes (writing
-                 out complete page, closing xml file properly, and opening new one
-                 with header).  This option requires the checkpointfile option.
-  --checkpointfile=<filenamepattern> Use this string for checkpoint filenames,
-                     substituting first pageid written for the first %s (required) and the
-              last pageid written for the second %s if it exists.
-  --quiet        Don't dump status reports to stderr.
-  --report=n  Report position and speed after every n pages processed.
-                         (Default: 100)
-  --server=h  Force reading from MySQL server h
-  --current      Base ETA on number of pages in database instead of all revisions
-  --spawn        Spawn a subprocess for loading text records
-  --buffersize=<size> Buffer size in bytes to use for reading the stub.
-              (Default: 512KB, Minimum: 4KB)
-  --help      Display this help message
-ENDS
-       );
+               return $text;
+       }
+
+       /**
+        * Tries to get the revision text for a revision id.
+        * Export transformations are applied if the content model can is given or can be
+        * determined from the database.
+        *
+        * Upon errors, retries (Up to $this->maxFailures tries each call).
+        * If still no good revision get could be found even after this retrying, "" is returned.
+        * If no good revision text could be returned for
+        * $this->maxConsecutiveFailedTextRetrievals consecutive calls to getText, MWException
+        * is thrown.
+        *
+        * @param string $id The revision id to get the text for
+        * @param string|bool|null $model The content model used to determine
+        *  applicable export transformations.
+        *  If $model is null, it will be determined from the database.
+        * @param string|null $format The content format used when applying export transformations.
+        *
+        * @throws MWException
+        * @return string The revision text for $id, or ""
+        */
+       function getText( $id, $model = null, $format = null ) {
+               global $wgContentHandlerUseDB;
+
+               $prefetchNotTried = true; // Whether or not we already tried to get the text via prefetch.
+               $text = false; // The candidate for a good text. false if no proper value.
+               $failures = 0; // The number of times, this invocation of getText already failed.
+
+               // The number of times getText failed without yielding a good text in between.
+               static $consecutiveFailedTextRetrievals = 0;
+
+               $this->fetchCount++;
+
+               // To allow to simply return on success and do not have to worry about book keeping,
+               // we assume, this fetch works (possible after some retries). Nevertheless, we koop
+               // the old value, so we can restore it, if problems occur (See after the while loop).
+               $oldConsecutiveFailedTextRetrievals = $consecutiveFailedTextRetrievals;
+               $consecutiveFailedTextRetrievals = 0;
+
+               if ( $model === null && $wgContentHandlerUseDB ) {
+                       $row = $this->db->selectRow(
+                               'revision',
+                               array( 'rev_content_model', 'rev_content_format' ),
+                               array( 'rev_id' => $this->thisRev ),
+                               __METHOD__
+                       );
+
+                       if ( $row ) {
+                               $model = $row->rev_content_model;
+                               $format = $row->rev_content_format;
+                       }
+               }
+
+               if ( $model === null || $model === '' ) {
+                       $model = false;
+               }
+
+               while ( $failures < $this->maxFailures ) {
+
+                       // As soon as we found a good text for the $id, we will return immediately.
+                       // Hence, if we make it past the try catch block, we know that we did not
+                       // find a good text.
+
+                       try {
+                               // Step 1: Get some text (or reuse from previous iteratuon if checking
+                               //         for plausibility failed)
+
+                               // Trying to get prefetch, if it has not been tried before
+                               if ( $text === false && isset( $this->prefetch ) && $prefetchNotTried ) {
+                                       $prefetchNotTried = false;
+                                       $tryIsPrefetch = true;
+                                       $text = $this->prefetch->prefetch( intval( $this->thisPage ),
+                                               intval( $this->thisRev ) );
+
+                                       if ( $text === null ) {
+                                               $text = false;
+                                       }
+
+                                       if ( is_string( $text ) && $model !== false ) {
+                                               // Apply export transformation to text coming from an old dump.
+                                               // The purpose of this transformation is to convert up from legacy
+                                               // formats, which may still be used in the older dump that is used
+                                               // for pre-fetching. Applying the transformation again should not
+                                               // interfere with content that is already in the correct form.
+                                               $text = $this->exportTransform( $text, $model, $format );
+                                       }
+                               }
+
+                               if ( $text === false ) {
+                                       // Fallback to asking the database
+                                       $tryIsPrefetch = false;
+                                       if ( $this->spawn ) {
+                                               $text = $this->getTextSpawned( $id );
+                                       } else {
+                                               $text = $this->getTextDb( $id );
+                                       }
+
+                                       if ( $text !== false && $model !== false ) {
+                                               // Apply export transformation to text coming from the database.
+                                               // Prefetched text should already have transformations applied.
+                                               $text = $this->exportTransform( $text, $model, $format );
+                                       }
+
+                                       // No more checks for texts from DB for now.
+                                       // If we received something that is not false,
+                                       // We treat it as good text, regardless of whether it actually is or is not
+                                       if ( $text !== false ) {
+                                               return $text;
+                                       }
+                               }
+
+                               if ( $text === false ) {
+                                       throw new MWException( "Generic error while obtaining text for id " . $id );
+                               }
+
+                               // We received a good candidate for the text of $id via some method
+
+                               // Step 2: Checking for plausibility and return the text if it is
+                               //         plausible
+                               $revID = intval( $this->thisRev );
+                               if ( !isset( $this->db ) ) {
+                                       throw new MWException( "No database available" );
+                               }
+
+                               if ( $model !== CONTENT_MODEL_WIKITEXT ) {
+                                       $revLength = strlen( $text );
+                               } else {
+                                       $revLength = $this->db->selectField( 'revision', 'rev_len', array( 'rev_id' => $revID ) );
+                               }
+
+                               if ( strlen( $text ) == $revLength ) {
+                                       if ( $tryIsPrefetch ) {
+                                               $this->prefetchCount++;
+                                       }
+
+                                       return $text;
+                               }
+
+                               $text = false;
+                               throw new MWException( "Received text is unplausible for id " . $id );
+                       } catch ( Exception $e ) {
+                               $msg = "getting/checking text " . $id . " failed (" . $e->getMessage() . ")";
+                               if ( $failures + 1 < $this->maxFailures ) {
+                                       $msg .= " (Will retry " . ( $this->maxFailures - $failures - 1 ) . " more times)";
+                               }
+                               $this->progress( $msg );
+                       }
+
+                       // Something went wrong; we did not a text that was plausible :(
+                       $failures++;
+
+                       // A failure in a prefetch hit does not warrant resetting db connection etc.
+                       if ( !$tryIsPrefetch ) {
+                               // After backing off for some time, we try to reboot the whole process as
+                               // much as possible to not carry over failures from one part to the other
+                               // parts
+                               sleep( $this->failureTimeout );
+                               try {
+                                       $this->rotateDb();
+                                       if ( $this->spawn ) {
+                                               $this->closeSpawn();
+                                               $this->openSpawn();
+                                       }
+                               } catch ( Exception $e ) {
+                                       $this->progress( "Rebooting getText infrastructure failed (" . $e->getMessage() . ")" .
+                                               " Trying to continue anyways" );
+                               }
+                       }
+               }
+
+               // Retirieving a good text for $id failed (at least) maxFailures times.
+               // We abort for this $id.
+
+               // Restoring the consecutive failures, and maybe aborting, if the dump
+               // is too broken.
+               $consecutiveFailedTextRetrievals = $oldConsecutiveFailedTextRetrievals + 1;
+               if ( $consecutiveFailedTextRetrievals > $this->maxConsecutiveFailedTextRetrievals ) {
+                       throw new MWException( "Graceful storage failure" );
+               }
+
+               return "";
+       }
+
+       /**
+        * May throw a database error if, say, the server dies during query.
+        * @param int $id
+        * @return bool|string
+        * @throws MWException
+        */
+       private function getTextDb( $id ) {
+               global $wgContLang;
+               if ( !isset( $this->db ) ) {
+                       throw new MWException( __METHOD__ . "No database available" );
+               }
+               $row = $this->db->selectRow( 'text',
+                       array( 'old_text', 'old_flags' ),
+                       array( 'old_id' => $id ),
+                       __METHOD__ );
+               $text = Revision::getRevisionText( $row );
+               if ( $text === false ) {
+                       return false;
+               }
+               $stripped = str_replace( "\r", "", $text );
+               $normalized = $wgContLang->normalize( $stripped );
+
+               return $normalized;
+       }
+
+       private function getTextSpawned( $id ) {
+               MediaWiki\suppressWarnings();
+               if ( !$this->spawnProc ) {
+                       // First time?
+                       $this->openSpawn();
+               }
+               $text = $this->getTextSpawnedOnce( $id );
+               MediaWiki\restoreWarnings();
+
+               return $text;
+       }
+
+       function openSpawn() {
+               global $IP;
+
+               if ( file_exists( "$IP/../multiversion/MWScript.php" ) ) {
+                       $cmd = implode( " ",
+                               array_map( 'wfEscapeShellArg',
+                                       array(
+                                               $this->php,
+                                               "$IP/../multiversion/MWScript.php",
+                                               "fetchText.php",
+                                               '--wiki', wfWikiID() ) ) );
+               } else {
+                       $cmd = implode( " ",
+                               array_map( 'wfEscapeShellArg',
+                                       array(
+                                               $this->php,
+                                               "$IP/maintenance/fetchText.php",
+                                               '--wiki', wfWikiID() ) ) );
+               }
+               $spec = array(
+                       0 => array( "pipe", "r" ),
+                       1 => array( "pipe", "w" ),
+                       2 => array( "file", "/dev/null", "a" ) );
+               $pipes = array();
+
+               $this->progress( "Spawning database subprocess: $cmd" );
+               $this->spawnProc = proc_open( $cmd, $spec, $pipes );
+               if ( !$this->spawnProc ) {
+                       $this->progress( "Subprocess spawn failed." );
+
+                       return false;
+               }
+               list(
+                       $this->spawnWrite, // -> stdin
+                       $this->spawnRead, // <- stdout
+               ) = $pipes;
+
+               return true;
+       }
+
+       private function closeSpawn() {
+               MediaWiki\suppressWarnings();
+               if ( $this->spawnRead ) {
+                       fclose( $this->spawnRead );
+               }
+               $this->spawnRead = false;
+               if ( $this->spawnWrite ) {
+                       fclose( $this->spawnWrite );
+               }
+               $this->spawnWrite = false;
+               if ( $this->spawnErr ) {
+                       fclose( $this->spawnErr );
+               }
+               $this->spawnErr = false;
+               if ( $this->spawnProc ) {
+                       pclose( $this->spawnProc );
+               }
+               $this->spawnProc = false;
+               MediaWiki\restoreWarnings();
+       }
+
+       private function getTextSpawnedOnce( $id ) {
+               global $wgContLang;
+
+               $ok = fwrite( $this->spawnWrite, "$id\n" );
+               // $this->progress( ">> $id" );
+               if ( !$ok ) {
+                       return false;
+               }
+
+               $ok = fflush( $this->spawnWrite );
+               // $this->progress( ">> [flush]" );
+               if ( !$ok ) {
+                       return false;
+               }
+
+               // check that the text id they are sending is the one we asked for
+               // this avoids out of sync revision text errors we have encountered in the past
+               $newId = fgets( $this->spawnRead );
+               if ( $newId === false ) {
+                       return false;
+               }
+               if ( $id != intval( $newId ) ) {
+                       return false;
+               }
+
+               $len = fgets( $this->spawnRead );
+               // $this->progress( "<< " . trim( $len ) );
+               if ( $len === false ) {
+                       return false;
+               }
+
+               $nbytes = intval( $len );
+               // actual error, not zero-length text
+               if ( $nbytes < 0 ) {
+                       return false;
+               }
+
+               $text = "";
+
+               // Subprocess may not send everything at once, we have to loop.
+               while ( $nbytes > strlen( $text ) ) {
+                       $buffer = fread( $this->spawnRead, $nbytes - strlen( $text ) );
+                       if ( $buffer === false ) {
+                               break;
+                       }
+                       $text .= $buffer;
+               }
+
+               $gotbytes = strlen( $text );
+               if ( $gotbytes != $nbytes ) {
+                       $this->progress( "Expected $nbytes bytes from database subprocess, got $gotbytes " );
+
+                       return false;
+               }
+
+               // Do normalization in the dump thread...
+               $stripped = str_replace( "\r", "", $text );
+               $normalized = $wgContLang->normalize( $stripped );
+
+               return $normalized;
+       }
+
+       function startElement( $parser, $name, $attribs ) {
+               $this->checkpointJustWritten = false;
+
+               $this->clearOpenElement( null );
+               $this->lastName = $name;
+
+               if ( $name == 'revision' ) {
+                       $this->state = $name;
+                       $this->egress->writeOpenPage( null, $this->buffer );
+                       $this->buffer = "";
+               } elseif ( $name == 'page' ) {
+                       $this->state = $name;
+                       if ( $this->atStart ) {
+                               $this->egress->writeOpenStream( $this->buffer );
+                               $this->buffer = "";
+                               $this->atStart = false;
+                       }
+               }
+
+               if ( $name == "text" && isset( $attribs['id'] ) ) {
+                       $id = $attribs['id'];
+                       $model = trim( $this->thisRevModel );
+                       $format = trim( $this->thisRevFormat );
+
+                       $model = $model === '' ? null : $model;
+                       $format = $format === '' ? null : $format;
+
+                       $text = $this->getText( $id, $model, $format );
+                       $this->openElement = array( $name, array( 'xml:space' => 'preserve' ) );
+                       if ( strlen( $text ) > 0 ) {
+                               $this->characterData( $parser, $text );
+                       }
+               } else {
+                       $this->openElement = array( $name, $attribs );
+               }
+       }
+
+       function endElement( $parser, $name ) {
+               $this->checkpointJustWritten = false;
+
+               if ( $this->openElement ) {
+                       $this->clearOpenElement( "" );
+               } else {
+                       $this->buffer .= "</$name>";
+               }
+
+               if ( $name == 'revision' ) {
+                       $this->egress->writeRevision( null, $this->buffer );
+                       $this->buffer = "";
+                       $this->thisRev = "";
+                       $this->thisRevModel = null;
+                       $this->thisRevFormat = null;
+               } elseif ( $name == 'page' ) {
+                       if ( !$this->firstPageWritten ) {
+                               $this->firstPageWritten = trim( $this->thisPage );
+                       }
+                       $this->lastPageWritten = trim( $this->thisPage );
+                       if ( $this->timeExceeded ) {
+                               $this->egress->writeClosePage( $this->buffer );
+                               // nasty hack, we can't just write the chardata after the
+                               // page tag, it will include leading blanks from the next line
+                               $this->egress->sink->write( "\n" );
+
+                               $this->buffer = $this->xmlwriterobj->closeStream();
+                               $this->egress->writeCloseStream( $this->buffer );
+
+                               $this->buffer = "";
+                               $this->thisPage = "";
+                               // this could be more than one file if we had more than one output arg
+
+                               $filenameList = (array)$this->egress->getFilenames();
+                               $newFilenames = array();
+                               $firstPageID = str_pad( $this->firstPageWritten, 9, "0", STR_PAD_LEFT );
+                               $lastPageID = str_pad( $this->lastPageWritten, 9, "0", STR_PAD_LEFT );
+                               $filenamesCount = count( $filenameList );
+                               for ( $i = 0; $i < $filenamesCount; $i++ ) {
+                                       $checkpointNameFilledIn = sprintf( $this->checkpointFiles[$i], $firstPageID, $lastPageID );
+                                       $fileinfo = pathinfo( $filenameList[$i] );
+                                       $newFilenames[] = $fileinfo['dirname'] . '/' . $checkpointNameFilledIn;
+                               }
+                               $this->egress->closeRenameAndReopen( $newFilenames );
+                               $this->buffer = $this->xmlwriterobj->openStream();
+                               $this->timeExceeded = false;
+                               $this->timeOfCheckpoint = $this->lastTime;
+                               $this->firstPageWritten = false;
+                               $this->checkpointJustWritten = true;
+                       } else {
+                               $this->egress->writeClosePage( $this->buffer );
+                               $this->buffer = "";
+                               $this->thisPage = "";
+                       }
+               } elseif ( $name == 'mediawiki' ) {
+                       $this->egress->writeCloseStream( $this->buffer );
+                       $this->buffer = "";
+               }
+       }
+
+       function characterData( $parser, $data ) {
+               $this->clearOpenElement( null );
+               if ( $this->lastName == "id" ) {
+                       if ( $this->state == "revision" ) {
+                               $this->thisRev .= $data;
+                       } elseif ( $this->state == "page" ) {
+                               $this->thisPage .= $data;
+                       }
+               } elseif ( $this->lastName == "model" ) {
+                       $this->thisRevModel .= $data;
+               } elseif ( $this->lastName == "format" ) {
+                       $this->thisRevFormat .= $data;
+               }
+
+               // have to skip the newline left over from closepagetag line of
+               // end of checkpoint files. nasty hack!!
+               if ( $this->checkpointJustWritten ) {
+                       if ( $data[0] == "\n" ) {
+                               $data = substr( $data, 1 );
+                       }
+                       $this->checkpointJustWritten = false;
+               }
+               $this->buffer .= htmlspecialchars( $data );
+       }
+
+       function clearOpenElement( $style ) {
+               if ( $this->openElement ) {
+                       $this->buffer .= Xml::element( $this->openElement[0], $this->openElement[1], $style );
+                       $this->openElement = false;
+               }
+       }
 }
+
+$maintClass = 'TextPassDumper';
+require_once RUN_MAINTENANCE_IF_MAIN;
index 9d53f07..026ac02 100644 (file)
@@ -76,7 +76,7 @@ By default, outputs relative paths against the parent directory of \$wgUploadDir
         * @param bool $shared True to pass shared-dir settings to hash func
         */
        function fetchUsed( $shared ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $image = $dbr->tableName( 'image' );
                $imagelinks = $dbr->tableName( 'imagelinks' );
 
@@ -97,7 +97,7 @@ By default, outputs relative paths against the parent directory of \$wgUploadDir
         * @param bool $shared True to pass shared-dir settings to hash func
         */
        function fetchLocal( $shared ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $result = $dbr->select( 'image',
                        array( 'img_name' ),
                        '',
index 8fdcef3..69a95e2 100644 (file)
@@ -55,7 +55,7 @@ class EraseArchivedFile extends Maintenance {
                        }
                        $afile = false;
                } else { // specified version
-                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw = $this->getDB( DB_MASTER );
                        $row = $dbw->selectRow( 'filearchive', '*',
                                array( 'fa_storage_group' => 'deleted', 'fa_storage_key' => $filekey ),
                                __METHOD__ );
@@ -85,7 +85,7 @@ class EraseArchivedFile extends Maintenance {
        }
 
        protected function scrubAllVersions( $name ) {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $res = $dbw->select( 'filearchive', '*',
                        array( 'fa_name' => $name, 'fa_storage_group' => 'deleted' ),
                        __METHOD__ );
index 983b772..cf12838 100644 (file)
@@ -48,7 +48,7 @@ class FetchText extends Maintenance {
         * note that the text string itself is *not* followed by newline
         */
        public function execute() {
-               $db = wfGetDB( DB_SLAVE );
+               $db = $this->getDB( DB_SLAVE );
                $stdin = $this->getStdin();
                while ( !feof( $stdin ) ) {
                        $line = fgets( $stdin );
index cbb1d5b..8c7e242 100644 (file)
@@ -117,7 +117,6 @@ class DeprecatedInterfaceFinder extends FileAwareNodeVisitor {
        }
 }
 
-
 /**
  * Maintenance task that recursively scans MediaWiki PHP files for deprecated
  * functions and interfaces and produces a report.
index 1265891..25ec342 100644 (file)
@@ -47,7 +47,7 @@ class FixDefaultJsonContentPages extends LoggedUpdateMaintenance {
                        return true;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $namespaces = array(
                        NS_MEDIAWIKI => $dbr->buildLike( $dbr->anyString(), '.json' ),
                        NS_USER => $dbr->buildLike( $dbr->anyString(), '/', $dbr->anyString(), '.json' ),
@@ -80,7 +80,7 @@ class FixDefaultJsonContentPages extends LoggedUpdateMaintenance {
                $this->output( "Processing {$title} ({$row->page_id})...\n" );
                $rev = Revision::newFromTitle( $title );
                $content = $rev->getContent( Revision::RAW );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                if ( $content instanceof JsonContent ) {
                        if ( $content->isValid() ) {
                                // Yay, actually JSON. We need to just change the
index 9568284..ca551f8 100644 (file)
@@ -54,7 +54,7 @@ class FixDoubleRedirects extends Maintenance {
                        $title = null;
                }
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                // See also SpecialDoubleRedirects
                $tables = array(
index 0c60e62..a44f8e5 100644 (file)
@@ -47,7 +47,7 @@ class FixExtLinksProtocolRelative extends LoggedUpdateMaintenance {
        }
 
        protected function doDBUpdates() {
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
                if ( !$db->tableExists( 'externallinks' ) ) {
                        $this->error( "externallinks table does not exist" );
 
index 5431cf2..c2a748c 100644 (file)
@@ -49,7 +49,7 @@ class FixTimestamps extends Maintenance {
                $grace = 60; // maximum normal clock offset
 
                # Find bounding revision IDs
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $revisionTable = $dbw->tableName( 'revision' );
                $res = $dbw->query( "SELECT MIN(rev_id) as minrev, MAX(rev_id) as maxrev FROM $revisionTable " .
                        "WHERE rev_timestamp BETWEEN '{$start}' AND '{$end}'", __METHOD__ );
index 40e0915..d09760b 100644 (file)
@@ -37,7 +37,7 @@ class FixUserRegistration extends Maintenance {
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $lastId = 0;
                do {
index b8caa4d..d3f082d 100644 (file)
@@ -22,4 +22,3 @@ $generator->forceClassPath( 'MyLocalSettingsGenerator', "$base/mw-config/overrid
 
 // Write out the autoload
 $generator->generateAutoload( 'maintenance/generateLocalAutoload.php' );
-
index 12711ea..c40d0ce 100644 (file)
@@ -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 = wfGetDB( DB_SLAVE );
+               $this->dbr = $this->getDB( DB_SLAVE );
                $this->generateNamespaces();
                $this->timestamp = wfTimestamp( TS_ISO_8601, wfTimestampNow() );
                $this->findex = fopen( "{$this->fspath}sitemap-index-{$this->identifier}.xml", 'wb' );
index 68c1943..c858c38 100644 (file)
@@ -40,7 +40,7 @@ class GetSlaveServer extends Maintenance {
                if ( $wgAllDBsAreLocalhost ) {
                        $host = 'localhost';
                } elseif ( $this->hasOption( 'group' ) ) {
-                       $db = wfGetDB( DB_SLAVE, $this->getOption( 'group' ) );
+                       $db = $this->getDB( DB_SLAVE, $this->getOption( 'group' ) );
                        $host = $db->getServer();
                } else {
                        $lb = wfGetLB();
index 7d7c1cc..c4b8cc9 100644 (file)
@@ -39,8 +39,6 @@ class GetTextMaint extends Maintenance {
        }
 
        public function execute() {
-               $this->db = wfGetDB( DB_SLAVE );
-
                $titleText = $this->getArg( 0 );
                $title = Title::newFromText( $titleText );
                if ( !$title ) {
index 6b7cfb6..5806ffc 100644 (file)
@@ -68,6 +68,8 @@ TEXT;
                $this->addOption( 'namespaces',
                        'Import only the pages from namespaces belonging to the list of ' .
                        'pipe-separated namespace names or namespace indexes', false, true );
+               $this->addOption( 'rootpage', 'Pages will be imported as subpages of the specified page',
+                       false, true );
                $this->addOption( 'dry-run', 'Parse dump without actually importing pages' );
                $this->addOption( 'debug', 'Output extra verbose debug information' );
                $this->addOption( 'uploads', 'Process file upload data if included (experimental)' );
@@ -199,7 +201,7 @@ TEXT;
                        if ( !$this->dryRun ) {
                                // bluuuh hack
                                // call_user_func( $this->uploadCallback, $revision );
-                               $dbw = wfGetDB( DB_MASTER );
+                               $dbw = $this->getDB( DB_MASTER );
 
                                return $dbw->deadlockLoop( array( $revision, 'importUpload' ) );
                        }
@@ -285,6 +287,14 @@ TEXT;
                if ( $this->hasOption( 'no-updates' ) ) {
                        $importer->setNoUpdates( true );
                }
+               if ( $this->hasOption( 'rootpage' ) ) {
+                       $statusRootPage = $importer->setTargetRootPage( $this->getOption( 'rootpage' ) );
+                       if ( !$statusRootPage->isGood() ) {
+                               // Die here so that it doesn't print "Done!"
+                               $this->error( $statusRootPage->getMessage()->text(), 1 );
+                               return false;
+                       }
+               }
                $importer->setPageCallback( array( &$this, 'reportPage' ) );
                $this->importCallback = $importer->setRevisionCallback(
                        array( &$this, 'handleRevision' ) );
index 4b839a0..0f69f66 100644 (file)
@@ -135,4 +135,3 @@ function getFileUserFromSourceWiki( $wiki_host, $file ) {
 
        return html_entity_decode( $matches[1] );
 }
-
index a040248..701af62 100644 (file)
@@ -136,7 +136,7 @@ $count = count( $files );
 if ( $count > 0 ) {
 
        foreach ( $files as $file ) {
-               $base = wfBaseName( $file );
+               $base = UtfNormal\Validator::cleanUp( wfBaseName( $file ) );
 
                # Validate a title
                $title = Title::makeTitleSafe( NS_FILE, $base );
index 7cd2000..c5c00aa 100644 (file)
@@ -24,7 +24,6 @@ class ImportSites extends Maintenance {
                parent::__construct();
        }
 
-
        /**
         * Do the import.
         */
diff --git a/maintenance/importTextFiles.php b/maintenance/importTextFiles.php
new file mode 100644 (file)
index 0000000..14d8420
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+/**
+ * Import pages from text files
+ *
+ * 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 which reads in text files
+ * and imports their content to a page of the wiki.
+ *
+ * @ingroup Maintenance
+ */
+class ImportTextFiles extends Maintenance {
+       public function __construct() {
+               parent::__construct();
+               $this->mDescription = "Reads in text files and imports their content to pages of the wiki";
+               $this->addOption( 'user', 'Username to which edits should be attributed. ' .
+                       'Default: "Maintenance script"', false, true, 'u' );
+               $this->addOption( 'summary', 'Specify edit summary for the edits', false, true, 's' );
+               $this->addOption( 'use-timestamp', 'Use the modification date of the text file ' .
+                       'as the timestamp for the edit' );
+               $this->addOption( 'overwrite', 'Overwrite existing pages. If --use-timestamp is passed, this ' .
+                       'will only overwrite pages if the file has been modified since the page was last modified.' );
+               $this->addOption( 'prefix', 'A string to place in front of the file name', false, true, 'p' );
+               $this->addOption( 'bot', 'Mark edits as bot edits in the recent changes list.' );
+               $this->addOption( 'rc', 'Place revisions in RecentChanges.' );
+               $this->addArg( 'files', 'Files to import' );
+       }
+
+       public function execute() {
+               $userName = $this->getOption( 'user', false );
+               $summary = $this->getOption( 'summary', 'Imported from text file' );
+               $useTimestamp = $this->hasOption( 'use-timestamp' );
+               $rc = $this->hasOption( 'rc' );
+               $bot = $this->hasOption( 'bot' );
+               $overwrite = $this->hasOption( 'overwrite' );
+               $prefix = $this->getOption( 'prefix', '' );
+
+               // Get all the arguments. A loop is required since Maintenance doesn't
+               // suppport an arbitrary number of arguments.
+               $files = array();
+               $i = 0;
+               while ( $arg = $this->getArg( $i++ ) ) {
+                       if ( file_exists( $arg ) ) {
+                               $files[$arg] = file_get_contents( $arg );
+                       } else {
+                               $this->error( "Fatal error: The file '$arg' does not exist!", 1 );
+                       }
+               };
+
+               $count = count( $files );
+               $this->output( "Importing $count pages...\n" );
+
+               if ( $userName === false ) {
+                       $user = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
+               } else {
+                       $user = User::newFromName( $userName );
+               }
+
+               if ( !$user ) {
+                       $this->error( "Invalid username\n", true );
+               }
+               if ( $user->isAnon() ) {
+                       $user->addToDatabase();
+               }
+
+               $exit = 0;
+
+               $successCount = 0;
+               $failCount = 0;
+               $skipCount = 0;
+
+               foreach ( $files as $file => $text ) {
+                       $pageName = $prefix . pathinfo( $file, PATHINFO_FILENAME );
+                       $timestamp = $useTimestamp ? wfTimestamp( TS_UNIX, filemtime( $file ) ) : wfTimestampNow();
+
+                       $title = Title::newFromText( $pageName );
+                       $exists = $title->exists();
+                       $oldRevID = $title->getLatestRevID();
+                       $oldRev = $oldRevID ? Revision::newFromId( $oldRevID ) : null;
+
+                       if ( !$title ) {
+                               $this->error( "Invalid title $pageName. Skipping.\n" );
+                               $skipCount++;
+                               continue;
+                       }
+
+                       $actualTitle = $title->getPrefixedText();
+
+                       if ( $exists ) {
+                               $touched = wfTimestamp( TS_UNIX, $title->getTouched() );
+                               if ( !$overwrite ) {
+                                       $this->output( "Title $actualTitle already exists. Skipping.\n" );
+                                       $skipCount++;
+                                       continue;
+                               } elseif ( $useTimestamp && intval( $touched ) >= intval( $timestamp ) ) {
+                                       $this->output( "File for title $actualTitle has not been modified since the " .
+                                               "destination page was touched. Skipping.\n" );
+                                       $skipCount++;
+                                       continue;
+                               }
+                       }
+
+                       $rev = new WikiRevision( ConfigFactory::getDefaultInstance()->makeConfig( 'main' ) );
+                       $rev->setText( rtrim( $text ) );
+                       $rev->setTitle( $title );
+                       $rev->setUserObj( $user );
+                       $rev->setComment( $summary );
+                       $rev->setTimestamp( $timestamp );
+
+                       if ( $exists && $overwrite && $rev->getContent()->equals( $oldRev->getContent() ) ) {
+                               $this->output( "File for title $actualTitle contains no changes from the current " .
+                                       "revision. Skipping.\n" );
+                               $skipCount++;
+                               continue;
+                       }
+
+                       $status = $rev->importOldRevision();
+                       $newId = $title->getLatestRevID();
+
+                       if ( $status ) {
+                               $action = $exists ? 'updated' : 'created';
+                               $this->output( "Successfully $action $actualTitle\n" );
+                               $successCount++;
+                       } else {
+                               $action = $exists ? 'update' : 'create';
+                               $this->output( "Failed to $action $actualTitle\n" );
+                               $failCount++;
+                               $exit = 1;
+                       }
+
+                       // Create the RecentChanges entry if necessary
+                       if ( $rc && $status ) {
+                               if ( $exists ) {
+                                       if ( is_object( $oldRev ) ) {
+                                               $oldContent = $oldRev->getContent();
+                                               RecentChange::notifyEdit(
+                                                       $timestamp,
+                                                       $title,
+                                                       $rev->getMinor(),
+                                                       $user,
+                                                       $summary,
+                                                       $oldRevID,
+                                                       $oldRev->getTimestamp(),
+                                                       $bot,
+                                                       '',
+                                                       $oldContent ? $oldContent->getSize() : 0,
+                                                       $rev->getContent()->getSize(),
+                                                       $newId,
+                                                       1 /* the pages don't need to be patrolled */
+                                               );
+                                       }
+                               } else {
+                                       RecentChange::notifyNew(
+                                               $timestamp,
+                                               $title,
+                                               $rev->getMinor(),
+                                               $user,
+                                               $summary,
+                                               $bot,
+                                               '',
+                                               $rev->getContent()->getSize(),
+                                               $newId,
+                                               1
+                                       );
+                               }
+                       }
+               }
+
+               $this->output( "Done! $successCount succeeded, $skipCount skipped.\n" );
+               if ( $exit ) {
+                       $this->error( "Import failed with $failCount failed pages.\n", $exit );
+               }
+       }
+}
+
+$maintClass = "ImportTextFiles";
+require_once RUN_MAINTENANCE_IF_MAIN;
index 7c6e7d4..dee5db8 100644 (file)
@@ -39,7 +39,7 @@ in the load balancer, usually indicating a replication environment.' );
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $user = $dbw->tableName( 'user' );
                $revision = $dbw->tableName( 'revision' );
 
@@ -58,7 +58,7 @@ in the load balancer, usually indicating a replication environment.' );
                if ( $backgroundMode ) {
                        $this->output( "Using replication-friendly background mode...\n" );
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_SLAVE );
                        $chunkSize = 100;
                        $lastUser = $dbr->selectField( 'user', 'MAX(user_id)', '', __METHOD__ );
 
index cac33ec..8d26063 100644 (file)
@@ -67,7 +67,7 @@ class InitSiteStats extends Maintenance {
 
                if ( $this->hasOption( 'active' ) ) {
                        $this->output( "\nCounting and updating active users..." );
-                       $active = SiteStatsUpdate::cacheUpdate( wfGetDB( DB_MASTER ) );
+                       $active = SiteStatsUpdate::cacheUpdate( $this->getDB( DB_MASTER ) );
                        $this->output( "{$active}\n" );
                }
 
index 86de029..05f091f 100644 (file)
@@ -40,6 +40,7 @@ U+05347升|U+05347升|U+06607昇|U+0965E陞|
 U+0535C卜|U+0535C卜|U+08514蔔|
 U+05360占|U+05360占|U+04F54佔|
 U+05364卤|U+09E75鹵|U+06EF7滷|
+U+05367卧|U+081E5臥|
 U+05377卷|U+05377卷|U+06372捲|
 U+05382厂|U+05EE0廠|U+05382厂|
 U+05386历|U+06B77歷|U+066C6曆|U+053A4厤|
index 7f94ede..944e20e 100644 (file)
 解析度      分辨率
 伺服器      服务器
 區域網      局域网
+區域網路   局域网络
 巨集 宏
 掃瞄器      扫描仪
 寬頻 宽带
 柯德莉·夏萍      奥黛丽·赫本
 華勒沙      瓦文萨
 華里沙      瓦文萨
+賓拉登      本拉登
+賓·拉登    本·拉登
 歐巴馬      奥巴马
 北韓 北朝鲜
 寮人民民主共和國       老挝人民民主共和国
@@ -2592,7 +2595,6 @@ A型肝炎        甲型肝炎
 大英國協   英联邦
 共和联邦   英联邦
 阿布達比   阿布扎比
-蓋曼群島   开曼群岛
 柴契爾      撒切尔
 戴卓爾      撒切尔
 凱薩琳      凯瑟琳
@@ -2609,9 +2611,9 @@ A型肝炎        甲型肝炎
 數位電視   数字电视
 數碼電視   数字电视
 數位技術   数字技术
-數碼技術   数字技术
 數位訊號   数字信号
 數碼訊號   数字信号
+數位化      数字化
 行動網路   移动网络
 流動網絡   移动网络
 咪高峰      麦克风
@@ -2639,7 +2641,7 @@ A型肝炎        甲型肝炎
 西元前      公元前
 翁山蘇姬   昂山素季
 昂山素姬   昂山素季
-西æ´\8bæ£\8b      å\9b¯际象棋
+西æ´\8bæ£\8b      å\9b½际象棋
 私隱 隐私
 格林美獎   格莱美奖
 葛萊美獎   格莱美奖
index acbce96..457457a 100644 (file)
 体里 體裏
 柜里 櫃裏
 电影里      電影裏
+广播里      廣播裏
+电视里      電視裏
+窝里斗      窩裏鬥
 苑裡 苑裡
 霄裡 霄裡
 岸裡 岸裡
 罗纳德·里根      朗奴·列根
 达芬奇      達文西
 达·芬奇    達·文西
+克卜勒      開普勒
 谢丽·布莱尔      彭雪玲
 葉爾欽      葉利欽
 菲利普親王        菲臘親王
 肖邦 蕭邦
 恺撒 凱撒
 肯尼迪      甘迺迪
+賓拉登      本拉登
+賓·拉登    本·拉登
 歐巴馬      奧巴馬
 戈登·布朗 白高敦
 狂牛症      瘋牛症
@@ -2956,7 +2962,6 @@ IP地址  IP位址
 大英國協   英聯邦
 共和联邦   英聯邦
 阿布達比   阿布扎比
-蓋曼群島   開曼群島
 宇航员      太空人
 薛丁格      薛定諤
 凯瑟琳      嘉芙蓮
@@ -2976,6 +2981,8 @@ IP地址  IP位址
 數位技術   數碼技術
 数字信号   數碼訊號
 數碼訊號   數碼訊號
+数字化      數碼化
+數位化      數碼化
 行動網路   流動網絡
 移动网络   流動網絡
 麥克風      咪高峰
index 57f9ab5..ddd6f9d 100644 (file)
@@ -4,7 +4,6 @@
 钩    鉤
 账    帳
 枱    檯
-卧    臥
 睾    睪
 酰    醯
 钫    鍅
 分辨率      解析度
 服务器      伺服器
 局域网      區域網
+局域网络   區域網路
 宽带 寬頻
 数据库      資料庫
 打印机      印表機
 穿梭機      太空梭
 因特网      網際網路
 互聯網      網際網路
+互聯網絡   網際網路
 机器人      機器人
 機械人      機器人
 移动电话   行動電話
 克林頓      柯林頓
 侯赛因      海珊
 侯賽因      海珊
+本拉登      賓拉登
+本·拉登    賓·拉登
 梵高 梵谷
 狄安娜      黛安娜
 戴安娜      黛安娜
 尼克松      尼克森
 威廉姆斯   威廉士
 多普勒      都卜勒
+开普勒      克卜勒
+開普勒      克卜勒
 叶利钦      葉爾欽
 卡斯特罗   卡斯楚
 包豪斯      包浩斯
@@ -663,7 +668,6 @@ IP地址    IP位址
 形而上学   形上學
 当且仅当   若且唯若
 圆珠笔      原子筆
-国际象棋   西洋棋
 可卡因      古柯鹼
 公共交通   公共運輸
 吉尼斯世界纪录  金氏世界紀錄
@@ -718,8 +722,6 @@ IP地址    IP位址
 英聯邦      大英國協
 共和联邦   大英國協
 阿布扎比   阿布達比
-开曼群岛   蓋曼群島
-開曼群島   蓋曼群島
 凯瑟琳      凱薩琳
 嘉芙蓮      凱薩琳
 门德尔松   孟德爾頌
@@ -740,6 +742,8 @@ IP地址    IP位址
 數碼技術   數位技術
 数字信号   數位訊號
 數碼訊號   數位訊號
+数字化      數位化
+數碼化      數位化
 移动网络   行動網路
 流動網絡   行動網路
 网络游戏   網路遊戲
@@ -758,7 +762,7 @@ IP地址    IP位址
 連結他      連結他
 昂山素季   翁山蘇姬
 昂山素姬   翁山蘇姬
\9b¯际象棋   西洋棋
\9b½际象棋   西洋棋
 國際象棋   西洋棋
 私隱 隱私
 硅藻 硅藻
@@ -772,3 +776,4 @@ IP地址    IP位址
 行人路權   行人路權
 行人路权   行人路權
 塑料袋      塑膠袋
+桃金娘      桃金孃
index b30fdd6..2bc8945 100644 (file)
@@ -47,6 +47,7 @@
 黃鈺筑      黃鈺筑
 答复 答覆
 反复 反覆
+反反复复   反反覆覆
 候复 候覆
 待复 待覆
 批复 批覆
 于克-蘭多縣       于克-蘭多縣
 于斯納爾斯貝里  于斯納爾斯貝里
 夏于喬      夏于喬
+于逸堯      于逸堯
 涂澤民      涂澤民
 涂長望      涂長望
 涂敏恆      涂敏恆
index 8e1e90d..309bd5d 100644 (file)
 皺彆
 一彆頭
 并州
+,並力
+,并力攻
+,并力討
 併兼
 併骨
 併網
 艸木丰丰
 張三丰
 復始
+往復式
 複分析
 複輔音
 複元音
 麵點、
 、麵點
 麵製品
+乾脆麵
 冷面相
 糞穢衊面
 僕僕
 瀋丹線
 瀋丹鐵路
 瀋丹客運
+瀋丹高
 瀋大線
 瀋大鐵路
 瀋大高速
 萬紮
 誌異
 筑前
+修築前
+建築前
 筑後
+修築後
+建築後
 筑紫
 筑波
 筑州
 顯示表示
 電錶
 水錶
+水表示
 咪錶
 射鵰
 神鵰
 傲遊 # 浏览器名
 網遊
 桌遊
+手遊
 遊輪
 遊牧
 遊蕩
 鬥垮
 鬥敗
 鬥戰
+窩裡鬥
 石樑
 木樑
 藏歷史
 讚唄
 點讚
 點個讚
+讚一個
 超讚
 飛紮
 紮裹
 醜語
 母醜
 一齣子
-齣兒
 丰標
 丰姿
 丰韻
 上天里
 天里昂
 人生天里
\99½子里
\99¾子里
 朴子里
 翁子里
 田子里
 櫃裡
 片裡
 電影裡
+廣播裡
+電視裡
 裏白 #植物常用名
 烏蘇里 #分詞用
 首發
 王后
 王侯后
 母后
+字母後
+聲母後
 武后
 歌后
 影后
 鮮于
 朝鮮於
 于寶軒
+于承惠
 于震
 於震前
 於震後
 電子製表
 製毒
 製販
+譯製
 遏制 #以下分詞用
 管制
 抑制
index 6903365..dc20eee 100644 (file)
@@ -41,7 +41,7 @@ class MigrateUserGroup extends Maintenance {
                $count = 0;
                $oldGroup = $this->getArg( 0 );
                $newGroup = $this->getArg( 1 );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $start = $dbw->selectField( 'user_groups', 'MIN(ug_user)',
                        array( 'ug_group' => $oldGroup ), __FUNCTION__ );
                $end = $dbw->selectField( 'user_groups', 'MAX(ug_user)',
@@ -58,7 +58,7 @@ class MigrateUserGroup extends Maintenance {
                        $affected = 0;
                        $this->output( "Doing users $blockStart to $blockEnd\n" );
 
-                       $dbw->begin( __METHOD__ );
+                       $this->beginTransaction( $dbw, __METHOD__ );
                        $dbw->update( 'user_groups',
                                array( 'ug_group' => $newGroup ),
                                array( 'ug_group' => $oldGroup,
@@ -77,7 +77,7 @@ class MigrateUserGroup extends Maintenance {
                                __METHOD__
                        );
                        $affected += $dbw->affectedRows();
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
 
                        // Clear cache for the affected users (bug 40340)
                        if ( $affected > 0 ) {
index 5849908..43d4d25 100644 (file)
@@ -85,7 +85,7 @@ class MoveBatch extends Maintenance {
                }
 
                # Setup complete, now start
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                // @codingStandardsIgnoreStart Ignore avoid function calls in a FOR loop test part warning
                for ( $linenum = 1; !feof( $file ); $linenum++ ) {
                        // @codingStandardsIgnoreEnd
@@ -106,13 +106,13 @@ class MoveBatch extends Maintenance {
                        }
 
                        $this->output( $source->getPrefixedText() . ' --> ' . $dest->getPrefixedText() );
-                       $dbw->begin( __METHOD__ );
+                       $this->beginTransaction( $dbw, __METHOD__ );
                        $mp = new MovePage( $source, $dest );
                        $status = $mp->move( $wgUser, $reason, !$noredirects );
                        if ( !$status->isOK() ) {
                                $this->output( "\nFAILED: " . $status->getWikiText() );
                        }
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                        $this->output( "\n" );
 
                        if ( $interval ) {
index 28176a5..6e5cd38 100644 (file)
@@ -67,7 +67,7 @@ class NamespaceConflictChecker extends Maintenance {
        }
 
        public function execute() {
-               $this->db = wfGetDB( DB_MASTER );
+               $this->db = $this->getDB( DB_MASTER );
 
                $options = array(
                        'fix' => $this->hasOption( 'fix' ),
@@ -570,6 +570,7 @@ class NamespaceConflictChecker extends Maintenance {
         *
         * @param integer $id The page_id
         * @param Title $newTitle The new title
+        * @return bool
         */
        private function mergePage( $row, Title $newTitle ) {
                $id = $row->page_id;
@@ -583,7 +584,7 @@ class NamespaceConflictChecker extends Maintenance {
                $wikiPage->loadPageData( 'fromdbmaster' );
 
                $destId = $newTitle->getArticleId();
-               $this->db->begin( __METHOD__ );
+               $this->beginTransaction( $this->db, __METHOD__ );
                $this->db->update( 'revision',
                        // SET
                        array( 'rev_page' => $destId ),
@@ -604,7 +605,7 @@ class NamespaceConflictChecker extends Maintenance {
                 */
                $update = new LinksDeletionUpdate( $wikiPage );
                $update->doUpdate();
-               $this->db->commit( __METHOD__ );
+               $this->commitTransaction( $this->db, __METHOD__ );
 
                return true;
        }
index 64bf1b6..0f2dbf6 100644 (file)
@@ -54,8 +54,8 @@ class NukeNS extends Maintenance {
                $ns = $this->getOption( 'ns', NS_MEDIAWIKI );
                $delete = $this->getOption( 'delete', false );
                $all = $this->getOption( 'all', false );
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $tbl_pag = $dbw->tableName( 'page' );
                $tbl_rev = $dbw->tableName( 'revision' );
@@ -86,7 +86,7 @@ class NukeNS extends Maintenance {
                                // I already have the id & revs
                                if ( $delete ) {
                                        $dbw->query( "DELETE FROM $tbl_pag WHERE page_id = $id" );
-                                       $dbw->commit( __METHOD__ );
+                                       $this->commitTransaction( $dbw, __METHOD__ );
                                        // Delete revisions as appropriate
                                        $child = $this->runChild( 'NukePage', 'nukePage.php' );
                                        $child->deleteRevisions( $revs );
@@ -97,7 +97,7 @@ class NukeNS extends Maintenance {
                                $this->output( "skip: " . $title->getPrefixedText() . "\n" );
                        }
                }
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
 
                if ( $n_deleted > 0 ) {
                        # update statistics - better to decrement existing count, or just count
index 1870273..dc45520 100644 (file)
@@ -43,8 +43,8 @@ class NukePage extends Maintenance {
                $name = $this->getArg();
                $delete = $this->getOption( 'delete', false );
 
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $tbl_pag = $dbw->tableName( 'page' );
                $tbl_rec = $dbw->tableName( 'recentchanges' );
@@ -79,7 +79,7 @@ class NukePage extends Maintenance {
                                $this->output( "done.\n" );
                        }
 
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
 
                        # Delete revisions as appropriate
                        if ( $delete && $count ) {
@@ -99,20 +99,20 @@ class NukePage extends Maintenance {
                        }
                } else {
                        $this->output( "not found in database.\n" );
-                       $dbw->commit( __METHOD__ );
+                       $this->commitTransaction( $dbw, __METHOD__ );
                }
        }
 
        public function deleteRevisions( $ids ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                $tbl_rev = $dbw->tableName( 'revision' );
 
                $set = implode( ', ', $ids );
                $dbw->query( "DELETE FROM $tbl_rev WHERE rev_id IN ( $set )" );
 
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
        }
 }
 
index eea6f7b..67e5ded 100644 (file)
@@ -48,7 +48,7 @@ class AlterSharedConstraints extends Maintenance {
                        return;
                }
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                foreach ( $wgSharedTables as $table ) {
                        $stable = $dbw->tableNameInternal( $table );
                        if ( $wgSharedPrefix != null ) {
index 7e27107..3c5566f 100644 (file)
@@ -71,7 +71,7 @@ class Orphans extends Maintenance {
         * @param bool $fix Whether to fix broken revisions when found
         */
        private function checkOrphans( $fix ) {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $page = $dbw->tableName( 'page' );
                $revision = $dbw->tableName( 'revision' );
 
@@ -129,7 +129,7 @@ class Orphans extends Maintenance {
         *       but valid revisions do exist)
         */
        private function checkWidows( $fix ) {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $page = $dbw->tableName( 'page' );
                $revision = $dbw->tableName( 'revision' );
 
@@ -175,7 +175,7 @@ class Orphans extends Maintenance {
         * @param bool $fix Whether to fix broken entries
         */
        private function checkSeparation( $fix ) {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $page = $dbw->tableName( 'page' );
                $revision = $dbw->tableName( 'revision' );
 
index 3bde81e..f414383 100644 (file)
@@ -51,4 +51,3 @@ class PageExists extends Maintenance {
 
 $maintClass = "PageExists";
 require_once RUN_MAINTENANCE_IF_MAIN;
-
index 5d9fc1b..1f77bdb 100644 (file)
@@ -44,7 +44,7 @@ class PatchSql extends Maintenance {
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                foreach ( $this->mArgs as $arg ) {
                        $files = array(
                                $arg,
index 65d272b..481e073 100644 (file)
@@ -71,7 +71,7 @@ TEXT;
                $throttle = $this->getOption( 'throttle', 0 );
                $force = $this->getOption( 'force', false );
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                if ( !$force ) {
                        $row = $dbw->selectRow(
index 7bca0ec..4f9c7ae 100644 (file)
@@ -37,7 +37,7 @@ class PopulateContentModel extends Maintenance {
        }
 
        public function execute() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $ns = $this->getOption( 'ns' );
                if ( !ctype_digit( $ns ) && $ns !== 'all' ) {
                        $this->error( 'Invalid namespace', 1 );
index a3099f9..5a67262 100644 (file)
@@ -45,7 +45,7 @@ class PopulateFilearchiveSha1 extends LoggedUpdateMaintenance {
 
        public function doDBUpdates() {
                $startTime = microtime( true );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $table = 'filearchive';
                $conds = array( 'fa_sha1' => '', 'fa_storage_key IS NOT NULL' );
 
index e9123aa..cc52239 100644 (file)
@@ -67,7 +67,7 @@ class PopulateImageSha1 extends LoggedUpdateMaintenance {
                $isRegen = ( $force || $file != '' ); // forced recalculation?
 
                $t = -microtime( true );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                if ( $file != '' ) {
                        $res = $dbw->select(
                                'image',
index 96cb1ec..60329c0 100644 (file)
@@ -67,12 +67,12 @@ class PopulateLogUsertext extends LoggedUpdateMaintenance {
                        $res = $db->select( array( 'logging', 'user' ),
                                array( 'log_id', 'user_name' ), $cond, __METHOD__ );
 
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        foreach ( $res as $row ) {
                                $db->update( 'logging', array( 'log_user_text' => $row->user_name ),
                                        array( 'log_id' => $row->log_id ), __METHOD__ );
                        }
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
                        wfWaitForSlaves();
index 686d9f2..9baf28e 100644 (file)
@@ -46,7 +46,7 @@ class PopulateParentId extends LoggedUpdateMaintenance {
        }
 
        protected function doDBUpdates() {
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
                if ( !$db->tableExists( 'revision' ) ) {
                        $this->error( "revision table does not exist" );
 
index b73ac7f..a9fb394 100644 (file)
@@ -100,14 +100,14 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
                                __METHOD__
                        );
 
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        # Go through and update rev_len from these rows.
                        foreach ( $res as $row ) {
                                if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
                                        $count++;
                                }
                        }
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
 
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
index b401db0..43504b1 100644 (file)
@@ -95,13 +95,13 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
                                AND $idCol IS NOT NULL AND {$prefix}_sha1 = ''";
                        $res = $db->select( $table, '*', $cond, __METHOD__ );
 
-                       $db->begin( __METHOD__ );
+                       $this->beginTransaction( $db, __METHOD__ );
                        foreach ( $res as $row ) {
                                if ( $this->upgradeRow( $row, $table, $idCol, $prefix ) ) {
                                        $count++;
                                }
                        }
-                       $db->commit( __METHOD__ );
+                       $this->commitTransaction( $db, __METHOD__ );
 
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
@@ -121,20 +121,20 @@ class PopulateRevisionSha1 extends LoggedUpdateMaintenance {
                        array( 'ar_rev_id IS NULL', 'ar_sha1' => '' ), __METHOD__ );
 
                $updateSize = 0;
-               $db->begin( __METHOD__ );
+               $this->beginTransaction( $db, __METHOD__ );
                foreach ( $res as $row ) {
                        if ( $this->upgradeLegacyArchiveRow( $row ) ) {
                                ++$count;
                        }
                        if ( ++$updateSize >= 100 ) {
                                $updateSize = 0;
-                               $db->commit( __METHOD__ );
+                               $this->commitTransaction( $db, __METHOD__ );
                                $this->output( "Commited row with ar_timestamp={$row->ar_timestamp}\n" );
                                wfWaitForSlaves();
-                               $db->begin( __METHOD__ );
+                               $this->beginTransaction( $db, __METHOD__ );
                        }
                }
-               $db->commit( __METHOD__ );
+               $this->commitTransaction( $db, __METHOD__ );
 
                return $count;
        }
diff --git a/maintenance/postgres/archives/patch-bot_passwords.sql b/maintenance/postgres/archives/patch-bot_passwords.sql
new file mode 100644 (file)
index 0000000..8e8a794
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE TABLE bot_passwords (
+  bp_user INTEGER NOT NULL,
+  bp_app_id TEXT NOT NULL,
+  bp_password TEXT NOT NULL,
+  bp_token TEXT NOT NULL,
+  bp_restrictions TEXT NOT NULL,
+  bp_grants TEXT NOT NULL,
+  PRIMARY KEY ( bp_user, bp_app_id )
+);
index ad7bd9d..c9f049b 100644 (file)
@@ -74,6 +74,15 @@ CREATE TABLE user_newtalk (
 CREATE INDEX user_newtalk_id_idx ON user_newtalk (user_id);
 CREATE INDEX user_newtalk_ip_idx ON user_newtalk (user_ip);
 
+CREATE TABLE bot_passwords (
+  bp_user INTEGER NOT NULL,
+  bp_app_id TEXT NOT NULL,
+  bp_password TEXT NOT NULL,
+  bp_token TEXT NOT NULL,
+  bp_restrictions TEXT NOT NULL,
+  bp_grants TEXT NOT NULL,
+  PRIMARY KEY ( bp_user, bp_app_id )
+);
 
 CREATE SEQUENCE page_page_id_seq;
 CREATE TABLE page (
index 31ea5d0..9963cbf 100644 (file)
@@ -86,7 +86,7 @@ class PurgeList extends Maintenance {
         * @param int|bool $namespace
         */
        private function purgeNamespace( $namespace = false ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $startId = 0;
                if ( $namespace === false ) {
                        $conds = array();
index 679cadb..e68937a 100644 (file)
@@ -74,8 +74,8 @@ class ReassignEdits extends Maintenance {
         * @return int Number of entries changed, or that would be changed
         */
        private function doReassignEdits( &$from, &$to, $rc = false, $report = false ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbw->begin( __METHOD__ );
+               $dbw = $this->getDB( DB_MASTER );
+               $this->beginTransaction( $dbw, __METHOD__ );
 
                # Count things
                $this->output( "Checking current edits..." );
@@ -139,7 +139,7 @@ class ReassignEdits extends Maintenance {
                        }
                }
 
-               $dbw->commit( __METHOD__ );
+               $this->commitTransaction( $dbw, __METHOD__ );
 
                return (int)$total;
        }
index 924457a..e07bf03 100644 (file)
@@ -70,7 +70,7 @@ class RebuildFileCache extends Maintenance {
 
                $this->output( "Building content page file cache from page {$start}!\n" );
 
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $overwrite = $this->getOption( 'overwrite', false );
                $start = ( $start > 0 )
                        ? $start
@@ -89,7 +89,7 @@ class RebuildFileCache extends Maintenance {
                $blockStart = $start;
                $blockEnd = $start + $this->mBatchSize - 1;
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                // Go through each page and save the output
                while ( $blockEnd <= $end ) {
                        // Get the pages
@@ -99,7 +99,7 @@ class RebuildFileCache extends Maintenance {
                                array( 'ORDER BY' => 'page_id ASC', 'USE INDEX' => 'PRIMARY' )
                        );
 
-                       $dbw->begin( __METHOD__ ); // for any changes
+                       $this->beginTransaction( $dbw, __METHOD__ ); // for any changes
                        foreach ( $res as $row ) {
                                $rebuilt = false;
                                $wgRequestTime = microtime( true ); # bug 22852
@@ -145,7 +145,7 @@ class RebuildFileCache extends Maintenance {
                                        $this->output( "Page {$row->page_id} not cacheable\n" );
                                }
                        }
-                       $dbw->commit( __METHOD__ ); // commit any changes (just for sanity)
+                       $this->commitTransaction( $dbw, __METHOD__ ); // commit any changes (just for sanity)
 
                        $blockStart += $this->mBatchSize;
                        $blockEnd += $this->mBatchSize;
index b1bb353..1b0a27d 100644 (file)
@@ -58,7 +58,7 @@ class ImageBuilder extends Maintenance {
        }
 
        public function execute() {
-               $this->dbw = wfGetDB( DB_MASTER );
+               $this->dbw = $this->getDB( DB_MASTER );
                $this->dryrun = $this->hasOption( 'dry-run' );
                if ( $this->dryrun ) {
                        $GLOBALS['wgReadOnly'] = 'Dry run mode, image upgrades are suppressed';
@@ -127,7 +127,7 @@ class ImageBuilder extends Maintenance {
                $this->init( $count, $table );
                $this->output( "Processing $table...\n" );
 
-               $result = wfGetDB( DB_SLAVE )->select( $table, '*', array(), __METHOD__ );
+               $result = $this->getDB( DB_SLAVE )->select( $table, '*', array(), __METHOD__ );
 
                foreach ( $result as $row ) {
                        $update = call_user_func( $callback, $row, null );
index eeee9c2..4ff873e 100644 (file)
@@ -41,7 +41,7 @@ class RebuildAll extends Maintenance {
 
        public function execute() {
                // Rebuild the text index
-               if ( wfGetDB( DB_SLAVE )->getType() != 'postgres' ) {
+               if ( $this->getDB( DB_SLAVE )->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 f4b0505..b6421f3 100644 (file)
@@ -46,11 +46,10 @@ class RebuildRecentchanges extends Maintenance {
        }
 
        /**
-        * Rebuild pass 1
-        * DOCUMENT ME!
+        * Rebuild pass 1: Insert `recentchanges` entries for page revisions.
         */
        private function rebuildRecentChangesTablePass1() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $dbw->delete( 'recentchanges', '*' );
 
@@ -100,11 +99,11 @@ class RebuildRecentchanges extends Maintenance {
        }
 
        /**
-        * Rebuild pass 2
-        * DOCUMENT ME!
+        * Rebuild pass 2: Enhance entries for page revisions with references to the previous revision
+        * (rc_last_oldid, rc_new etc.) and size differences (rc_old_len, rc_new_len).
         */
        private function rebuildRecentChangesTablePass2() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                list( $recentchanges, $revision ) = $dbw->tableNamesN( 'recentchanges', 'revision' );
 
                $this->output( "Updating links and size differences...\n" );
@@ -167,11 +166,10 @@ class RebuildRecentchanges extends Maintenance {
        }
 
        /**
-        * Rebuild pass 3
-        * DOCUMENT ME!
+        * Rebuild pass 3: Insert `recentchanges` entries for action logs.
         */
        private function rebuildRecentChangesTablePass3() {
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $this->output( "Loading from user, page, and logging tables...\n" );
 
@@ -221,13 +219,12 @@ class RebuildRecentchanges extends Maintenance {
        }
 
        /**
-        * Rebuild pass 4
-        * DOCUMENT ME!
+        * Rebuild pass 4: Mark bot and autopatrolled entries.
         */
        private function rebuildRecentChangesTablePass4() {
                global $wgUseRCPatrol;
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                list( $recentchanges, $usergroups, $user ) =
                        $dbw->tableNamesN( 'recentchanges', 'user_groups', 'user' );
index e29d89e..e8d59bc 100644 (file)
@@ -51,12 +51,11 @@ class RebuildTextIndex extends Maintenance {
 
        public function execute() {
                // Shouldn't be needed for Postgres
-               $this->db = wfGetDB( DB_MASTER );
+               $this->db = $this->getDB( DB_MASTER );
                if ( $this->db->getType() == 'postgres' ) {
                        $this->error( "This script is not needed when using Postgres.\n", true );
                }
 
-               $this->db = wfGetDB( DB_MASTER );
                if ( $this->db->getType() == 'sqlite' ) {
                        if ( !DatabaseSqlite::getFulltextSearchModule() ) {
                                $this->error( "Your version of SQLite module for PHP doesn't "
index 8b852e3..6bc72ec 100644 (file)
@@ -47,7 +47,7 @@ class RefreshFileHeaders extends Maintenance {
                $end = str_replace( ' ', '_', $this->getOption( 'end', '' ) ); // page on img_name
 
                $count = 0;
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                do {
                        $conds = array( "img_name > {$dbr->addQuotes( $start )}" );
                        if ( strlen( $end ) ) {
index 831118c..4f2341c 100644 (file)
@@ -95,7 +95,7 @@ class RefreshImageMetadata extends Maintenance {
                $leftAlone = 0;
                $error = 0;
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                if ( $this->mBatchSize <= 0 ) {
                        $this->error( "Batch size is too low...", 12 );
                }
index ed16805..1159e53 100644 (file)
@@ -73,10 +73,8 @@ class RefreshLinks extends Maintenance {
        private function doRefreshLinks( $start, $newOnly = false,
                $end = null, $redirectsOnly = false, $oldRedirectsOnly = false
        ) {
-               global $wgParser;
-
                $reportingInterval = 100;
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                if ( $start === null ) {
                        $start = 1;
@@ -85,9 +83,6 @@ class RefreshLinks extends Maintenance {
                // Give extensions a chance to optimize settings
                Hooks::run( 'MaintenanceRefreshLinksInit', array( $this ) );
 
-               # Don't generate extension images (e.g. Timeline)
-               $wgParser->clearTagHooks();
-
                $what = $redirectsOnly ? "redirects" : "links";
 
                if ( $oldRedirectsOnly ) {
@@ -192,7 +187,7 @@ class RefreshLinks extends Maintenance {
         */
        private function fixRedirect( $id ) {
                $page = WikiPage::newFromID( $id );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                if ( $page === null ) {
                        // This page doesn't exist (any more)
@@ -262,7 +257,7 @@ class RefreshLinks extends Maintenance {
        ) {
                wfWaitForSlaves();
                $this->output( "Deleting illegal entries from the links tables...\n" );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                do {
                        // Find the start of the next chunk. This is based only
                        // on existent page_ids.
@@ -302,8 +297,8 @@ class RefreshLinks extends Maintenance {
         * @param int $batchSize The size of deletion batches
         */
        private function dfnCheckInterval( $start = null, $end = null, $batchSize = 100 ) {
-               $dbw = wfGetDB( DB_MASTER );
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbw = $this->getDB( DB_MASTER );
+               $dbr = $this->getDB( DB_SLAVE );
 
                $linksTables = array( // table name => page_id field
                        'pagelinks' => 'pl_from',
index 90dc622..7937dd0 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 = array();
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $res = $dbr->select( 'user', array( 'user_id', 'user_name', 'user_touched' ), '', __METHOD__ );
                if ( $this->hasOption( 'ignore-groups' ) ) {
                        $excludedGroups = explode( ',', $this->getOption( 'ignore-groups' ) );
@@ -76,7 +76,7 @@ class RemoveUnusedAccounts extends Maintenance {
                # If required, go back and delete each marked account
                if ( $count > 0 && $this->hasOption( 'delete' ) ) {
                        $this->output( "\nDeleting unused accounts..." );
-                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw = $this->getDB( DB_MASTER );
                        $dbw->delete( 'user', array( 'user_id' => $del ), __METHOD__ );
                        $dbw->delete( 'user_groups', array( 'ug_user' => $del ), __METHOD__ );
                        $dbw->delete( 'user_former_groups', array( 'ufg_user' => $del ), __METHOD__ );
@@ -107,7 +107,7 @@ class RemoveUnusedAccounts extends Maintenance {
         * @return bool
         */
        private function isInactiveAccount( $id, $master = false ) {
-               $dbo = wfGetDB( $master ? DB_MASTER : DB_SLAVE );
+               $dbo = $this->getDB( $master ? DB_MASTER : DB_SLAVE );
                $checks = array(
                        'revision' => 'rev',
                        'archive' => 'ar',
@@ -117,7 +117,7 @@ class RemoveUnusedAccounts extends Maintenance {
                );
                $count = 0;
 
-               $dbo->begin( __METHOD__ );
+               $this->beginTransaction( $dbo, __METHOD__ );
                foreach ( $checks as $table => $fprefix ) {
                        $conds = array( $fprefix . '_user' => $id );
                        $count += (int)$dbo->selectField( $table, 'COUNT(*)', $conds, __METHOD__ );
@@ -126,7 +126,7 @@ class RemoveUnusedAccounts extends Maintenance {
                $conds = array( 'log_user' => $id, 'log_type != ' . $dbo->addQuotes( 'newusers' ) );
                $count += (int)$dbo->selectField( 'logging', 'COUNT(*)', $conds, __METHOD__ );
 
-               $dbo->commit( __METHOD__ );
+               $this->commitTransaction( $dbo, __METHOD__ );
 
                return $count == 0;
        }
index ed9d1f5..2772f04 100644 (file)
@@ -71,7 +71,7 @@ class RenameDbPrefix extends Maintenance {
                $this->output( "Renaming DB prefix for tables of $wgDBname from '$old' to '$new'\n" );
                $count = 0;
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $res = $dbw->query( "SHOW TABLES " . $dbw->buildLike( $old, $dbw->anyString() ) );
                foreach ( $res as $row ) {
                        // XXX: odd syntax. MySQL outputs an oddly cased "Tables of X"
index 08be553..9c7aef2 100644 (file)
@@ -65,7 +65,7 @@ class ResetUserTokens extends Maintenance {
                }
 
                // We list user by user_id from one of the slave database
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                $where = array();
                if ( $this->nullsOnly ) {
index 7be5a1f..7134453 100644 (file)
@@ -95,7 +95,7 @@ class RollbackEdits extends Maintenance {
         * @return array
         */
        private function getRollbackTitles( $user ) {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $titles = array();
                $results = $dbr->select(
                        array( 'page', 'revision' ),
index af88905..3fd9e02 100644 (file)
@@ -45,7 +45,7 @@ class BatchedQueryRunner extends Maintenance {
 
                $query = $this->getArg();
                $n = 1;
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                do {
                        $this->output( "Batch $n: " );
                        $n++;
index 370d14e..56cc573 100644 (file)
@@ -53,7 +53,7 @@ class ShowSiteStats extends Maintenance {
                );
 
                // Get cached stats from slave database
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $stats = $dbr->selectRow( 'site_stats', '*', '', __METHOD__ );
 
                // Get maximum size for each column
index b11f1c8..96a8a38 100644 (file)
@@ -59,7 +59,7 @@ class SqliteMaintenance extends Maintenance {
                        return;
                }
 
-               $this->db = wfGetDB( DB_MASTER );
+               $this->db = $this->getDB( DB_MASTER );
 
                if ( $this->db->getType() != 'sqlite' ) {
                        $this->error( "This maintenance script requires a SQLite database.\n" );
index 6cac9a3..fbc407c 100644 (file)
@@ -54,4 +54,3 @@ CREATE TABLE /*$wgDBprefix*/blob_orphans (
 
        PRIMARY KEY (bo_cluster, bo_blob_id)
 ) /*$wgDBTableOptions*/;
-
index 16c676d..b27b111 100644 (file)
@@ -151,7 +151,7 @@ class CompressOld extends Maintenance {
        private function compressOldPages( $start = 0, $extdb = '' ) {
                $chunksize = 50;
                $this->output( "Starting from old_id $start...\n" );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                do {
                        $res = $dbw->select(
                                'text',
@@ -192,7 +192,7 @@ class CompressOld extends Maintenance {
                        # print "Already compressed row {$row->old_id}\n";
                        return false;
                }
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $flags = $row->old_flags ? "{$row->old_flags},gzip" : "gzip";
                $compress = gzdeflate( $row->old_text );
 
@@ -237,8 +237,8 @@ class CompressOld extends Maintenance {
        ) {
                $loadStyle = self::LS_CHUNKED;
 
-               $dbr = wfGetDB( DB_SLAVE );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbr = $this->getDB( DB_SLAVE );
+               $dbw = $this->getDB( DB_MASTER );
 
                # Set up external storage
                if ( $extdb != '' ) {
@@ -359,7 +359,7 @@ class CompressOld extends Maintenance {
 
                                $chunk = new ConcatenatedGzipHistoryBlob();
                                $stubs = array();
-                               $dbw->begin( __METHOD__ );
+                               $this->beginTransaction( $dbw, __METHOD__ );
                                $usedChunk = false;
                                $primaryOldid = $revs[$i]->rev_text_id;
 
@@ -463,7 +463,7 @@ class CompressOld extends Maintenance {
                                }
                                # Done, next
                                $this->output( "/" );
-                               $dbw->commit( __METHOD__ );
+                               $this->commitTransaction( $dbw, __METHOD__ );
                                $i += $thisChunkSize;
                                wfWaitForSlaves();
                        }
index f12bbd1..dcb76e3 100644 (file)
@@ -36,7 +36,7 @@ class DumpRev extends Maintenance {
        }
 
        public function execute() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                $row = $dbr->selectRow(
                        array( 'text', 'revision' ),
                        array( 'old_flags', 'old_text' ),
index dd4cd54..e926f56 100644 (file)
@@ -42,8 +42,8 @@ class FixBug20757 extends Maintenance {
        }
 
        function execute() {
-               $dbr = wfGetDB( DB_SLAVE );
-               $dbw = wfGetDB( DB_MASTER );
+               $dbr = $this->getDB( DB_SLAVE );
+               $dbw = $this->getDB( DB_MASTER );
 
                $dryRun = $this->getOption( 'dry-run' );
                if ( $dryRun ) {
@@ -213,7 +213,7 @@ class FixBug20757 extends Maintenance {
 
                                if ( !$dryRun ) {
                                        // Reset the text row to point to the original copy
-                                       $dbw->begin( __METHOD__ );
+                                       $this->beginTransaction( $dbw, __METHOD__ );
                                        $dbw->update(
                                                'text',
                                                // SET
@@ -241,7 +241,7 @@ class FixBug20757 extends Maintenance {
                                                ),
                                                __METHOD__
                                        );
-                                       $dbw->commit( __METHOD__ );
+                                       $this->commitTransaction( $dbw, __METHOD__ );
                                        $this->waitForSlaves();
                                }
 
@@ -283,7 +283,7 @@ class FixBug20757 extends Maintenance {
                                unset( $this->mapCache[$key] );
                        }
 
-                       $dbr = wfGetDB( DB_SLAVE );
+                       $dbr = $this->getDB( DB_SLAVE );
                        $map = array();
                        $res = $dbr->select( 'revision',
                                array( 'rev_id', 'rev_text_id' ),
index c5213ad..b1bf95b 100644 (file)
@@ -43,7 +43,7 @@ class OrphanStats extends Maintenance {
        }
 
        public function execute() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
                if ( !$dbr->tableExists( 'blob_orphans' ) ) {
                        $this->error( "blob_orphans doesn't seem to exist, need to run trackBlobs.php first", true );
                }
index f7907ad..7386df8 100644 (file)
@@ -58,6 +58,7 @@ class RecompressTracked {
        public $orphanBatchSize = 1000;
        public $reportingInterval = 10;
        public $numProcs = 1;
+       public $numBatches = 0;
        public $useDiff, $pageBlobClass, $orphanBlobClass;
        public $slavePipes, $slaveProcs, $prevSlaveId;
        public $copyOnly = false;
@@ -195,7 +196,7 @@ class RecompressTracked {
 
                        return false;
                }
-               $row = $dbr->selectRow( 'blob_tracking', '*', false, __METHOD__ );
+               $row = $dbr->selectRow( 'blob_tracking', '*', '', __METHOD__ );
                if ( !$row ) {
                        $this->info( "Warning: blob_tracking table contains no rows, skipping this wiki." );
 
@@ -228,7 +229,7 @@ class RecompressTracked {
 
                $this->slavePipes = $this->slaveProcs = array();
                for ( $i = 0; $i < $this->numProcs; $i++ ) {
-                       $pipes = false;
+                       $pipes = array();
                        $spec = array(
                                array( 'pipe', 'r' ),
                                array( 'file', 'php://stdout', 'w' ),
@@ -340,10 +341,10 @@ class RecompressTracked {
                                break;
                        }
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_page;
                                $this->dispatch( 'doPage', $row->bt_page );
                                $i++;
                        }
-                       $startId = $row->bt_page;
                        $this->report( 'pages', $i, $numPages );
                }
                $this->report( 'pages', $i, $numPages );
@@ -413,6 +414,7 @@ class RecompressTracked {
                        }
                        $ids = array();
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_text_id;
                                $ids[] = $row->bt_text_id;
                                $i++;
                        }
@@ -431,7 +433,6 @@ class RecompressTracked {
                                call_user_func_array( array( $this, 'dispatch' ), $args );
                        }
 
-                       $startId = $row->bt_text_id;
                        $this->report( 'orphans', $i, $numOrphans );
                }
                $this->report( 'orphans', $i, $numOrphans );
@@ -513,6 +514,7 @@ class RecompressTracked {
 
                        $lastTextId = 0;
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_text_id;
                                if ( $lastTextId == $row->bt_text_id ) {
                                        // Duplicate (null edit)
                                        continue;
@@ -533,7 +535,6 @@ class RecompressTracked {
                                        wfWaitForSlaves();
                                }
                        }
-                       $startId = $row->bt_text_id;
                }
 
                $this->debug( "$titleText: committing blob with " . $trx->getSize() . " items" );
@@ -611,12 +612,12 @@ class RecompressTracked {
                        }
                        $this->debug( 'Incomplete: ' . $res->numRows() . ' rows' );
                        foreach ( $res as $row ) {
+                               $startId = $row->bt_text_id;
                                $this->moveTextRow( $row->bt_text_id, $row->bt_new_url );
                                if ( $row->bt_text_id % 10 == 0 ) {
                                        wfWaitForSlaves();
                                }
                        }
-                       $startId = $row->bt_text_id;
                }
        }
 
@@ -693,8 +694,10 @@ class RecompressTracked {
  * Class to represent a recompression operation for a single CGZ blob
  */
 class CgzCopyTransaction {
+       /** @var RecompressTracked */
        public $parent;
        public $blobClass;
+       /** @var ConcatenatedGzipHistoryBlob */
        public $cgz;
        public $referrers;
 
@@ -787,7 +790,8 @@ class CgzCopyTransaction {
                                // All have been moved already
                                if ( $originalCount > 1 ) {
                                        // This is suspcious, make noise
-                                       $this->critical( "Warning: concurrent operation detected, are there two conflicting " .
+                                       $this->parent->critical(
+                                               "Warning: concurrent operation detected, are there two conflicting " .
                                                "processes running, doing the same job?" );
                                }
 
index e33057f..e156efe 100644 (file)
@@ -23,7 +23,7 @@ require_once __DIR__ . '/../Maintenance.php';
 
 class StorageTypeStats extends Maintenance {
        function execute() {
-               $dbr = wfGetDB( DB_SLAVE );
+               $dbr = $this->getDB( DB_SLAVE );
 
                $endId = $dbr->selectField( 'text', 'MAX(old_id)', false, __METHOD__ );
                if ( !$endId ) {
index f7ec662..148a9d1 100644 (file)
@@ -46,7 +46,7 @@ if ( isset( $options['limit'] ) ) {
 }
 $type = isset( $options['type'] ) ? $options['type'] : 'ConcatenatedGzipHistoryBlob';
 
-$dbr = wfGetDB( DB_SLAVE );
+$dbr = $this->getDB( DB_SLAVE );
 $res = $dbr->select(
        array( 'page', 'revision', 'text' ),
        '*',
index 756f6c0..743b9be 100644 (file)
@@ -220,6 +220,32 @@ CREATE TABLE /*_*/user_properties (
 CREATE UNIQUE INDEX /*i*/user_properties_user_property ON /*_*/user_properties (up_user,up_property);
 CREATE INDEX /*i*/user_properties_property ON /*_*/user_properties (up_property);
 
+--
+-- This table contains a user's bot passwords: passwords that allow access to
+-- the account via the API with limited rights.
+--
+CREATE TABLE /*_*/bot_passwords (
+  -- User ID obtained from CentralIdLookup.
+  bp_user int NOT NULL,
+
+  -- Application identifier
+  bp_app_id varbinary(32) NOT NULL,
+
+  -- Password hashes, like user.user_password
+  bp_password tinyblob NOT NULL,
+
+  -- Like user.user_token
+  bp_token binary(32) NOT NULL default '',
+
+  -- JSON blob for MWRestrictions
+  bp_restrictions blob NOT NULL,
+
+  -- Grants allowed to the account when authenticated with this bot-password
+  bp_grants blob NOT NULL,
+
+  PRIMARY KEY ( bp_user, bp_app_id )
+) /*$wgDBTableOptions*/;
+
 --
 -- Core of the wiki: each page has an entry here which identifies
 -- it by title and contains some essential metadata.
index 1ad9c7e..eac7b3f 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 = wfGetDB( DB_SLAVE )->select(
+               $result = $this->getDB( DB_SLAVE )->select(
                        'logging',
                        array( 'log_id', 'log_params' ),
                        array(
@@ -22,7 +22,7 @@ class TidyUpBug37714 extends Maintenance {
                foreach ( $result as $row ) {
                        $paramLines = explode( "\n", $row->log_params );
                        $ids = explode( ',', $paramLines[0] ); // Array dereferencing is PHP >= 5.4 :(
-                       $result = wfGetDB( DB_SLAVE )->select( // Work out what log entries were changed here.
+                       $result = $this->getDB( DB_SLAVE )->select( // Work out what log entries were changed here.
                                'logging',
                                'log_type',
                                array( 'log_id' => $ids ),
@@ -33,7 +33,7 @@ class TidyUpBug37714 extends Maintenance {
                                // If there's only one type, the target title can be set to include it.
                                $logTitle = SpecialPage::getTitleFor( 'Log', $result->current()->log_type )->getText();
                                $this->output( 'Set log_title to "' . $logTitle . '" for log entry ' . $row->log_id . ".\n" );
-                               wfGetDB( DB_MASTER )->update(
+                               $this->getDB( DB_MASTER )->update(
                                        'logging',
                                        array( 'log_title' => $logTitle ),
                                        array( 'log_id' => $row->log_id ),
index 452b53c..eeaf9c8 100755 (executable)
@@ -139,7 +139,7 @@ class UpdateMediaWiki extends Maintenance {
 
                # Attempt to connect to the database as a privileged user
                # This will vomit up an error if there are permissions problems
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
 
                $this->output( "Going to run database updates for " . wfWikiID() . "\n" );
                if ( $db->getType() === 'sqlite' ) {
index 55f535d..9537a79 100644 (file)
@@ -44,9 +44,9 @@ class UpdateArticleCount extends Maintenance {
                $this->output( "Counting articles..." );
 
                if ( $this->hasOption( 'use-master' ) ) {
-                       $dbr = wfGetDB( DB_MASTER );
+                       $dbr = $this->getDB( DB_MASTER );
                } else {
-                       $dbr = wfGetDB( DB_SLAVE, 'vslow' );
+                       $dbr = $this->getDB( DB_SLAVE, 'vslow' );
                }
                $counter = new SiteStatsInit( $dbr );
                $result = $counter->articles();
@@ -54,7 +54,7 @@ class UpdateArticleCount extends Maintenance {
                $this->output( "found {$result}.\n" );
                if ( $this->hasOption( 'update' ) ) {
                        $this->output( "Updating site statistics table... " );
-                       $dbw = wfGetDB( DB_MASTER );
+                       $dbw = $this->getDB( DB_MASTER );
                        $dbw->update(
                                'site_stats',
                                array( 'ss_good_articles' => $result ),
index 5cf8afa..bd75b3b 100644 (file)
@@ -86,7 +86,7 @@ TEXT;
 
                $options = array(
                        'LIMIT' => self::BATCH_SIZE,
-                       'ORDER BY' => 'cl_to, cl_type, cl_from',
+                       'ORDER BY' => 'cl_from, cl_to',
                        'STRAIGHT_JOIN',
                );
 
@@ -132,7 +132,7 @@ TEXT;
                        $res = $dbw->select(
                                array( 'categorylinks', 'page' ),
                                array( 'cl_from', 'cl_to', 'cl_sortkey_prefix', 'cl_collation',
-                                       'cl_sortkey', 'cl_type', 'page_namespace', 'page_title'
+                                       'cl_sortkey', 'page_namespace', 'page_title'
                                ),
                                array_merge( $collationConds, $batchConds, array( 'cl_from = page_id' ) ),
                                __METHOD__,
@@ -141,7 +141,7 @@ TEXT;
                        $this->output( " processing..." );
 
                        if ( !$dryRun ) {
-                               $dbw->begin( __METHOD__ );
+                               $this->beginTransaction( $dbw, __METHOD__ );
                        }
                        foreach ( $res as $row ) {
                                $title = Title::newFromRow( $row );
@@ -193,7 +193,7 @@ TEXT;
                                }
                        }
                        if ( !$dryRun ) {
-                               $dbw->commit( __METHOD__ );
+                               $this->commitTransaction( $dbw, __METHOD__ );
                        }
 
                        $count += $res->numRows();
@@ -216,13 +216,13 @@ TEXT;
 
        /**
         * Return an SQL expression selecting rows which sort above the given row,
-        * assuming an ordering of cl_to, cl_type, cl_from
+        * assuming an ordering of cl_from, cl_to
         * @param stdClass $row
         * @param DatabaseBase $dbw
         * @return string
         */
        function getBatchCondition( $row, $dbw ) {
-               $fields = array( 'cl_to', 'cl_type', 'cl_from' );
+               $fields = array( 'cl_from', 'cl_to' );
                $first = true;
                $cond = false;
                $prefix = false;
index 796cedd..5c21b40 100644 (file)
@@ -51,7 +51,7 @@ class UpdateDoubleWidthSearch extends Maintenance {
        public function execute() {
                $maxLockTime = $this->getOption( 'l', 20 );
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                if ( $dbw->getType() !== 'mysql' ) {
                        $this->error( "This change is only needed on MySQL, quitting.\n", true );
                }
index 5b5cc04..ebfffe4 100644 (file)
@@ -40,7 +40,7 @@ class UpdateRestrictions extends Maintenance {
        }
 
        public function execute() {
-               $db = wfGetDB( DB_MASTER );
+               $db = $this->getDB( DB_MASTER );
                if ( !$db->tableExists( 'page_restrictions' ) ) {
                        $this->error( "page_restrictions table does not exist", true );
                }
index 68a51bd..18edecc 100644 (file)
@@ -96,7 +96,7 @@ class UpdateSearchIndex extends Maintenance {
 
                $wgDisableSearchUpdate = false;
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
                $recentchanges = $dbw->tableName( 'recentchanges' );
 
                $this->output( "Updating searchindex between $start and $end\n" );
index c800664..8b24b90 100644 (file)
@@ -42,7 +42,7 @@ class UpdateSpecialPages extends Maintenance {
        public function execute() {
                global $wgQueryCacheLimit, $wgDisableQueryPageUpdate;
 
-               $dbw = wfGetDB( DB_MASTER );
+               $dbw = $this->getDB( DB_MASTER );
 
                $this->doSpecialPageCacheUpdates( $dbw );
 
diff --git a/maintenance/waitForSlave.php b/maintenance/waitForSlave.php
deleted file mode 100644 (file)
index 50665ef..0000000
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-/**
- * Wait for the slaves to catch up to the master position.
- *
- * 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
- * @see wfWaitForSlaves()
- */
-
-require_once __DIR__ . '/Maintenance.php';
-
-/**
- * Maintenance script to wait for the slaves to catch up to the master position.
- *
- * @ingroup Maintenance
- */
-class WaitForSlave extends Maintenance {
-       public function execute() {
-               wfWaitForSlaves();
-       }
-}
-
-$maintClass = "WaitForSlave";
-require_once RUN_MAINTENANCE_IF_MAIN;
index 37272a0..e0c10f8 100644 (file)
@@ -72,7 +72,7 @@ class WrapOldPasswords extends Maintenance {
 
                $minUserId = 0;
                do {
-                       $dbw->begin();
+                       $this->beginTransaction( $dbw, __METHOD__ );
 
                        $res = $dbw->select( 'user',
                                array( 'user_id', 'user_name', 'user_password' ),
@@ -112,7 +112,7 @@ class WrapOldPasswords extends Maintenance {
                                $minUserId = $row->user_id;
                        }
 
-                       $dbw->commit();
+                       $this->commitTransaction( $dbw, __METHOD__ );
 
                        // Clear memcached so old passwords are wiped out
                        foreach ( $updateUsers as $user ) {
index 08646d6..5a8257c 100644 (file)
     "grunt-contrib-copy": "0.8.1",
     "grunt-contrib-jshint": "0.11.3",
     "grunt-contrib-watch": "0.6.1",
-    "grunt-jscs": "2.5.0",
-    "grunt-jsonlint": "1.0.5",
+    "grunt-jscs": "2.6.0",
+    "grunt-jsonlint": "1.0.7",
     "grunt-karma": "0.12.1",
-    "karma": "0.13.10",
-    "karma-chrome-launcher": "0.2.0",
-    "karma-firefox-launcher": "0.1.6",
+    "karma": "0.13.19",
+    "karma-chrome-launcher": "0.2.2",
+    "karma-firefox-launcher": "0.1.7",
     "karma-qunit": "0.1.5",
     "qunitjs": "1.18.0"
   }
index 376e582..38c7aaa 100644 (file)
--- a/phpcs.xml
+++ b/phpcs.xml
        <rule ref="PSR2.Methods.MethodDeclaration.Underscore">
                <exclude-pattern>*/includes/StubObject.php</exclude-pattern>
        </rule>
+       <rule ref="MediaWiki.ControlStructures.AssignmentInControlStructures.AssignmentInControlStructures">
+               <severity>0</severity>
+       </rule>
+       <rule ref="Generic.ControlStructures.InlineControlStructure.NotAllowed">
+               <severity>0</severity>
+       </rule>
        <exclude-pattern>node_modules</exclude-pattern>
        <exclude-pattern>vendor</exclude-pattern>
        <exclude-pattern>extensions</exclude-pattern>
        <exclude-pattern>skins</exclude-pattern>
+       <exclude-pattern>.git</exclude-pattern>
 </ruleset>
index 4e247e0..18bff51 100644 (file)
@@ -355,9 +355,6 @@ return array(
                'scripts' => 'resources/lib/jquery/jquery.ba-throttle-debounce.js',
                'targets' => array( 'desktop', 'mobile' ),
        ),
-       'jquery.validate' => array(
-               'scripts' => 'resources/lib/jquery/jquery.validate.js',
-       ),
        'jquery.xmldom' => array(
                'scripts' => 'resources/lib/jquery/jquery.xmldom.js',
        ),
@@ -835,7 +832,6 @@ return array(
        'mediawiki.apihelp' => array(
                'styles' => 'resources/src/mediawiki/mediawiki.apihelp.css',
                'targets' => array( 'desktop' ),
-               'dependencies' => 'mediawiki.hlist',
                'position' => 'top',
        ),
        'mediawiki.template' => array(
@@ -1029,8 +1025,6 @@ return array(
        ),
        'mediawiki.hlist' => array(
                'styles' => 'resources/src/mediawiki/mediawiki.hlist.css',
-               'scripts' => 'resources/src/mediawiki/mediawiki.hlist.js',
-               'dependencies' => 'jquery.client',
        ),
        'mediawiki.htmlform' => array(
                'scripts' => 'resources/src/mediawiki/mediawiki.htmlform.js',
@@ -1158,6 +1152,10 @@ return array(
                        'mediawiki.Upload',
                        'oojs',
                ),
+               'messages' => array(
+                       'uploaddisabledtext',
+                       'upload-foreign-cant-upload',
+               )
        ),
        'mediawiki.ForeignStructuredUpload' => array(
                'scripts' => 'resources/src/mediawiki/mediawiki.ForeignStructuredUpload.js',
@@ -1186,6 +1184,7 @@ return array(
                ),
                'dependencies' => array(
                        'oojs-ui',
+                       'mediawiki.Title',
                        'mediawiki.user',
                        'mediawiki.Upload',
                        'mediawiki.jqueryMsg',
@@ -1194,7 +1193,9 @@ return array(
                        'upload-form-label-select-file',
                        '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',
                        'upload-form-label-usage-title',
                        'upload-form-label-usage-filename',
                        'api-error-unknownerror',
@@ -1261,6 +1262,7 @@ return array(
                        'mediawiki.widgets.DateInputWidget',
                        'mediawiki.jqueryMsg',
                        'moment',
+                       'mediawiki.libs.jpegmeta',
                ),
                'messages' => array(
                        'foreign-structured-upload-form-label-own-work',
@@ -1344,6 +1346,14 @@ return array(
                'position' => 'top', // For $wgPreloadJavaScriptMwUtil
                'targets' => array( 'desktop', 'mobile' ),
        ),
+       'mediawiki.checkboxtoggle' => array(
+               'scripts' => 'resources/src/mediawiki/mediawiki.checkboxtoggle.js',
+               'position' => 'top',
+       ),
+       'mediawiki.checkboxtoggle.styles' => array(
+               'styles' => 'resources/src/mediawiki/mediawiki.checkboxtoggle.css',
+               'position' => 'top',
+       ),
        'mediawiki.cookie' => array(
                'scripts' => 'resources/src/mediawiki/mediawiki.cookie.js',
                'dependencies' => 'jquery.cookie',
@@ -1478,6 +1488,7 @@ return array(
                'scripts' => 'resources/src/mediawiki.action/mediawiki.action.view.redirect.js',
                'dependencies' => 'jquery.client',
                'position' => 'top',
+               'targets' => array( 'desktop', 'mobile' ),
        ),
        'mediawiki.action.view.redirectPage' => array(
                'position' => 'top',
@@ -1689,6 +1700,10 @@ return array(
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.block.css',
                'dependencies' => 'mediawiki.util',
        ),
+       'mediawiki.special.blocklist' => array(
+               'styles' => 'resources/src/mediawiki.special/mediawiki.special.blocklist.css',
+               'position' => 'top',
+       ),
        'mediawiki.special.changeslist' => array(
                'position' => 'top',
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.changeslist.css',
@@ -1712,6 +1727,10 @@ return array(
                'position' => 'top',
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.changeslist.visitedstatus.js',
        ),
+       'mediawiki.special.comparepages.styles' => array(
+               'position' => 'top',
+               'styles' => 'resources/src/mediawiki.special/mediawiki.special.comparepages.styles.less',
+       ),
        'mediawiki.special.edittags' => array(
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.edittags.js',
                'dependencies' => array(
@@ -1866,16 +1885,6 @@ return array(
        'mediawiki.special.watchlist' => array(
                'scripts' => 'resources/src/mediawiki.special/mediawiki.special.watchlist.js',
        ),
-       'mediawiki.special.javaScriptTest' => array(
-               'scripts' => 'resources/src/mediawiki.special/mediawiki.special.javaScriptTest.js',
-               'messages' => array_merge( Skin::getSkinNameMessages(), array(
-                       'colon-separator',
-                       'javascripttest-pagetext-skins',
-               ) ),
-               'dependencies' => 'mediawiki.Uri',
-               'position' => 'top',
-               'targets' => array( 'desktop', 'mobile' ),
-       ),
        'mediawiki.special.version' => array(
                'styles' => 'resources/src/mediawiki.special/mediawiki.special.version.css',
        ),
@@ -2069,6 +2078,69 @@ return array(
                ),
                'targets' => array( 'desktop', 'mobile' ),
        ),
+       'mediawiki.widgets.datetime' => array(
+               'scripts' => array(
+                       'resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js',
+                       'resources/src/mediawiki.widgets.datetime/CalendarWidget.js',
+                       'resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js',
+                       'resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js',
+                       'resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js',
+               ),
+               'skinStyles' => array(
+                       'default' => array(
+                               'resources/src/mediawiki.widgets.datetime/CalendarWidget.less',
+                               'resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less',
+                       ),
+               ),
+               'messages' => array(
+                       'timezone-utc',
+                       'timezone-local',
+                       'january',
+                       'february',
+                       'march',
+                       'april',
+                       'may_long',
+                       'june',
+                       'july',
+                       'august',
+                       'september',
+                       'october',
+                       'november',
+                       'december',
+                       'jan',
+                       'feb',
+                       'mar',
+                       'apr',
+                       'may',
+                       'jun',
+                       'jul',
+                       'aug',
+                       'sep',
+                       'oct',
+                       'nov',
+                       'dec',
+                       'sunday',
+                       'monday',
+                       'tuesday',
+                       'wednesday',
+                       'thursday',
+                       'friday',
+                       'saturday',
+                       'sun',
+                       'mon',
+                       'tue',
+                       'wed',
+                       'thu',
+                       'fri',
+                       'sat',
+                       'period-am',
+                       'period-pm',
+               ),
+               'dependencies' => array(
+                       'oojs-ui',
+               ),
+               'targets' => array( 'desktop', 'mobile' ),
+       ),
        'mediawiki.widgets.CategorySelector' => array(
                'scripts' => array(
                        'resources/src/mediawiki.widgets/mw.widgets.CategoryCapsuleItemWidget.js',
@@ -2092,6 +2164,7 @@ return array(
                'dependencies' => array(
                        'oojs-ui',
                ),
+               'targets' => array( 'desktop', 'mobile' ),
        ),
 
        /* es5-shim */
diff --git a/resources/lib/jquery/jquery.validate.js b/resources/lib/jquery/jquery.validate.js
deleted file mode 100644 (file)
index 72296a6..0000000
+++ /dev/null
@@ -1,1166 +0,0 @@
-/**
- * jQuery Validation Plugin 1.8.1
- *
- * http://bassistance.de/jquery-plugins/jquery-plugin-validation/
- * http://docs.jquery.com/Plugins/Validation
- *
- * Copyright (c) 2006 - 2011 Jörn Zaefferer
- *
- * Dual licensed under the MIT and GPL licenses:
- *   http://www.opensource.org/licenses/mit-license.php
- *   http://www.gnu.org/licenses/gpl.html
- */
-
-(function($) {
-
-$.extend($.fn, {
-       // http://docs.jquery.com/Plugins/Validation/validate
-       validate: function( options ) {
-
-               // if nothing is selected, return nothing; can't chain anyway
-               if (!this.length) {
-                       options && options.debug && window.console && console.warn( "nothing selected, can't validate, returning nothing" );
-                       return;
-               }
-
-               // check if a validator for this form was already created
-               var validator = $.data(this[0], 'validator');
-               if ( validator ) {
-                       return validator;
-               }
-
-               validator = new $.validator( options, this[0] );
-               $.data(this[0], 'validator', validator);
-
-               if ( validator.settings.onsubmit ) {
-
-                       // allow suppresing validation by adding a cancel class to the submit button
-                       this.find("input, button").filter(".cancel").click(function() {
-                               validator.cancelSubmit = true;
-                       });
-
-                       // when a submitHandler is used, capture the submitting button
-                       if (validator.settings.submitHandler) {
-                               this.find("input, button").filter(":submit").click(function() {
-                                       validator.submitButton = this;
-                               });
-                       }
-
-                       // validate the form on submit
-                       this.submit( function( event ) {
-                               if ( validator.settings.debug )
-                                       // prevent form submit to be able to see console output
-                                       event.preventDefault();
-
-                               function handle() {
-                                       if ( validator.settings.submitHandler ) {
-                                               if (validator.submitButton) {
-                                                       // insert a hidden input as a replacement for the missing submit button
-                                                       var hidden = $("<input type='hidden'/>").attr("name", validator.submitButton.name).val(validator.submitButton.value).appendTo(validator.currentForm);
-                                               }
-                                               validator.settings.submitHandler.call( validator, validator.currentForm );
-                                               if (validator.submitButton) {
-                                                       // and clean up afterwards; thanks to no-block-scope, hidden can be referenced
-                                                       hidden.remove();
-                                               }
-                                               return false;
-                                       }
-                                       return true;
-                               }
-
-                               // prevent submit for invalid forms or custom submit handlers
-                               if ( validator.cancelSubmit ) {
-                                       validator.cancelSubmit = false;
-                                       return handle();
-                               }
-                               if ( validator.form() ) {
-                                       if ( validator.pendingRequest ) {
-                                               validator.formSubmitted = true;
-                                               return false;
-                                       }
-                                       return handle();
-                               } else {
-                                       validator.focusInvalid();
-                                       return false;
-                               }
-                       });
-               }
-
-               return validator;
-       },
-       // http://docs.jquery.com/Plugins/Validation/valid
-       valid: function() {
-        if ( $(this[0]).is('form')) {
-            return this.validate().form();
-        } else {
-            var valid = true;
-            var validator = $(this[0].form).validate();
-            this.each(function() {
-                               valid &= validator.element(this);
-            });
-            return valid;
-        }
-    },
-       // attributes: space seperated list of attributes to retrieve and remove
-       removeAttrs: function(attributes) {
-               var result = {},
-                       $element = this;
-               $.each(attributes.split(/\s/), function(index, value) {
-                       result[value] = $element.attr(value);
-                       $element.removeAttr(value);
-               });
-               return result;
-       },
-       // http://docs.jquery.com/Plugins/Validation/rules
-       rules: function(command, argument) {
-               var element = this[0];
-
-               if (command) {
-                       var settings = $.data(element.form, 'validator').settings;
-                       var staticRules = settings.rules;
-                       var existingRules = $.validator.staticRules(element);
-                       switch(command) {
-                       case "add":
-                               $.extend(existingRules, $.validator.normalizeRule(argument));
-                               staticRules[element.name] = existingRules;
-                               if (argument.messages)
-                                       settings.messages[element.name] = $.extend( settings.messages[element.name], argument.messages );
-                               break;
-                       case "remove":
-                               if (!argument) {
-                                       delete staticRules[element.name];
-                                       return existingRules;
-                               }
-                               var filtered = {};
-                               $.each(argument.split(/\s/), function(index, method) {
-                                       filtered[method] = existingRules[method];
-                                       delete existingRules[method];
-                               });
-                               return filtered;
-                       }
-               }
-
-               var data = $.validator.normalizeRules(
-               $.extend(
-                       {},
-                       $.validator.metadataRules(element),
-                       $.validator.classRules(element),
-                       $.validator.attributeRules(element),
-                       $.validator.staticRules(element)
-               ), element);
-
-               // make sure required is at front
-               if (data.required) {
-                       var param = data.required;
-                       delete data.required;
-                       data = $.extend({required: param}, data);
-               }
-
-               return data;
-       }
-});
-
-// Custom selectors
-$.extend($.expr[":"], {
-       // http://docs.jquery.com/Plugins/Validation/blank
-       blank: function(a) {return !$.trim("" + a.value);},
-       // http://docs.jquery.com/Plugins/Validation/filled
-       filled: function(a) {return !!$.trim("" + a.value);},
-       // http://docs.jquery.com/Plugins/Validation/unchecked
-       unchecked: function(a) {return !a.checked;}
-});
-
-// constructor for validator
-$.validator = function( options, form ) {
-       this.settings = $.extend( true, {}, $.validator.defaults, options );
-       this.currentForm = form;
-       this.init();
-};
-
-$.validator.format = function(source, params) {
-       if ( arguments.length == 1 )
-               return function() {
-                       var args = $.makeArray(arguments);
-                       args.unshift(source);
-                       return $.validator.format.apply( this, args );
-               };
-       if ( arguments.length > 2 && params.constructor != Array  ) {
-               params = $.makeArray(arguments).slice(1);
-       }
-       if ( params.constructor != Array ) {
-               params = [ params ];
-       }
-       $.each(params, function(i, n) {
-               source = source.replace(new RegExp("\\{" + i + "\\}", "g"), n);
-       });
-       return source;
-};
-
-$.extend($.validator, {
-
-       defaults: {
-               messages: {},
-               groups: {},
-               rules: {},
-               errorClass: "error",
-               validClass: "valid",
-               errorElement: "label",
-               focusInvalid: true,
-               errorContainer: $( [] ),
-               errorLabelContainer: $( [] ),
-               onsubmit: true,
-               ignore: [],
-               ignoreTitle: false,
-               onfocusin: function(element) {
-                       this.lastActive = element;
-
-                       // hide error label and remove error class on focus if enabled
-                       if ( this.settings.focusCleanup && !this.blockFocusCleanup ) {
-                               this.settings.unhighlight && this.settings.unhighlight.call( this, element, this.settings.errorClass, this.settings.validClass );
-                               this.addWrapper(this.errorsFor(element)).hide();
-                       }
-               },
-               onfocusout: function(element) {
-                       if ( !this.checkable(element) && (element.name in this.submitted || !this.optional(element)) ) {
-                               this.element(element);
-                       }
-               },
-               onkeyup: function(element) {
-                       if ( element.name in this.submitted || element == this.lastElement ) {
-                               this.element(element);
-                       }
-               },
-               onclick: function(element) {
-                       // click on selects, radiobuttons and checkboxes
-                       if ( element.name in this.submitted )
-                               this.element(element);
-                       // or option elements, check parent select in that case
-                       else if (element.parentNode.name in this.submitted)
-                               this.element(element.parentNode);
-               },
-               highlight: function(element, errorClass, validClass) {
-                       if (element.type === 'radio') {
-                               this.findByName(element.name).addClass(errorClass).removeClass(validClass);
-                       } else {
-                               $(element).addClass(errorClass).removeClass(validClass);
-                       }
-               },
-               unhighlight: function(element, errorClass, validClass) {
-                       if (element.type === 'radio') {
-                               this.findByName(element.name).removeClass(errorClass).addClass(validClass);
-                       } else {
-                               $(element).removeClass(errorClass).addClass(validClass);
-                       }
-               }
-       },
-
-       // http://docs.jquery.com/Plugins/Validation/Validator/setDefaults
-       setDefaults: function(settings) {
-               $.extend( $.validator.defaults, settings );
-       },
-
-       messages: {
-               required: "This field is required.",
-               remote: "Please fix this field.",
-               email: "Please enter a valid email address.",
-               url: "Please enter a valid URL.",
-               date: "Please enter a valid date.",
-               dateISO: "Please enter a valid date (ISO).",
-               number: "Please enter a valid number.",
-               digits: "Please enter only digits.",
-               creditcard: "Please enter a valid credit card number.",
-               equalTo: "Please enter the same value again.",
-               accept: "Please enter a value with a valid extension.",
-               maxlength: $.validator.format("Please enter no more than {0} characters."),
-               minlength: $.validator.format("Please enter at least {0} characters."),
-               rangelength: $.validator.format("Please enter a value between {0} and {1} characters long."),
-               range: $.validator.format("Please enter a value between {0} and {1}."),
-               max: $.validator.format("Please enter a value less than or equal to {0}."),
-               min: $.validator.format("Please enter a value greater than or equal to {0}.")
-       },
-
-       autoCreateRanges: false,
-
-       prototype: {
-
-               init: function() {
-                       this.labelContainer = $(this.settings.errorLabelContainer);
-                       this.errorContext = this.labelContainer.length && this.labelContainer || $(this.currentForm);
-                       this.containers = $(this.settings.errorContainer).add( this.settings.errorLabelContainer );
-                       this.submitted = {};
-                       this.valueCache = {};
-                       this.pendingRequest = 0;
-                       this.pending = {};
-                       this.invalid = {};
-                       this.reset();
-
-                       var groups = (this.groups = {});
-                       $.each(this.settings.groups, function(key, value) {
-                               $.each(value.split(/\s/), function(index, name) {
-                                       groups[name] = key;
-                               });
-                       });
-                       var rules = this.settings.rules;
-                       $.each(rules, function(key, value) {
-                               rules[key] = $.validator.normalizeRule(value);
-                       });
-
-                       function delegate(event) {
-                               var validator = $.data(this[0].form, "validator"),
-                                       eventType = "on" + event.type.replace(/^validate/, "");
-                               validator.settings[eventType] && validator.settings[eventType].call(validator, this[0] );
-                       }
-                       $(this.currentForm)
-                               .validateDelegate(":text, :password, :file, select, textarea", "focusin focusout keyup", delegate)
-                               .validateDelegate(":radio, :checkbox, select, option", "click", delegate);
-
-                       if (this.settings.invalidHandler)
-                               $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/form
-               form: function() {
-                       this.checkForm();
-                       $.extend(this.submitted, this.errorMap);
-                       this.invalid = $.extend({}, this.errorMap);
-                       if (!this.valid())
-                               $(this.currentForm).triggerHandler("invalid-form", [this]);
-                       this.showErrors();
-                       return this.valid();
-               },
-
-               checkForm: function() {
-                       this.prepareForm();
-                       for ( var i = 0, elements = (this.currentElements = this.elements()); elements[i]; i++ ) {
-                               this.check( elements[i] );
-                       }
-                       return this.valid();
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/element
-               element: function( element ) {
-                       element = this.clean( element );
-                       this.lastElement = element;
-                       this.prepareElement( element );
-                       this.currentElements = $(element);
-                       var result = this.check( element );
-                       if ( result ) {
-                               delete this.invalid[element.name];
-                       } else {
-                               this.invalid[element.name] = true;
-                       }
-                       if ( !this.numberOfInvalids() ) {
-                               // Hide error containers on last error
-                               this.toHide = this.toHide.add( this.containers );
-                       }
-                       this.showErrors();
-                       return result;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/showErrors
-               showErrors: function(errors) {
-                       if(errors) {
-                               // add items to error list and map
-                               $.extend( this.errorMap, errors );
-                               this.errorList = [];
-                               for ( var name in errors ) {
-                                       this.errorList.push({
-                                               message: errors[name],
-                                               element: this.findByName(name)[0]
-                                       });
-                               }
-                               // remove items from success list
-                               this.successList = $.grep( this.successList, function(element) {
-                                       return !(element.name in errors);
-                               });
-                       }
-                       this.settings.showErrors
-                               ? this.settings.showErrors.call( this, this.errorMap, this.errorList )
-                               : this.defaultShowErrors();
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Validator/resetForm
-               resetForm: function() {
-                       if ( $.fn.resetForm )
-                               $( this.currentForm ).resetForm();
-                       this.submitted = {};
-                       this.prepareForm();
-                       this.hideErrors();
-                       this.elements().removeClass( this.settings.errorClass );
-               },
-
-               numberOfInvalids: function() {
-                       return this.objectLength(this.invalid);
-               },
-
-               objectLength: function( obj ) {
-                       var count = 0;
-                       for ( var i in obj )
-                               count++;
-                       return count;
-               },
-
-               hideErrors: function() {
-                       this.addWrapper( this.toHide ).hide();
-               },
-
-               valid: function() {
-                       return this.size() == 0;
-               },
-
-               size: function() {
-                       return this.errorList.length;
-               },
-
-               focusInvalid: function() {
-                       if( this.settings.focusInvalid ) {
-                               try {
-                                       $(this.findLastActive() || this.errorList.length && this.errorList[0].element || [])
-                                       .filter(":visible")
-                                       .focus()
-                                       // manually trigger focusin event; without it, focusin handler isn't called, findLastActive won't have anything to find
-                                       .trigger("focusin");
-                               } catch(e) {
-                                       // ignore IE throwing errors when focusing hidden elements
-                               }
-                       }
-               },
-
-               findLastActive: function() {
-                       var lastActive = this.lastActive;
-                       return lastActive && $.grep(this.errorList, function(n) {
-                               return n.element.name == lastActive.name;
-                       }).length == 1 && lastActive;
-               },
-
-               elements: function() {
-                       var validator = this,
-                               rulesCache = {};
-
-                       // select all valid inputs inside the form (no submit or reset buttons)
-                       return $(this.currentForm)
-                       .find("input, select, textarea")
-                       .not(":submit, :reset, :image, [disabled]")
-                       .not( this.settings.ignore )
-                       .filter(function() {
-                               !this.name && validator.settings.debug && window.console && console.error( "%o has no name assigned", this);
-
-                               // select only the first element for each name, and only those with rules specified
-                               if ( this.name in rulesCache || !validator.objectLength($(this).rules()) )
-                                       return false;
-
-                               rulesCache[this.name] = true;
-                               return true;
-                       });
-               },
-
-               clean: function( selector ) {
-                       return $( selector )[0];
-               },
-
-               errors: function() {
-                       return $( this.settings.errorElement + "." + this.settings.errorClass, this.errorContext );
-               },
-
-               reset: function() {
-                       this.successList = [];
-                       this.errorList = [];
-                       this.errorMap = {};
-                       this.toShow = $([]);
-                       this.toHide = $([]);
-                       this.currentElements = $([]);
-               },
-
-               prepareForm: function() {
-                       this.reset();
-                       this.toHide = this.errors().add( this.containers );
-               },
-
-               prepareElement: function( element ) {
-                       this.reset();
-                       this.toHide = this.errorsFor(element);
-               },
-
-               check: function( element ) {
-                       element = this.clean( element );
-
-                       // if radio/checkbox, validate first element in group instead
-                       if (this.checkable(element)) {
-                               element = this.findByName( element.name ).not(this.settings.ignore)[0];
-                       }
-
-                       var rules = $(element).rules();
-                       var dependencyMismatch = false;
-                       for (var method in rules ) {
-                               var rule = { method: method, parameters: rules[method] };
-                               try {
-                                       var result = $.validator.methods[method].call( this, element.value.replace(/\r/g, ""), element, rule.parameters );
-
-                                       // if a method indicates that the field is optional and therefore valid,
-                                       // don't mark it as valid when there are no other rules
-                                       if ( result == "dependency-mismatch" ) {
-                                               dependencyMismatch = true;
-                                               continue;
-                                       }
-                                       dependencyMismatch = false;
-
-                                       if ( result == "pending" ) {
-                                               this.toHide = this.toHide.not( this.errorsFor(element) );
-                                               return;
-                                       }
-
-                                       if( !result ) {
-                                               this.formatAndAdd( element, rule );
-                                               return false;
-                                       }
-                               } catch(e) {
-                                       this.settings.debug && window.console && console.log("exception occured when checking element " + element.id
-                                                + ", check the '" + rule.method + "' method", e);
-                                       throw e;
-                               }
-                       }
-                       if (dependencyMismatch)
-                               return;
-                       if ( this.objectLength(rules) )
-                               this.successList.push(element);
-                       return true;
-               },
-
-               // return the custom message for the given element and validation method
-               // specified in the element's "messages" metadata
-               customMetaMessage: function(element, method) {
-                       if (!$.metadata)
-                               return;
-
-                       var meta = this.settings.meta
-                               ? $(element).metadata()[this.settings.meta]
-                               : $(element).metadata();
-
-                       return meta && meta.messages && meta.messages[method];
-               },
-
-               // return the custom message for the given element name and validation method
-               customMessage: function( name, method ) {
-                       var m = this.settings.messages[name];
-                       return m && (m.constructor == String
-                               ? m
-                               : m[method]);
-               },
-
-               // return the first defined argument, allowing empty strings
-               findDefined: function() {
-                       for(var i = 0; i < arguments.length; i++) {
-                               if (arguments[i] !== undefined)
-                                       return arguments[i];
-                       }
-                       return undefined;
-               },
-
-               defaultMessage: function( element, method) {
-                       return this.findDefined(
-                               this.customMessage( element.name, method ),
-                               this.customMetaMessage( element, method ),
-                               // title is never undefined, so handle empty string as undefined
-                               !this.settings.ignoreTitle && element.title || undefined,
-                               $.validator.messages[method],
-                               "<strong>Warning: No message defined for " + element.name + "</strong>"
-                       );
-               },
-
-               formatAndAdd: function( element, rule ) {
-                       var message = this.defaultMessage( element, rule.method ),
-                               theregex = /\$?\{(\d+)\}/g;
-                       if ( typeof message == "function" ) {
-                               message = message.call(this, rule.parameters, element);
-                       } else if (theregex.test(message)) {
-                               message = jQuery.format(message.replace(theregex, '{$1}'), rule.parameters);
-                       }
-                       this.errorList.push({
-                               message: message,
-                               element: element
-                       });
-
-                       this.errorMap[element.name] = message;
-                       this.submitted[element.name] = message;
-               },
-
-               addWrapper: function(toToggle) {
-                       if ( this.settings.wrapper )
-                               toToggle = toToggle.add( toToggle.parent( this.settings.wrapper ) );
-                       return toToggle;
-               },
-
-               defaultShowErrors: function() {
-                       for ( var i = 0; this.errorList[i]; i++ ) {
-                               var error = this.errorList[i];
-                               this.settings.highlight && this.settings.highlight.call( this, error.element, this.settings.errorClass, this.settings.validClass );
-                               this.showLabel( error.element, error.message );
-                       }
-                       if( this.errorList.length ) {
-                               this.toShow = this.toShow.add( this.containers );
-                       }
-                       if (this.settings.success) {
-                               for ( var i = 0; this.successList[i]; i++ ) {
-                                       this.showLabel( this.successList[i] );
-                               }
-                       }
-                       if (this.settings.unhighlight) {
-                               for ( var i = 0, elements = this.validElements(); elements[i]; i++ ) {
-                                       this.settings.unhighlight.call( this, elements[i], this.settings.errorClass, this.settings.validClass );
-                               }
-                       }
-                       this.toHide = this.toHide.not( this.toShow );
-                       this.hideErrors();
-                       this.addWrapper( this.toShow ).show();
-               },
-
-               validElements: function() {
-                       return this.currentElements.not(this.invalidElements());
-               },
-
-               invalidElements: function() {
-                       return $(this.errorList).map(function() {
-                               return this.element;
-                       });
-               },
-
-               showLabel: function(element, message) {
-                       var label = this.errorsFor( element );
-                       if ( label.length ) {
-                               // refresh error/success class
-                               label.removeClass().addClass( this.settings.errorClass );
-
-                               // check if we have a generated label, replace the message then
-                               label.attr("generated") && label.html(message);
-                       } else {
-                               // create label
-                               label = $("<" + this.settings.errorElement + "/>")
-                                       .attr({"for":  this.idOrName(element), generated: true})
-                                       .addClass(this.settings.errorClass)
-                                       .html(message || "");
-                               if ( this.settings.wrapper ) {
-                                       // make sure the element is visible, even in IE
-                                       // actually showing the wrapped element is handled elsewhere
-                                       label = label.hide().show().wrap("<" + this.settings.wrapper + "/>").parent();
-                               }
-                               if ( !this.labelContainer.append(label).length )
-                                       this.settings.errorPlacement
-                                               ? this.settings.errorPlacement(label, $(element) )
-                                               : label.insertAfter(element);
-                       }
-                       if ( !message && this.settings.success ) {
-                               label.text("");
-                               typeof this.settings.success == "string"
-                                       ? label.addClass( this.settings.success )
-                                       : this.settings.success( label );
-                       }
-                       this.toShow = this.toShow.add(label);
-               },
-
-               errorsFor: function(element) {
-                       var name = this.idOrName(element);
-               return this.errors().filter(function() {
-                               return $(this).attr('for') == name;
-                       });
-               },
-
-               idOrName: function(element) {
-                       return this.groups[element.name] || (this.checkable(element) ? element.name : element.id || element.name);
-               },
-
-               checkable: function( element ) {
-                       return /radio|checkbox/i.test(element.type);
-               },
-
-               findByName: function( name ) {
-                       // select by name and filter by form for performance over form.find("[name=...]")
-                       var form = this.currentForm;
-                       return $(document.getElementsByName(name)).map(function(index, element) {
-                               return element.form == form && element.name == name && element  || null;
-                       });
-               },
-
-               getLength: function(value, element) {
-                       switch( element.nodeName.toLowerCase() ) {
-                       case 'select':
-                               return $("option:selected", element).length;
-                       case 'input':
-                               if( this.checkable( element) )
-                                       return this.findByName(element.name).filter(':checked').length;
-                       }
-                       return value.length;
-               },
-
-               depend: function(param, element) {
-                       return this.dependTypes[typeof param]
-                               ? this.dependTypes[typeof param](param, element)
-                               : true;
-               },
-
-               dependTypes: {
-                       "boolean": function(param, element) {
-                               return param;
-                       },
-                       "string": function(param, element) {
-                               return !!$(param, element.form).length;
-                       },
-                       "function": function(param, element) {
-                               return param(element);
-                       }
-               },
-
-               optional: function(element) {
-                       return !$.validator.methods.required.call(this, $.trim(element.value), element) && "dependency-mismatch";
-               },
-
-               startRequest: function(element) {
-                       if (!this.pending[element.name]) {
-                               this.pendingRequest++;
-                               this.pending[element.name] = true;
-                       }
-               },
-
-               stopRequest: function(element, valid) {
-                       this.pendingRequest--;
-                       // sometimes synchronization fails, make sure pendingRequest is never < 0
-                       if (this.pendingRequest < 0)
-                               this.pendingRequest = 0;
-                       delete this.pending[element.name];
-                       if ( valid && this.pendingRequest == 0 && this.formSubmitted && this.form() ) {
-                               $(this.currentForm).submit();
-                               this.formSubmitted = false;
-                       } else if (!valid && this.pendingRequest == 0 && this.formSubmitted) {
-                               $(this.currentForm).triggerHandler("invalid-form", [this]);
-                               this.formSubmitted = false;
-                       }
-               },
-
-               previousValue: function(element) {
-                       return $.data(element, "previousValue") || $.data(element, "previousValue", {
-                               old: null,
-                               valid: true,
-                               message: this.defaultMessage( element, "remote" )
-                       });
-               }
-
-       },
-
-       classRuleSettings: {
-               required: {required: true},
-               email: {email: true},
-               url: {url: true},
-               date: {date: true},
-               dateISO: {dateISO: true},
-               dateDE: {dateDE: true},
-               number: {number: true},
-               numberDE: {numberDE: true},
-               digits: {digits: true},
-               creditcard: {creditcard: true}
-       },
-
-       addClassRules: function(className, rules) {
-               className.constructor == String ?
-                       this.classRuleSettings[className] = rules :
-                       $.extend(this.classRuleSettings, className);
-       },
-
-       classRules: function(element) {
-               var rules = {};
-               var classes = $(element).attr('class');
-               classes && $.each(classes.split(' '), function() {
-                       if (this in $.validator.classRuleSettings) {
-                               $.extend(rules, $.validator.classRuleSettings[this]);
-                       }
-               });
-               return rules;
-       },
-
-       attributeRules: function(element) {
-               var rules = {};
-               var $element = $(element);
-
-               for (var method in $.validator.methods) {
-                       var value = $element.attr(method);
-                       if (value) {
-                               rules[method] = value;
-                       }
-               }
-
-               // maxlength may be returned as -1, 2147483647 (IE) and 524288 (safari) for text inputs
-               if (rules.maxlength && /-1|2147483647|524288/.test(rules.maxlength)) {
-                       delete rules.maxlength;
-               }
-
-               return rules;
-       },
-
-       metadataRules: function(element) {
-               if (!$.metadata) return {};
-
-               var meta = $.data(element.form, 'validator').settings.meta;
-               return meta ?
-                       $(element).metadata()[meta] :
-                       $(element).metadata();
-       },
-
-       staticRules: function(element) {
-               var rules = {};
-               var validator = $.data(element.form, 'validator');
-               if (validator.settings.rules) {
-                       rules = $.validator.normalizeRule(validator.settings.rules[element.name]) || {};
-               }
-               return rules;
-       },
-
-       normalizeRules: function(rules, element) {
-               // handle dependency check
-               $.each(rules, function(prop, val) {
-                       // ignore rule when param is explicitly false, eg. required:false
-                       if (val === false) {
-                               delete rules[prop];
-                               return;
-                       }
-                       if (val.param || val.depends) {
-                               var keepRule = true;
-                               switch (typeof val.depends) {
-                                       case "string":
-                                               keepRule = !!$(val.depends, element.form).length;
-                                               break;
-                                       case "function":
-                                               keepRule = val.depends.call(element, element);
-                                               break;
-                               }
-                               if (keepRule) {
-                                       rules[prop] = val.param !== undefined ? val.param : true;
-                               } else {
-                                       delete rules[prop];
-                               }
-                       }
-               });
-
-               // evaluate parameters
-               $.each(rules, function(rule, parameter) {
-                       rules[rule] = $.isFunction(parameter) ? parameter(element) : parameter;
-               });
-
-               // clean number parameters
-               $.each(['minlength', 'maxlength', 'min', 'max'], function() {
-                       if (rules[this]) {
-                               rules[this] = Number(rules[this]);
-                       }
-               });
-               $.each(['rangelength', 'range'], function() {
-                       if (rules[this]) {
-                               rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
-                       }
-               });
-
-               if ($.validator.autoCreateRanges) {
-                       // auto-create ranges
-                       if (rules.min && rules.max) {
-                               rules.range = [rules.min, rules.max];
-                               delete rules.min;
-                               delete rules.max;
-                       }
-                       if (rules.minlength && rules.maxlength) {
-                               rules.rangelength = [rules.minlength, rules.maxlength];
-                               delete rules.minlength;
-                               delete rules.maxlength;
-                       }
-               }
-
-               // To support custom messages in metadata ignore rule methods titled "messages"
-               if (rules.messages) {
-                       delete rules.messages;
-               }
-
-               return rules;
-       },
-
-       // Converts a simple string to a {string: true} rule, e.g., "required" to {required:true}
-       normalizeRule: function(data) {
-               if( typeof data == "string" ) {
-                       var transformed = {};
-                       $.each(data.split(/\s/), function() {
-                               transformed[this] = true;
-                       });
-                       data = transformed;
-               }
-               return data;
-       },
-
-       // http://docs.jquery.com/Plugins/Validation/Validator/addMethod
-       addMethod: function(name, method, message) {
-               $.validator.methods[name] = method;
-               $.validator.messages[name] = message != undefined ? message : $.validator.messages[name];
-               if (method.length < 3) {
-                       $.validator.addClassRules(name, $.validator.normalizeRule(name));
-               }
-       },
-
-       methods: {
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/required
-               required: function(value, element, param) {
-                       // check if dependency is met
-                       if ( !this.depend(param, element) )
-                               return "dependency-mismatch";
-                       switch( element.nodeName.toLowerCase() ) {
-                       case 'select':
-                               // could be an array for select-multiple or a string, both are fine this way
-                               var val = $(element).val();
-                               return val && val.length > 0;
-                       case 'input':
-                               if ( this.checkable(element) )
-                                       return this.getLength(value, element) > 0;
-                       default:
-                               return $.trim(value).length > 0;
-                       }
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/remote
-               remote: function(value, element, param) {
-                       if ( this.optional(element) )
-                               return "dependency-mismatch";
-
-                       var previous = this.previousValue(element);
-                       if (!this.settings.messages[element.name] )
-                               this.settings.messages[element.name] = {};
-                       previous.originalMessage = this.settings.messages[element.name].remote;
-                       this.settings.messages[element.name].remote = previous.message;
-
-                       param = typeof param == "string" && {url:param} || param;
-
-                       if ( this.pending[element.name] ) {
-                               return "pending";
-                       }
-                       if ( previous.old === value ) {
-                               return previous.valid;
-                       }
-
-                       previous.old = value;
-                       var validator = this;
-                       this.startRequest(element);
-                       var data = {};
-                       data[element.name] = value;
-                       $.ajax($.extend(true, {
-                               url: param,
-                               mode: "abort",
-                               port: "validate" + element.name,
-                               dataType: "json",
-                               data: data,
-                               success: function(response) {
-                                       validator.settings.messages[element.name].remote = previous.originalMessage;
-                                       var valid = response === true;
-                                       if ( valid ) {
-                                               var submitted = validator.formSubmitted;
-                                               validator.prepareElement(element);
-                                               validator.formSubmitted = submitted;
-                                               validator.successList.push(element);
-                                               validator.showErrors();
-                                       } else {
-                                               var errors = {};
-                                               var message = response || validator.defaultMessage( element, "remote" );
-                                               errors[element.name] = previous.message = $.isFunction(message) ? message(value) : message;
-                                               validator.showErrors(errors);
-                                       }
-                                       previous.valid = valid;
-                                       validator.stopRequest(element, valid);
-                               }
-                       }, param));
-                       return "pending";
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/minlength
-               minlength: function(value, element, param) {
-                       return this.optional(element) || this.getLength($.trim(value), element) >= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/maxlength
-               maxlength: function(value, element, param) {
-                       return this.optional(element) || this.getLength($.trim(value), element) <= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/rangelength
-               rangelength: function(value, element, param) {
-                       var length = this.getLength($.trim(value), element);
-                       return this.optional(element) || ( length >= param[0] && length <= param[1] );
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/min
-               min: function( value, element, param ) {
-                       return this.optional(element) || value >= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/max
-               max: function( value, element, param ) {
-                       return this.optional(element) || value <= param;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/range
-               range: function( value, element, param ) {
-                       return this.optional(element) || ( value >= param[0] && value <= param[1] );
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/email
-               email: function(value, element) {
-                       // contributed by Scott Gonzalez: http://projects.scottsplayground.com/email_address_validation/
-                       return this.optional(element) || /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?$/i.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/url
-               url: function(value, element) {
-                       // contributed by Scott Gonzalez: http://projects.scottsplayground.com/iri/
-                       return this.optional(element) || /^(https?|ftp):\/\/(((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:)*@)?(((\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5]))|((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.?)(:\d*)?)(\/((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)+(\/(([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)*)*)?)?(\?((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|[\uE000-\uF8FF]|\/|\?)*)?(\#((([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(%[\da-f]{2})|[!\$&'\(\)\*\+,;=]|:|@)|\/|\?)*)?$/i.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/date
-               date: function(value, element) {
-                       return this.optional(element) || !/Invalid|NaN/.test(new Date(value));
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/dateISO
-               dateISO: function(value, element) {
-                       return this.optional(element) || /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/number
-               number: function(value, element) {
-                       return this.optional(element) || /^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d+)?$/.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/digits
-               digits: function(value, element) {
-                       return this.optional(element) || /^\d+$/.test(value);
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/creditcard
-               // based on http://en.wikipedia.org/wiki/Luhn
-               creditcard: function(value, element) {
-                       if ( this.optional(element) )
-                               return "dependency-mismatch";
-                       // accept only digits and dashes
-                       if (/[^0-9-]+/.test(value))
-                               return false;
-                       var nCheck = 0,
-                               nDigit = 0,
-                               bEven = false;
-
-                       value = value.replace(/\D/g, "");
-
-                       for (var n = value.length - 1; n >= 0; n--) {
-                               var cDigit = value.charAt(n);
-                               var nDigit = parseInt(cDigit, 10);
-                               if (bEven) {
-                                       if ((nDigit *= 2) > 9)
-                                               nDigit -= 9;
-                               }
-                               nCheck += nDigit;
-                               bEven = !bEven;
-                       }
-
-                       return (nCheck % 10) == 0;
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/accept
-               accept: function(value, element, param) {
-                       param = typeof param == "string" ? param.replace(/,/g, '|') : "png|jpe?g|gif";
-                       return this.optional(element) || value.match(new RegExp(".(" + param + ")$", "i"));
-               },
-
-               // http://docs.jquery.com/Plugins/Validation/Methods/equalTo
-               equalTo: function(value, element, param) {
-                       // bind to the blur event of the target in order to revalidate whenever the target field is updated
-                       // TODO find a way to bind the event just once, avoiding the unbind-rebind overhead
-                       var target = $(param).unbind(".validate-equalTo").bind("blur.validate-equalTo", function() {
-                               $(element).valid();
-                       });
-                       return value == target.val();
-               }
-
-       }
-
-});
-
-// deprecated, use $.validator.format instead
-$.format = $.validator.format;
-
-})(jQuery);
-
-// ajax mode: abort
-// usage: $.ajax({ mode: "abort"[, port: "uniqueport"]});
-// if mode:"abort" is used, the previous request on that port (port can be undefined) is aborted via XMLHttpRequest.abort()
-;(function($) {
-       var pendingRequests = {};
-       // Use a prefilter if available (1.5+)
-       if ( $.ajaxPrefilter ) {
-               $.ajaxPrefilter(function(settings, _, xhr) {
-                       var port = settings.port;
-                       if (settings.mode == "abort") {
-                               if ( pendingRequests[port] ) {
-                                       pendingRequests[port].abort();
-                               }
-                               pendingRequests[port] = xhr;
-                       }
-               });
-       } else {
-               // Proxy ajax
-               var ajax = $.ajax;
-               $.ajax = function(settings) {
-                       var mode = ( "mode" in settings ? settings : $.ajaxSettings ).mode,
-                               port = ( "port" in settings ? settings : $.ajaxSettings ).port;
-                       if (mode == "abort") {
-                               if ( pendingRequests[port] ) {
-                                       pendingRequests[port].abort();
-                               }
-                               return (pendingRequests[port] = ajax.apply(this, arguments));
-                       }
-                       return ajax.apply(this, arguments);
-               };
-       }
-})(jQuery);
-
-// provides cross-browser focusin and focusout events
-// IE has native support, in other browsers, use event caputuring (neither bubbles)
-
-// provides delegate(type: String, delegate: Selector, handler: Callback) plugin for easier event delegation
-// handler is only called when $(event.target).is(delegate), in the scope of the jquery-object for event.target
-;(function($) {
-       // only implement if not provided by jQuery core (since 1.4)
-       // TODO verify if jQuery 1.4's implementation is compatible with older jQuery special-event APIs
-       if (!jQuery.event.special.focusin && !jQuery.event.special.focusout && document.addEventListener) {
-               $.each({
-                       focus: 'focusin',
-                       blur: 'focusout'
-               }, function( original, fix ){
-                       $.event.special[fix] = {
-                               setup:function() {
-                                       this.addEventListener( original, handler, true );
-                               },
-                               teardown:function() {
-                                       this.removeEventListener( original, handler, true );
-                               },
-                               handler: function(e) {
-                                       arguments[0] = $.event.fix(e);
-                                       arguments[0].type = fix;
-                                       return $.event.handle.apply(this, arguments);
-                               }
-                       };
-                       function handler(e) {
-                               e = $.event.fix(e);
-                               e.type = fix;
-                               return $.event.handle.call(this, e);
-                       }
-               });
-       };
-       $.extend($.fn, {
-               validateDelegate: function(delegate, type, handler) {
-                       return this.bind(type, function(event) {
-                               var target = $(event.target);
-                               if (target.is(delegate)) {
-                                       return handler.apply(target, arguments);
-                               }
-                       });
-               }
-       });
-})(jQuery);
index 3548239..a4b86ad 100644 (file)
@@ -21,6 +21,8 @@
        "ooui-dialog-process-dismiss": "Прапусьціць",
        "ooui-dialog-process-retry": "Паспрабаваць зноў",
        "ooui-dialog-process-continue": "Працягваць",
+       "ooui-selectfile-button-select": "Абраць файл",
        "ooui-selectfile-not-supported": "Выбар файлу не падтрымліваецца",
-       "ooui-selectfile-placeholder": "Ніводзін файл не абраны"
+       "ooui-selectfile-placeholder": "Ніводзін файл не абраны",
+       "ooui-selectfile-dragdrop-placeholder": "Перацягніце файл сюды"
 }
index f1a1a47..76e2614 100644 (file)
@@ -23,6 +23,7 @@
        "ooui-dialog-process-dismiss": "დამალვა",
        "ooui-dialog-process-retry": "კიდევ სცადეთ",
        "ooui-dialog-process-continue": "გაგრძელება",
+       "ooui-selectfile-button-select": "აირჩიეთ ფაილი",
        "ooui-selectfile-not-supported": "ფაილის არჩევა არ არის მხარდაჭერილი",
        "ooui-selectfile-placeholder": "ფაილი არ არის არჩეული"
 }
index 1d7317b..779ba7b 100644 (file)
@@ -15,5 +15,9 @@
        "ooui-dialog-process-error": "Бірдеңеден қате кетті",
        "ooui-dialog-process-dismiss": "Тоқтату",
        "ooui-dialog-process-retry": "Қайта байқап көріңіз",
-       "ooui-dialog-process-continue": "Жалғастыру"
+       "ooui-dialog-process-continue": "Жалғастыру",
+       "ooui-selectfile-button-select": "Файлды таңдау",
+       "ooui-selectfile-not-supported": "Файл таңдауды қолдамайды",
+       "ooui-selectfile-placeholder": "Файл таңдалмады",
+       "ooui-selectfile-dragdrop-placeholder": "Файлды мында жылжыту"
 }
index ec17c9b..ab6db14 100644 (file)
@@ -1,7 +1,8 @@
 {
        "@metadata": {
                "authors": [
-                       "Hosseinblue"
+                       "Hosseinblue",
+                       "Arash71"
                ]
        },
        "ooui-outline-control-move-down": "جاوواز کردن ئإ هووار",
@@ -11,7 +12,7 @@
        "ooui-toolgroup-expand": "ویشتر/فرۀتر",
        "ooui-toolgroup-collapse": "کۀمتر",
        "ooui-dialog-message-accept": "خوو/ باشد",
-       "ooui-dialog-message-reject": "ئآهووسانن-لغو",
+       "ooui-dialog-message-reject": "ئآهووسانن/لغو",
        "ooui-dialog-process-error": "مشکلی هۀس",
        "ooui-dialog-process-dismiss": "رد کردن",
        "ooui-dialog-process-retry": "دووآرۀ تلاش کۀ",
diff --git a/resources/lib/oojs-ui/i18n/vep.json b/resources/lib/oojs-ui/i18n/vep.json
new file mode 100644 (file)
index 0000000..b6ad092
--- /dev/null
@@ -0,0 +1,8 @@
+{
+       "@metadata": {
+               "authors": [
+                       "Sebranik"
+               ]
+       },
+       "ooui-toolgroup-expand": "Enamba"
+}
diff --git a/resources/lib/oojs-ui/i18n/war.json b/resources/lib/oojs-ui/i18n/war.json
new file mode 100644 (file)
index 0000000..b0ea30c
--- /dev/null
@@ -0,0 +1,19 @@
+{
+       "@metadata": {
+               "authors": [
+                       "JinJian"
+               ]
+       },
+       "ooui-outline-control-move-down": "Ibalhin paubos",
+       "ooui-outline-control-move-up": "Ibalhin paigbaw",
+       "ooui-outline-control-remove": "Tanggala",
+       "ooui-toolbar-more": "Damo pa",
+       "ooui-toolgroup-expand": "Damo pa",
+       "ooui-toolgroup-collapse": "Guruguti",
+       "ooui-dialog-message-accept": "OK",
+       "ooui-dialog-message-reject": "Igpabaliwaray",
+       "ooui-dialog-process-error": "Mayda sayop nga nahitabo",
+       "ooui-dialog-process-retry": "Utroha",
+       "ooui-dialog-process-continue": "Padayon",
+       "ooui-selectfile-button-select": "Pagpili hin file"
+}
index 5e1caa8..ef5a0da 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.14.1
+ * OOjs UI v0.15.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
- * Copyright 2011–2015 OOjs UI Team and other contributors.
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-12-08T21:43:53Z
+ * Date: 2016-01-12T23:06:40Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
           -moz-transition: border-color 100ms ease;
                transition: border-color 100ms ease;
        background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#dddddd');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#ddd');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
        background-image: -webkit-linear-gradient(top, #ffffff 0%, #dddddd 100%);
        background-image:    -moz-linear-gradient(top, #ffffff 0%, #dddddd 100%);
        color: black;
        border-color: #c9c9c9;
        background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#dddddd', endColorstr='#ffffff');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ddd', endColorstr='#fff');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #dddddd), color-stop(100%, #ffffff));
        background-image: -webkit-linear-gradient(top, #dddddd 0%, #ffffff 100%);
        background-image:    -moz-linear-gradient(top, #dddddd 0%, #ffffff 100%);
        border-color: rgba(0, 0, 0, 0.2);
        box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
        background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#f1f7fb', endColorstr='#ffffff');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#F1F7FB', endColorstr='#fff');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f1f7fb), color-stop(100%, #ffffff));
        background-image: -webkit-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        background-image:    -moz-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        border-bottom-right-radius: 0;
        box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
        background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#f1f7fb', endColorstr='#ffffff');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#F1F7FB', endColorstr='#fff');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f1f7fb), color-stop(100%, #ffffff));
        background-image: -webkit-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        background-image:    -moz-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        border-color: rgba(0, 0, 0, 0.1);
        box-shadow: inset 0 0.0875em 0.0875em 0 rgba(0, 0, 0, 0.07);
        background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#f1f7fb', endColorstr='#ffffff');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#F1F7FB', endColorstr='#fff');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #f1f7fb), color-stop(100%, #ffffff));
        background-image: -webkit-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
        background-image:    -moz-linear-gradient(top, #f1f7fb 0%, #ffffff 100%);
 .oo-ui-toolbar-bar {
        border-bottom: 1px solid #cccccc;
        background: #f8fbfd;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#f1f7fb');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#F1F7FB');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #f1f7fb));
        background-image: -webkit-linear-gradient(top, #ffffff 0%, #f1f7fb 100%);
        background-image:    -moz-linear-gradient(top, #ffffff 0%, #f1f7fb 100%);
        border: 1px solid #cccccc;
        margin-right: 0.5em;
        background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#dddddd', endColorstr='#ffffff');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ddd', endColorstr='#fff');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #dddddd), color-stop(100%, #ffffff));
        background-image: -webkit-linear-gradient(top, #dddddd 0%, #ffffff 100%);
        background-image:    -moz-linear-gradient(top, #dddddd 0%, #ffffff 100%);
           -moz-transition: left 250ms ease, margin-left 250ms ease;
                transition: left 250ms ease, margin-left 250ms ease;
        background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#dddddd');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#ddd');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
        background-image: -webkit-linear-gradient(top, #ffffff 0%, #dddddd 100%);
        background-image:    -moz-linear-gradient(top, #ffffff 0%, #dddddd 100%);
        height: 1.7em;
        line-height: 1.7em;
        background: #eeeeee;
-       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#ffffff', endColorstr='#dddddd');
+       filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorstr='#fff', endColorstr='#ddd');
        background-image: -webkit-gradient(linear, right top, right bottom, color-stop(0%, #ffffff), color-stop(100%, #dddddd));
        background-image: -webkit-linear-gradient(top, #ffffff 0%, #dddddd 100%);
        background-image:    -moz-linear-gradient(top, #ffffff 0%, #dddddd 100%);
index 7db559c..c287b0d 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.14.1
+ * OOjs UI v0.15.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
- * Copyright 2011–2015 OOjs UI Team and other contributors.
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-12-08T21:43:47Z
+ * Date: 2016-01-12T23:06:31Z
  */
 /**
  * @class
index a3ebbae..f97dcc9 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.14.1
+ * OOjs UI v0.15.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
- * Copyright 2011–2015 OOjs UI Team and other contributors.
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-12-08T21:43:53Z
+ * Date: 2016-01-12T23:06:40Z
  */
 @-webkit-keyframes oo-ui-progressBarWidget-slide {
        from {
                margin-left: 100%;
        }
 }
-@-ms-keyframes oo-ui-progressBarWidget-slide {
-       from {
-               margin-left: -40%;
-       }
-       to {
-               margin-left: 100%;
-       }
-}
 @-o-keyframes oo-ui-progressBarWidget-slide {
        from {
                margin-left: -40%;
        left: 0.2em;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-disabled > .oo-ui-buttonElement-button {
-       color: #ffffff;
        background: #dddddd;
+       color: #ffffff;
        border: 1px solid #dddddd;
 }
 .oo-ui-buttonElement-framed.oo-ui-widget-enabled > .oo-ui-buttonElement-button {
        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;
-       padding-left: 1em;
+       padding: 0.25em 0.25em 0.25em 1em;
 }
 .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 {
        list-style: none none;
-       margin: 0;
+       margin: 0.25em 0 0 0.25em;
        padding: 0;
-       margin-top: 0.25em;
-       margin-left: 0.25em;
 }
 .oo-ui-fieldLayout-messages > li {
        margin: 0;
        position: relative;
        margin: 0;
        padding: 0;
-       border: none;
+       border: 0;
 }
 .oo-ui-fieldsetLayout.oo-ui-iconElement > .oo-ui-iconElement-icon {
        display: block;
 }
 .oo-ui-panelLayout-framed {
        border: 1px solid #aaaaaa;
-       border-radius: 0.2em;
+       border-radius: 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 .oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
        color: #555555;
 }
 .oo-ui-toolbar-bar .oo-ui-toolbar-bar {
-       border: none;
+       border: 0;
        background: none;
        box-shadow: none;
 }
        display: block;
        cursor: pointer;
        padding: 0.25em 0.5em;
-       border: none;
+       border: 0;
 }
 .oo-ui-optionWidget.oo-ui-widget-disabled {
        cursor: default;
        background-color: transparent;
 }
 .oo-ui-radioOptionWidget.oo-ui-labelElement .oo-ui-labelElement-label {
-       padding: 0.25em;
-       padding-left: 1em;
+       padding: 0.25em 0.25em 0.25em 1em;
 }
 .oo-ui-radioOptionWidget .oo-ui-radioInputWidget {
        margin-right: 0;
        margin-left: 0;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on {
-       background: #347bff;
+       background-color: #347bff;
        border-color: #347bff;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled.oo-ui-toggleWidget-on .oo-ui-toggleSwitchWidget-grip {
-       background: #ffffff;
+       background-color: #ffffff;
        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover {
        border-color: #2962cc;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:hover.oo-ui-toggleWidget-on {
-       background: #2962cc;
+       background-color: #2962cc;
        border-color: #2962cc;
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-enabled:focus {
 }
 .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: #ffffff;
+       background-color: #ffffff;
        box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
 }
 .oo-ui-toggleSwitchWidget.oo-ui-widget-disabled {
        max-width: 50em;
        background-color: #ffffff;
        border: 1px solid #cccccc;
-       border-radius: 0.1em;
+       border-radius: 2px;
        overflow: hidden;
 }
 .oo-ui-progressBarWidget-bar {
 .oo-ui-popupWidget-popup {
        background-color: #ffffff;
        border: 1px solid #aaaaaa;
-       border-radius: 0.2em;
+       border-radius: 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 .oo-ui-popupWidget-anchored .oo-ui-popupWidget-popup {
           -moz-box-sizing: border-box;
                box-sizing: border-box;
        border: 1px solid #cccccc;
-       border-radius: 0.1em;
+       border-radius: 2px;
        padding-left: 1em;
        vertical-align: middle;
 }
        font-family: inherit;
        background-color: #ffffff;
        color: black;
-       border: solid 1px #cccccc;
+       border: 1px solid #cccccc;
        box-shadow: inset 0 0 0 0 #347bff;
-       border-radius: 0.1em;
+       border-radius: 2px;
        -webkit-transition: box-shadow 100ms ease;
           -moz-transition: box-shadow 100ms ease;
                transition: box-shadow 100ms ease;
 }
 .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: red;
-       box-shadow: inset 0 0 0 0 red;
+       border-color: #ff0000;
+       box-shadow: inset 0 0 0 0 #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: red;
-       box-shadow: inset 0 0 0 0.1em red;
+       border-color: #ff0000;
+       box-shadow: inset 0 0 0 0.1em #ff0000;
 }
 .oo-ui-textInputWidget.oo-ui-widget-disabled input,
 .oo-ui-textInputWidget.oo-ui-widget-disabled textarea {
        background-color: #ffffff;
        margin-top: -1px;
        border: 1px solid #aaaaaa;
-       border-radius: 0 0 0.2em 0.2em;
+       border-radius: 0 0 2px 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
 .oo-ui-menuSelectWidget input {
        height: 2.275em;
        line-height: 1.275;
        border: 1px solid #cccccc;
-       border-radius: 0.1em;
+       border-radius: 2px;
 }
 .oo-ui-dropdownWidget-handle .oo-ui-indicatorElement-indicator {
        right: 0;
        height: 2.4em;
        background-color: #ffffff;
        border: 1px solid #cccccc;
-       border-radius: 0.1em;
+       border-radius: 2px;
 }
 .oo-ui-selectFileWidget-info > .oo-ui-indicatorElement-indicator {
        right: 0;
        margin-right: 0.5em;
        padding: 0.15em 0.25em;
        border: 1px solid #cccccc;
-       border-radius: 0.1em;
+       border-radius: 2px;
        -webkit-box-sizing: border-box;
           -moz-box-sizing: border-box;
                box-sizing: border-box;
        background-repeat: no-repeat;
 }
 .oo-ui-capsuleMultiSelectWidget-handle > .oo-ui-capsuleMultiSelectWidget-content > input {
-       border: none;
+       border: 0;
        line-height: 1.675em;
-       margin: 0;
-       margin-left: 0.2em;
+       margin: 0 0 0 0.2em;
        padding: 0;
        font-size: inherit;
        font-family: inherit;
        background-color: #eeeeee;
        border: 1px solid #cccccc;
        color: #555555;
-       border-radius: 0.1em;
+       border-radius: 2px;
 }
 .oo-ui-capsuleItemWidget > .oo-ui-iconElement-icon {
        cursor: pointer;
        padding: 1em;
        border: 1px solid #ff9e9e;
        background-color: #fff7f7;
-       border-radius: 0.25em;
+       border-radius: 2px;
 }
 .oo-ui-windowManager-modal > .oo-ui-dialog {
        position: fixed;
 }
 .oo-ui-windowManager-modal.oo-ui-windowManager-floating > .oo-ui-dialog > .oo-ui-window-frame {
        border: 1px solid #aaaaaa;
-       border-radius: 0.2em;
+       border-radius: 2px;
        box-shadow: 0 0.15em 0 0 rgba(0, 0, 0, 0.15);
 }
index 677ab5b..210fec9 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.14.1
+ * OOjs UI v0.15.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
- * Copyright 2011–2015 OOjs UI Team and other contributors.
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-12-08T21:43:47Z
+ * Date: 2016-01-12T23:06:31Z
  */
 /**
  * @class
index c77bfd7..f280853 100644 (file)
@@ -1,12 +1,12 @@
 /*!
- * OOjs UI v0.14.1
+ * OOjs UI v0.15.0
  * https://www.mediawiki.org/wiki/OOjs_UI
  *
- * Copyright 2011–2015 OOjs UI Team and other contributors.
+ * Copyright 2011–2016 OOjs UI Team and other contributors.
  * Released under the MIT license
  * http://oojs.mit-license.org
  *
- * Date: 2015-12-08T21:43:47Z
+ * Date: 2016-01-12T23:06:31Z
  */
 ( function ( OO ) {
 
@@ -44,6 +44,17 @@ OO.ui.Keys = {
        SPACE: 32
 };
 
+/**
+ * Constants for MouseEvent.which
+ *
+ * @property {Object}
+ */
+OO.ui.MouseButtons = {
+       LEFT: 1,
+       MIDDLE: 2,
+       RIGHT: 3
+};
+
 /**
  * @property {Number}
  */
@@ -248,35 +259,27 @@ OO.ui.debounce = function ( func, wait, immediate ) {
 };
 
 /**
- * Proxy for `node.addEventListener( eventName, handler, true )`, if the browser supports it.
- * Otherwise falls back to non-capturing event listeners.
+ * Proxy for `node.addEventListener( eventName, handler, true )`.
  *
  * @param {HTMLElement} node
  * @param {string} eventName
  * @param {Function} handler
+ * @deprecated
  */
 OO.ui.addCaptureEventListener = function ( node, eventName, handler ) {
-       if ( node.addEventListener ) {
-               node.addEventListener( eventName, handler, true );
-       } else {
-               node.attachEvent( 'on' + eventName, handler );
-       }
+       node.addEventListener( eventName, handler, true );
 };
 
 /**
- * Proxy for `node.removeEventListener( eventName, handler, true )`, if the browser supports it.
- * Otherwise falls back to non-capturing event listeners.
+ * Proxy for `node.removeEventListener( eventName, handler, true )`.
  *
  * @param {HTMLElement} node
  * @param {string} eventName
  * @param {Function} handler
+ * @deprecated
  */
 OO.ui.removeCaptureEventListener = function ( node, eventName, handler ) {
-       if ( node.addEventListener ) {
-               node.removeEventListener( eventName, handler, true );
-       } else {
-               node.detachEvent( 'on' + eventName, handler );
-       }
+       node.removeEventListener( eventName, handler, true );
 };
 
 /**
@@ -1487,9 +1490,7 @@ OO.ui.Element.static.getDocument = function ( obj ) {
  */
 OO.ui.Element.static.getWindow = function ( obj ) {
        var doc = this.getDocument( obj );
-       // Support: IE 8
-       // Standard Document.defaultView is IE9+
-       return doc.parentWindow || doc.defaultView;
+       return doc.defaultView;
 };
 
 /**
@@ -1604,14 +1605,8 @@ OO.ui.Element.static.getRelativePosition = function ( $element, $anchor ) {
  */
 OO.ui.Element.static.getBorders = function ( el ) {
        var doc = el.ownerDocument,
-               // Support: IE 8
-               // Standard Document.defaultView is IE9+
-               win = doc.parentWindow || doc.defaultView,
-               style = win && win.getComputedStyle ?
-                       win.getComputedStyle( el, null ) :
-                       // Support: IE 8
-                       // Standard getComputedStyle() is IE9+
-                       el.currentStyle,
+               win = doc.defaultView,
+               style = win.getComputedStyle( el, null ),
                $el = $( el ),
                top = parseFloat( style ? style.borderTopWidth : $el.css( 'borderTopWidth' ) ) || 0,
                left = parseFloat( style ? style.borderLeftWidth : $el.css( 'borderLeftWidth' ) ) || 0,
@@ -1636,9 +1631,7 @@ OO.ui.Element.static.getBorders = function ( el ) {
 OO.ui.Element.static.getDimensions = function ( el ) {
        var $el, $win,
                doc = el.ownerDocument || el.document,
-               // Support: IE 8
-               // Standard Document.defaultView is IE9+
-               win = doc.parentWindow || doc.defaultView;
+               win = doc.defaultView;
 
        if ( win === el || el === doc.documentElement ) {
                $win = $( win );
@@ -2924,7 +2917,7 @@ OO.ui.Dialog.static.name = '';
  *
  * The title can be specified as a plaintext string, a {@link OO.ui.mixin.LabelElement Label} node, or a function
  * that will produce a Label node or string. The title can also be specified with data passed to the
- * constructor (see #getSetupProcess). In this case, the static value will be overriden.
+ * constructor (see #getSetupProcess). In this case, the static value will be overridden.
  *
  * @abstract
  * @static
@@ -2937,7 +2930,7 @@ OO.ui.Dialog.static.title = '';
  * An array of configured {@link OO.ui.ActionWidget action widgets}.
  *
  * Actions can also be specified with data passed to the constructor (see #getSetupProcess). In this case, the static
- * value will be overriden.
+ * value will be overridden.
  *
  * [2]: https://www.mediawiki.org/wiki/OOjs_UI/Windows/Process_Dialogs#Action_sets
  *
@@ -2967,7 +2960,7 @@ OO.ui.Dialog.static.escapable = true;
  */
 OO.ui.Dialog.prototype.onDialogKeyDown = function ( e ) {
        if ( e.which === OO.ui.Keys.ESCAPE ) {
-               this.close();
+               this.executeAction( '' );
                e.preventDefault();
                e.stopPropagation();
        }
@@ -4740,13 +4733,13 @@ OO.ui.mixin.ButtonElement.prototype.setButtonElement = function ( $button ) {
  * @param {jQuery.Event} e Mouse down event
  */
 OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
-       if ( this.isDisabled() || e.which !== 1 ) {
+       if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
                return;
        }
        this.$element.addClass( 'oo-ui-buttonElement-pressed' );
        // Run the mouseup handler no matter where the mouse is when the button is let go, so we can
        // reliably remove the pressed class
-       OO.ui.addCaptureEventListener( this.getElementDocument(), 'mouseup', this.onMouseUpHandler );
+       this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
        // Prevent change of focus unless specifically configured otherwise
        if ( this.constructor.static.cancelButtonMouseDownEvents ) {
                return false;
@@ -4760,12 +4753,12 @@ OO.ui.mixin.ButtonElement.prototype.onMouseDown = function ( e ) {
  * @param {jQuery.Event} e Mouse up event
  */
 OO.ui.mixin.ButtonElement.prototype.onMouseUp = function ( e ) {
-       if ( this.isDisabled() || e.which !== 1 ) {
+       if ( this.isDisabled() || e.which !== OO.ui.MouseButtons.LEFT ) {
                return;
        }
        this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
        // Stop listening for mouseup, since we only needed this once
-       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup', this.onMouseUpHandler );
+       this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
 };
 
 /**
@@ -4776,7 +4769,7 @@ OO.ui.mixin.ButtonElement.prototype.onMouseUp = function ( e ) {
  * @fires click
  */
 OO.ui.mixin.ButtonElement.prototype.onClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === 1 ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
                if ( this.emit( 'click' ) ) {
                        return false;
                }
@@ -4796,7 +4789,7 @@ OO.ui.mixin.ButtonElement.prototype.onKeyDown = function ( e ) {
        this.$element.addClass( 'oo-ui-buttonElement-pressed' );
        // Run the keyup handler no matter where the key is when the button is let go, so we can
        // reliably remove the pressed class
-       OO.ui.addCaptureEventListener( this.getElementDocument(), 'keyup', this.onKeyUpHandler );
+       this.getElementDocument().addEventListener( 'keyup', this.onKeyUpHandler, true );
 };
 
 /**
@@ -4811,7 +4804,7 @@ OO.ui.mixin.ButtonElement.prototype.onKeyUp = function ( e ) {
        }
        this.$element.removeClass( 'oo-ui-buttonElement-pressed' );
        // Stop listening for keyup, since we only needed this once
-       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'keyup', this.onKeyUpHandler );
+       this.getElementDocument().removeEventListener( 'keyup', this.onKeyUpHandler, true );
 };
 
 /**
@@ -7491,7 +7484,7 @@ OO.ui.Tool.static.autoAddToGroup = true;
 /**
  * Check if this tool is compatible with given data.
  *
- * This is a stub that can be overriden to provide support for filtering tools based on an
+ * This is a stub that can be overridden to provide support for filtering tools based on an
  * arbitrary piece of information  (e.g., where the cursor is in a document). The implementation
  * must also call this method so that the compatibility check can be performed.
  *
@@ -8310,13 +8303,13 @@ OO.ui.ToolGroup.prototype.updateDisabled = function () {
 OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
        if (
                !this.isDisabled() &&
-               ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
        ) {
                this.pressed = this.getTargetTool( e );
                if ( this.pressed ) {
                        this.pressed.setActive( true );
-                       OO.ui.addCaptureEventListener( this.getElementDocument(), 'mouseup', this.onCapturedMouseKeyUpHandler );
-                       OO.ui.addCaptureEventListener( this.getElementDocument(), 'keyup', this.onCapturedMouseKeyUpHandler );
+                       this.getElementDocument().addEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
+                       this.getElementDocument().addEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
                }
                return false;
        }
@@ -8329,8 +8322,8 @@ OO.ui.ToolGroup.prototype.onMouseKeyDown = function ( e ) {
  * @param {Event} e Mouse up or key up event
  */
 OO.ui.ToolGroup.prototype.onCapturedMouseKeyUp = function ( e ) {
-       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup', this.onCapturedMouseKeyUpHandler );
-       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'keyup', this.onCapturedMouseKeyUpHandler );
+       this.getElementDocument().removeEventListener( 'mouseup', this.onCapturedMouseKeyUpHandler, true );
+       this.getElementDocument().removeEventListener( 'keyup', this.onCapturedMouseKeyUpHandler, true );
        // onMouseKeyUp may be called a second time, depending on where the mouse is when the button is
        // released, but since `this.pressed` will no longer be true, the second call will be ignored.
        this.onMouseKeyUp( e );
@@ -8347,7 +8340,7 @@ OO.ui.ToolGroup.prototype.onMouseKeyUp = function ( e ) {
 
        if (
                !this.isDisabled() && this.pressed && this.pressed === tool &&
-               ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
        ) {
                this.pressed.onSelect();
                this.pressed = null;
@@ -11815,7 +11808,7 @@ OO.ui.PopupToolGroup.prototype.onMouseKeyUp = function ( e ) {
        // Only close toolgroup when a tool was actually selected
        if (
                !this.isDisabled() && this.pressed && this.pressed === this.getTargetTool( e ) &&
-               ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
        ) {
                this.setActive( false );
        }
@@ -11831,7 +11824,7 @@ OO.ui.PopupToolGroup.prototype.onMouseKeyUp = function ( e ) {
 OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
        if (
                !this.isDisabled() &&
-               ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
        ) {
                return false;
        }
@@ -11846,7 +11839,7 @@ OO.ui.PopupToolGroup.prototype.onHandleMouseKeyUp = function ( e ) {
 OO.ui.PopupToolGroup.prototype.onHandleMouseKeyDown = function ( e ) {
        if (
                !this.isDisabled() &&
-               ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
        ) {
                this.setActive( !this.active );
                return false;
@@ -11865,8 +11858,8 @@ OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
        if ( this.active !== value ) {
                this.active = value;
                if ( value ) {
-                       OO.ui.addCaptureEventListener( this.getElementDocument(), 'mouseup', this.onBlurHandler );
-                       OO.ui.addCaptureEventListener( this.getElementDocument(), 'keyup', this.onBlurHandler );
+                       this.getElementDocument().addEventListener( 'mouseup', this.onBlurHandler, true );
+                       this.getElementDocument().addEventListener( 'keyup', this.onBlurHandler, true );
 
                        this.$clippable.css( 'left', '' );
                        // Try anchoring the popup to the left first
@@ -11894,8 +11887,8 @@ OO.ui.PopupToolGroup.prototype.setActive = function ( value ) {
                                } );
                        }
                } else {
-                       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup', this.onBlurHandler );
-                       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'keyup', this.onBlurHandler );
+                       this.getElementDocument().removeEventListener( 'mouseup', this.onBlurHandler, true );
+                       this.getElementDocument().removeEventListener( 'keyup', this.onBlurHandler, true );
                        this.$element.removeClass(
                                'oo-ui-popupToolGroup-active oo-ui-popupToolGroup-left  oo-ui-popupToolGroup-right'
                        );
@@ -12092,7 +12085,7 @@ OO.ui.ListToolGroup.prototype.onMouseKeyUp = function ( e ) {
        // Do not close the popup when the user wants to show more/fewer tools
        if (
                $( e.target ).closest( '.oo-ui-tool-name-more-fewer' ).length &&
-               ( e.which === 1 || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
+               ( e.which === OO.ui.MouseButtons.LEFT || e.which === OO.ui.Keys.SPACE || e.which === OO.ui.Keys.ENTER )
        ) {
                // HACK: Prevent the popup list from being hidden. Skip the PopupToolGroup implementation (which
                // hides the popup list when a tool is selected) and call ToolGroup's implementation directly.
@@ -13863,7 +13856,7 @@ OO.ui.CapsuleMultiSelectWidget.prototype.onPopupFocusOut = function () {
  * @param {jQuery.Event} e Mouse down event
  */
 OO.ui.CapsuleMultiSelectWidget.prototype.onMouseDown = function ( e ) {
-       if ( e.which === 1 ) {
+       if ( e.which === OO.ui.MouseButtons.LEFT ) {
                this.focus();
                return false;
        } else {
@@ -14286,7 +14279,7 @@ OO.ui.DropdownWidget.prototype.onMenuSelect = function ( item ) {
  * @param {jQuery.Event} e Mouse click event
  */
 OO.ui.DropdownWidget.prototype.onClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === 1 ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
                this.menu.toggle();
        }
        return false;
@@ -16051,7 +16044,7 @@ OO.ui.TextInputWidget.static.gatherPreInfuseState = function ( node, config ) {
  * @fires icon
  */
 OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
-       if ( e.which === 1 ) {
+       if ( e.which === OO.ui.MouseButtons.LEFT ) {
                this.$input[ 0 ].focus();
                return false;
        }
@@ -16065,7 +16058,7 @@ OO.ui.TextInputWidget.prototype.onIconMouseDown = function ( e ) {
  * @fires indicator
  */
 OO.ui.TextInputWidget.prototype.onIndicatorMouseDown = function ( e ) {
-       if ( e.which === 1 ) {
+       if ( e.which === OO.ui.MouseButtons.LEFT ) {
                if ( this.type === 'search' ) {
                        // Clear the text field
                        this.setValue( '' );
@@ -16349,7 +16342,7 @@ OO.ui.TextInputWidget.prototype.isAutosizing = function () {
  * @chainable
  */
 OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
-       var textRange, isBackwards, start, end,
+       var isBackwards, start, end,
                input = this.$input[ 0 ];
 
        to = to || from;
@@ -16360,16 +16353,7 @@ OO.ui.TextInputWidget.prototype.selectRange = function ( from, to ) {
 
        this.focus();
 
-       if ( input.setSelectionRange ) {
-               input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
-       } else if ( input.createTextRange ) {
-               // IE 8 and below
-               textRange = input.createTextRange();
-               textRange.collapse( true );
-               textRange.moveStart( 'character', start );
-               textRange.moveEnd( 'character', end - start );
-               textRange.select();
-       }
+       input.setSelectionRange( start, end, isBackwards ? 'backward' : 'forward' );
        return this;
 };
 
@@ -16836,7 +16820,7 @@ OO.ui.ComboBoxInputWidget.prototype.onInputChange = function ( value ) {
  * @param {jQuery.Event} e Mouse click event
  */
 OO.ui.ComboBoxInputWidget.prototype.onIndicatorClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === 1 ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
                this.menu.toggle();
                this.$input[ 0 ].focus();
        }
@@ -17591,7 +17575,7 @@ OO.ui.OutlineOptionWidget.prototype.setMovable = function ( movable ) {
  *
  * Removability is used by {@link OO.ui.OutlineControlsWidget outline controls}.
  *
- * @param {boolean} movable Item is removable
+ * @param {boolean} removable Item is removable
  * @chainable
  */
 OO.ui.OutlineOptionWidget.prototype.setRemovable = function ( removable ) {
@@ -17815,7 +17799,7 @@ OO.ui.PopupWidget.prototype.onMouseDown = function ( e ) {
  */
 OO.ui.PopupWidget.prototype.bindMouseDownListener = function () {
        // Capture clicks outside popup
-       OO.ui.addCaptureEventListener( this.getElementWindow(), 'mousedown', this.onMouseDownHandler );
+       this.getElementWindow().addEventListener( 'mousedown', this.onMouseDownHandler, true );
 };
 
 /**
@@ -17835,7 +17819,7 @@ OO.ui.PopupWidget.prototype.onCloseButtonClick = function () {
  * @private
  */
 OO.ui.PopupWidget.prototype.unbindMouseDownListener = function () {
-       OO.ui.removeCaptureEventListener( this.getElementWindow(), 'mousedown', this.onMouseDownHandler );
+       this.getElementWindow().removeEventListener( 'mousedown', this.onMouseDownHandler, true );
 };
 
 /**
@@ -17861,7 +17845,7 @@ OO.ui.PopupWidget.prototype.onDocumentKeyDown = function ( e ) {
  * @private
  */
 OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
-       OO.ui.addCaptureEventListener( this.getElementWindow(), 'keydown', this.onDocumentKeyDownHandler );
+       this.getElementWindow().addEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
 };
 
 /**
@@ -17870,7 +17854,7 @@ OO.ui.PopupWidget.prototype.bindKeyDownListener = function () {
  * @private
  */
 OO.ui.PopupWidget.prototype.unbindKeyDownListener = function () {
-       OO.ui.removeCaptureEventListener( this.getElementWindow(), 'keydown', this.onDocumentKeyDownHandler );
+       this.getElementWindow().removeEventListener( 'keydown', this.onDocumentKeyDownHandler, true );
 };
 
 /**
@@ -18449,22 +18433,14 @@ OO.ui.SelectWidget.static.passAllFilter = function () {
 OO.ui.SelectWidget.prototype.onMouseDown = function ( e ) {
        var item;
 
-       if ( !this.isDisabled() && e.which === 1 ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
                this.togglePressed( true );
                item = this.getTargetItem( e );
                if ( item && item.isSelectable() ) {
                        this.pressItem( item );
                        this.selecting = item;
-                       OO.ui.addCaptureEventListener(
-                               this.getElementDocument(),
-                               'mouseup',
-                               this.onMouseUpHandler
-                       );
-                       OO.ui.addCaptureEventListener(
-                               this.getElementDocument(),
-                               'mousemove',
-                               this.onMouseMoveHandler
-                       );
+                       this.getElementDocument().addEventListener( 'mouseup', this.onMouseUpHandler, true );
+                       this.getElementDocument().addEventListener( 'mousemove', this.onMouseMoveHandler, true );
                }
        }
        return false;
@@ -18486,16 +18462,14 @@ OO.ui.SelectWidget.prototype.onMouseUp = function ( e ) {
                        this.selecting = item;
                }
        }
-       if ( !this.isDisabled() && e.which === 1 && this.selecting ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT && this.selecting ) {
                this.pressItem( null );
                this.chooseItem( this.selecting );
                this.selecting = null;
        }
 
-       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mouseup',
-               this.onMouseUpHandler );
-       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mousemove',
-               this.onMouseMoveHandler );
+       this.getElementDocument().removeEventListener( 'mouseup', this.onMouseUpHandler, true );
+       this.getElementDocument().removeEventListener( 'mousemove', this.onMouseMoveHandler, true );
 
        return false;
 };
@@ -18615,7 +18589,7 @@ OO.ui.SelectWidget.prototype.onKeyDown = function ( e ) {
  * @protected
  */
 OO.ui.SelectWidget.prototype.bindKeyDownListener = function () {
-       OO.ui.addCaptureEventListener( this.getElementWindow(), 'keydown', this.onKeyDownHandler );
+       this.getElementWindow().addEventListener( 'keydown', this.onKeyDownHandler, true );
 };
 
 /**
@@ -18624,7 +18598,7 @@ OO.ui.SelectWidget.prototype.bindKeyDownListener = function () {
  * @protected
  */
 OO.ui.SelectWidget.prototype.unbindKeyDownListener = function () {
-       OO.ui.removeCaptureEventListener( this.getElementWindow(), 'keydown', this.onKeyDownHandler );
+       this.getElementWindow().removeEventListener( 'keydown', this.onKeyDownHandler, true );
 };
 
 /**
@@ -18733,7 +18707,7 @@ OO.ui.SelectWidget.prototype.getItemMatcher = function ( s, exact ) {
  * @protected
  */
 OO.ui.SelectWidget.prototype.bindKeyPressListener = function () {
-       OO.ui.addCaptureEventListener( this.getElementWindow(), 'keypress', this.onKeyPressHandler );
+       this.getElementWindow().addEventListener( 'keypress', this.onKeyPressHandler, true );
 };
 
 /**
@@ -18745,7 +18719,7 @@ OO.ui.SelectWidget.prototype.bindKeyPressListener = function () {
  * @protected
  */
 OO.ui.SelectWidget.prototype.unbindKeyPressListener = function () {
-       OO.ui.removeCaptureEventListener( this.getElementWindow(), 'keypress', this.onKeyPressHandler );
+       this.getElementWindow().removeEventListener( 'keypress', this.onKeyPressHandler, true );
        this.clearKeyPressBuffer();
 };
 
@@ -19551,12 +19525,12 @@ OO.ui.MenuSelectWidget.prototype.toggle = function ( visible ) {
 
                        // Auto-hide
                        if ( this.autoHide ) {
-                               OO.ui.addCaptureEventListener( this.getElementDocument(), 'mousedown', this.onDocumentMouseDownHandler );
+                               this.getElementDocument().addEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
                        }
                } else {
                        this.unbindKeyDownListener();
                        this.unbindKeyPressListener();
-                       OO.ui.removeCaptureEventListener( this.getElementDocument(), 'mousedown', this.onDocumentMouseDownHandler );
+                       this.getElementDocument().removeEventListener( 'mousedown', this.onDocumentMouseDownHandler, true );
                        this.toggleClipping( false );
                }
        }
@@ -20145,7 +20119,7 @@ OO.mixinClass( OO.ui.ToggleSwitchWidget, OO.ui.mixin.TabIndexedElement );
  * @param {jQuery.Event} e Mouse click event
  */
 OO.ui.ToggleSwitchWidget.prototype.onClick = function ( e ) {
-       if ( !this.isDisabled() && e.which === 1 ) {
+       if ( !this.isDisabled() && e.which === OO.ui.MouseButtons.LEFT ) {
                this.setValue( !this.value );
        }
        return false;
diff --git a/resources/lib/oojs-ui/themes/apex/icons-content.json b/resources/lib/oojs-ui/themes/apex/icons-content.json
new file mode 100644 (file)
index 0000000..0886fa6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+       "prefix": "oo-ui-icon",
+       "intro": "@import '../../../../src/styles/common';",
+       "images": {
+               "articleRedirect": { "file": {
+                       "ltr": "images/icons/articleRedirect-ltr.svg",
+                       "rtl": "images/icons/articleRedirect-rtl.svg"
+               } }
+       }
+}
index a6abce5..ab04d36 100644 (file)
                        "ltr": "images/icons/find-ltr.svg",
                        "rtl": "images/icons/find-rtl.svg"
                } },
-               "insert": { "file": "images/icons/add.svg" },
+               "language": { "file": {
+                       "ltr": "images/icons/language-ltr.svg",
+                       "rtl": "images/icons/language-rtl.svg"
+               } },
                "layout": { "file": {
                        "ltr": "images/icons/layout-ltr.svg",
                        "rtl": "images/icons/layout-rtl.svg"
                        "ltr": "images/icons/newline-ltr.svg",
                        "rtl": "images/icons/newline-rtl.svg"
                } },
-               "redirect": { "file": {
-                       "ltr": "images/icons/redirect-ltr.svg",
-                       "rtl": "images/icons/redirect-rtl.svg"
-               } },
                "noWikiText": { "file": {
                        "ltr": "images/icons/noWikiText-ltr.svg",
                        "rtl": "images/icons/noWikiText-rtl.svg"
@@ -47,8 +46,8 @@
                        "rtl": "images/icons/quotesAdd-rtl.svg"
                } },
                "redirect": { "file": {
-                       "ltr": "images/icons/redirect-ltr.svg",
-                       "rtl": "images/icons/redirect-rtl.svg"
+                       "ltr": "images/icons/articleRedirect-ltr.svg",
+                       "rtl": "images/icons/articleRedirect-rtl.svg"
                } },
                "searchCaseSensitive": { "file": "images/icons/case-sensitive.svg" },
                "searchRegularExpression": { "file": "images/icons/regular-expression.svg" },
@@ -71,8 +70,8 @@
                        "rtl": "images/icons/templateAdd-rtl.svg"
                } },
                "translation": { "file": {
-                       "ltr": "images/icons/translation-ltr.svg",
-                       "rtl": "images/icons/translation-rtl.svg"
+                       "ltr": "images/icons/language-ltr.svg",
+                       "rtl": "images/icons/language-rtl.svg"
                } },
                "wikiText": { "file": "images/icons/wikiText.svg" }
        }
index 4fb736c..decae86 100644 (file)
                                "en": "images/icons/underline-u.svg"
                        }
                } },
-               "textLanguage": { "file": "images/icons/language.svg" },
+               "textLanguage": { "file": {
+                       "ltr": "images/icons/language-ltr.svg",
+                       "rtl": "images/icons/language-rtl.svg"
+               } },
                "textDirLTR": { "file": "images/icons/text-dir-lefttoright.svg" },
                "textDirRTL": { "file": "images/icons/text-dir-righttoleft.svg" },
                "textStyle": { "file": "images/icons/text-style.svg" }
index 091d5d7..b5fbbed 100644 (file)
@@ -29,7 +29,6 @@
                        "rtl": "images/icons/move-rtl.svg"
                } },
                "notice": { "file": "images/icons/notice.svg" },
-               "picture": { "file": "images/icons/image.svg" },
                "previous": { "file": {
                        "ltr": "images/icons/move-rtl.svg",
                        "rtl": "images/icons/move-ltr.svg"
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.png
new file mode 100644 (file)
index 0000000..8b0920f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-ltr.svg
new file mode 100644 (file)
index 0000000..028c64c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="article-redirect">
+        <path id="arrow" d="M18.1 14.2L23 18l-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
+        <path id="page" d="M5 3v13c0 1.7 1.3 3 3 3h3.375c-.157-.205-.3-.43-.438-.656-.42-.688-.77-1.483-.843-2.344H7v-1h3.125l.125-1H7v-1h3.375l.03-.188.283.188H16v1h-3.906l.22.156c.523.375 1.065.64 1.592.844H16v.406c.208-.013.418-.07.625-.094.068-1.294.125-3.874.125-3.874l1.25.968V3H5zm2 2h4v1H7V5zm5 0h4v5h-4V5zM7 7h4v1H7V7zm0 2h4v1H7V9zm0 2h9v1H7v-1z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.png
new file mode 100644 (file)
index 0000000..709673f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/articleRedirect-rtl.svg
new file mode 100644 (file)
index 0000000..6a9c683
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="article-redirect">
+        <path id="arrow" d="M5.9 14.2L1 18l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3"/>
+        <path id="page" d="M19 3v13c0 1.7-1.3 3-3 3h-3.375c.157-.205.3-.43.438-.656.42-.688.77-1.483.843-2.344H17v-1h-3.125l-.125-1H17v-1h-3.375l-.03-.188-.283.188H8v1h3.906l-.22.156c-.523.375-1.065.64-1.592.844H8v.406c-.208-.013-.418-.07-.625-.094-.068-1.294-.125-3.874-.125-3.874L6 12.405V3zm-2 2h-4v1h4zm-5 0H8v5h4zm5 2h-4v1h4zm0 2h-4v1h4zm0 2H8v1h9z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png
new file mode 100644 (file)
index 0000000..ef61b8b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/language-ltr.svg
new file mode 100644 (file)
index 0000000..4bf074d
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="translation">
+        <path id="english" d="M14.34 9l-3.53 10h2.064l.72-2.406h3.624l.72 2.406H20L16.465 9h-2.12zm1.065 1.53L16.75 15h-2.69z"/>
+        <path id="chinese" d="M8.97 4.22c-.43.29-.88.616-1.25.874l.186.312c.14.194.275.393.407.594H4.47v1.47h1.593c.43 1.41 1.11 2.624 2.03 3.624-1.008.664-2.192 1.248-3.624 1.75L4 13c.317.487.714.976 1.03 1.375l.25-.094c1.593-.59 2.91-1.266 4.032-2.06.818.628 1.71 1.158 2.657 1.592l.56-1.624c-.725-.334-1.36-.692-1.905-1.063.284-.28.59-.634.906-1.156.46-.717.777-1.572 1-2.5h1.658V6h-4.063c-.283-.552-.596-1.083-.97-1.53l-.186-.25zM7.72 7.47h3.186c-.32 1.075-.83 1.937-1.53 2.624-.713-.705-1.26-1.568-1.657-2.625zm6.31 5.31l-.467 1.658c.292-.514.577-1.075.812-1.532l-.344-.125z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png
new file mode 100644 (file)
index 0000000..8cd9282
Binary files /dev/null and b/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/language-rtl.svg
new file mode 100644 (file)
index 0000000..9b1ac39
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="translation">
+        <path id="english" d="M7.53 9L4 19h2.063l.72-2.406h3.624l.72 2.406h2.062L9.653 9h-2.12zm1.064 1.53L9.938 15H7.25z"/>
+        <path id="chinese" d="M14.594 4.22c-.43.29-.88.616-1.25.874l.187.312c.14.194.276.393.408.594h-3.844v1.47h1.594c.43 1.41 1.11 2.624 2.03 3.624-.662.437-1.413.82-2.25 1.187l.563 1.564c1.11-.48 2.056-1.022 2.908-1.625 1.187.91 2.514 1.63 3.968 2.124l.282.094c.292-.514.577-1.075.812-1.532l-.375-.125c-1.38-.49-2.49-1.052-3.375-1.655.284-.28.59-.634.906-1.156.46-.717.776-1.572 1-2.5h1.657V6H15.75c-.283-.552-.596-1.083-.97-1.53l-.186-.25zm-1.25 3.25h3.187c-.318 1.075-.828 1.937-1.53 2.624-.712-.705-1.26-1.568-1.656-2.625zM9.97 12.874L9.624 13c.196.3.406.594.625.875l-.28-1z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/language.png b/resources/lib/oojs-ui/themes/apex/images/icons/language.png
deleted file mode 100644 (file)
index b4f0875..0000000
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/language.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/language.svg b/resources/lib/oojs-ui/themes/apex/images/icons/language.svg
deleted file mode 100644 (file)
index 956aba1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="language">
-        <path id="japanese" d="M17.533 9.81l.27-.59 1.042.407-.18.363c.66.27 1.1.468 1.312.59.33.21.618.513.86.904.21.393.316.846.316 1.358 0 .786-.302 1.48-.905 2.083-.604.634-1.66 1.057-3.17 1.268-.12-.36-.257-.68-.407-.95.97-.15 1.65-.333 2.04-.545.455-.21.786-.48 1-.813.21-.303.313-.663.313-1.087 0-.482-.135-.905-.406-1.27-.33-.33-.8-.588-1.402-.77-.332.635-.648 1.118-.95 1.45-.242.332-.694.906-1.358 1.72.09.394.18.71.272.952l-1.043.362-.09-.498c-.424.36-.802.617-1.134.77-.36.15-.664.226-.905.226-.303 0-.574-.136-.814-.407-.243-.3-.362-.68-.362-1.132 0-.6.137-1.143.408-1.63.24-.45.603-.89 1.086-1.31.273-.24.726-.53 1.36-.86 0-.27.03-.8.09-1.584-.514.03-.92.045-1.222.045-.393 0-.71-.015-.95-.045l-.047-1.04c.726.09 1.495.134 2.31.134 0-.15.076-.74.228-1.767l1.177.184c-.15.542-.256 1.04-.316 1.493.24-.03.542-.077.905-.138.36-.06.573-.09.634-.09s.647-.15 1.765-.453l.045 1.04c-.966.242-2.144.44-3.53.59-.063.662-.093 1.085-.093 1.265.664-.15 1.285-.225 1.858-.225zm-2.672 3.893c-.06-.48-.132-1.252-.223-2.31-.573.424-1.04.86-1.403 1.313-.302.423-.45.875-.45 1.358 0 .24.043.438.135.588.09.092.194.137.315.137.364 0 .908-.365 1.63-1.09zm.775-2.763c0 .483.03 1.088.09 1.81.604-.904 1.057-1.598 1.36-2.08-.575.06-1.06.15-1.45.27z"/>
-        <path id="english" d="M9.497 15.98h1.85L8.265 7.033h-1.85l-3.08 8.95h1.85L5.74 14h3.21l.547 1.98zm-3.49-3.376L7.34 8.822l1.343 3.782H6.008z"/>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.png
deleted file mode 100644 (file)
index 18ceb35..0000000
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/redirect-ltr.svg
deleted file mode 100644 (file)
index be25d43..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="create_redirect">
-        <g>
-            <path d="M17.7 2.4c-.3-.3-.7-.4-1.2-.4H4.4v16.2c0 .5.1.8.4 1.1s.7.7 1.2.7h10.2c-.6-.2-1.2-.5-1.9-1-.4-.3-.8-.6-1.2-1l-.5-.6H6.4V16h5.4s-.4-1.5-.4-2h-5v-1h9v1c.4.1 1.1.1 1.5.1.4 0 .7 0 1.1-.1V3.5c.1-.5-.1-.9-.3-1.1zM12.5 4h3v4.5h-3V4zM6.4 4h4v1.6h-4V4zm0 3h4v1.5h-4V7zm0 3h9v1.5h-9V10zm12.7 3.1l4.9 3.8-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
-        </g>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.png
deleted file mode 100644 (file)
index dc9b0e6..0000000
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/redirect-rtl.svg
deleted file mode 100644 (file)
index a41d178..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="create_redirect">
-        <g id="g3264">
-            <path d="M6.3 2.4c.3-.3.7-.4 1.2-.4h12.1v16.2c0 .5-.1.8-.4 1.1-.3.3-.7.7-1.2.7H7.8c.6-.2 1.2-.5 1.9-1 .4-.3.8-.6 1.2-1l.5-.6h6.2V16h-5.4s.4-1.5.4-2h5v-1h-9v1c-.4.1-1.1.1-1.5.1-.4 0-.7 0-1.1-.1V3.5c-.1-.5.1-.9.3-1.1zM11.5 4h-3v4.5h3V4zm6.1 0h-4v1.6h4V4zm0 3h-4v1.5h4V7zm0 3h-9v1.5h9V10z" id="path3266"/>
-            <path d="M4.9 13.1L0 16.9l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3" id="path3268"/>
-        </g>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.png b/resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.png
deleted file mode 100644 (file)
index 1025461..0000000
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.svg b/resources/lib/oojs-ui/themes/apex/images/icons/translation-ltr.svg
deleted file mode 100644 (file)
index 8954a21..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?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.1 13.1C9.3 11 8.4 8.8 8.1 8h4.7l.7-2H8V3H6v3H1v2h5c-.2.9-1.3 4.8-5.1 7.6l1.2 1.6c2.7-2 4.3-4.5 5.1-6.4.7 1.3 1.7 3 3.2 4.5l.7-2.2zm1.4 6.9l1.3-4h5.3l1.3 4h2.2L18 6h-3l-4.7 14h2.2zm4-12l2 6h-4l2-6z"/>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.png b/resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.png
deleted file mode 100644 (file)
index 38066d6..0000000
Binary files a/resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.svg b/resources/lib/oojs-ui/themes/apex/images/icons/translation-rtl.svg
deleted file mode 100644 (file)
index 44ba971..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?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.4 13.1c1.8-2.1 2.7-4.3 3-5.1h-4.7L10 6h5.5V3h2v3h5v2h-5c.2.9 1.3 4.8 5.1 7.6l-1.2 1.6c-2.7-2-4.3-4.5-5.1-6.4-.7 1.3-1.7 3-3.2 4.5l-.7-2.2zM11 20l-1.3-4H4.4l-1.3 4H.9L5.5 6h3l4.7 14H11zM7 8l-2 6h4L7 8z" id="path704"/>
-</svg>
index 18c8dd5..73af2f1 100644 (file)
                        "ltr": "images/icons/articleSearch-ltr.svg",
                        "rtl": "images/icons/articleSearch-rtl.svg"
                } },
+               "articleRedirect": { "file": {
+                       "ltr": "images/icons/articleRedirect-ltr.svg",
+                       "rtl": "images/icons/articleRedirect-rtl.svg"
+               } },
                "book": { "file": {
                        "ltr": "images/icons/book-ltr.svg",
                        "rtl": "images/icons/book-rtl.svg"
index d981728..27e3b0e 100644 (file)
                        "ltr": "images/icons/find-ltr.svg",
                        "rtl": "images/icons/find-rtl.svg"
                } },
-               "insert": { "file": "images/icons/add.svg" },
+               "language": { "file": {
+                       "ltr": "images/icons/language-ltr.svg",
+                       "rtl": "images/icons/language-rtl.svg"
+               } },
                "layout": { "file": {
                        "ltr": "images/icons/layout-ltr.svg",
                        "rtl": "images/icons/layout-rtl.svg"
                        "ltr": "images/icons/newline-ltr.svg",
                        "rtl": "images/icons/newline-rtl.svg"
                } },
-               "redirect": { "file": {
-                       "ltr": "images/icons/redirect-ltr.svg",
-                       "rtl": "images/icons/redirect-rtl.svg"
-               } },
                "noWikiText": { "file": {
                        "ltr": "images/icons/noWikiText-ltr.svg",
                        "rtl": "images/icons/noWikiText-rtl.svg"
@@ -53,8 +52,8 @@
                        "rtl": "images/icons/quotesAdd-rtl.svg"
                } },
                "redirect": { "file": {
-                       "ltr": "images/icons/redirect-ltr.svg",
-                       "rtl": "images/icons/redirect-rtl.svg"
+                       "ltr": "images/icons/articleRedirect-ltr.svg",
+                       "rtl": "images/icons/articleRedirect-rtl.svg"
                } },
                "searchCaseSensitive": { "file": "images/icons/case-sensitive.svg" },
                "searchRegularExpression": { "file": "images/icons/regular-expression.svg" },
@@ -77,8 +76,8 @@
                        "rtl": "images/icons/templateAdd-rtl.svg"
                } },
                "translation": { "file": {
-                       "ltr": "images/icons/translation-ltr.svg",
-                       "rtl": "images/icons/translation-rtl.svg"
+                       "ltr": "images/icons/language-ltr.svg",
+                       "rtl": "images/icons/language-rtl.svg"
                } },
                "wikiText": { "file": "images/icons/wikiText.svg" }
        }
index 48af33a..e070154 100644 (file)
                                "en": "images/icons/underline-u.svg"
                        }
                } },
-               "textLanguage": { "file": "images/icons/language.svg" },
+               "textLanguage": { "file": {
+                       "ltr": "images/icons/language-ltr.svg",
+                       "rtl": "images/icons/language-rtl.svg"
+               } },
                "textDirLTR": { "file": "images/icons/text-dir-lefttoright.svg" },
                "textDirRTL": { "file": "images/icons/text-dir-righttoleft.svg" },
                "textStyle": { "file": "images/icons/text-style.svg" }
index fc058cd..0c3b4eb 100644 (file)
                        "rtl": "images/icons/move-rtl.svg"
                } },
                "notice": { "file": "images/icons/notice.svg" },
-               "picture": { "file": {
-                       "ltr": "images/icons/image-rtl.svg",
-                       "rtl": "images/icons/image-ltr.svg"
-               } },
                "previous": { "file": {
                        "ltr": "images/icons/move-rtl.svg",
                        "rtl": "images/icons/move-ltr.svg"
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.png
new file mode 100644 (file)
index 0000000..9261197
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr-invert.svg
new file mode 100644 (file)
index 0000000..5317700
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+    <g id="article-redirect">
+        <path id="arrow" d="M18.1 14.2L23 18l-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
+        <path id="page" d="M5 3v13c0 1.7 1.3 3 3 3h3.375c-.157-.205-.3-.43-.438-.656-.42-.688-.77-1.483-.843-2.344H7v-1h3.125l.125-1H7v-1h3.375l.03-.188.283.188H16v1h-3.906l.22.156c.523.375 1.065.64 1.592.844H16v.406c.208-.013.418-.07.625-.094.068-1.294.125-3.874.125-3.874l1.25.968V3H5zm2 2h4v1H7V5zm5 0h4v5h-4V5zM7 7h4v1H7V7zm0 2h4v1H7V9zm0 2h9v1H7v-1z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.png
new file mode 100644 (file)
index 0000000..8b0920f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-ltr.svg
new file mode 100644 (file)
index 0000000..028c64c
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="article-redirect">
+        <path id="arrow" d="M18.1 14.2L23 18l-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
+        <path id="page" d="M5 3v13c0 1.7 1.3 3 3 3h3.375c-.157-.205-.3-.43-.438-.656-.42-.688-.77-1.483-.843-2.344H7v-1h3.125l.125-1H7v-1h3.375l.03-.188.283.188H16v1h-3.906l.22.156c.523.375 1.065.64 1.592.844H16v.406c.208-.013.418-.07.625-.094.068-1.294.125-3.874.125-3.874l1.25.968V3H5zm2 2h4v1H7V5zm5 0h4v5h-4V5zM7 7h4v1H7V7zm0 2h4v1H7V9zm0 2h9v1H7v-1z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.png
new file mode 100644 (file)
index 0000000..8dd4d77
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl-invert.svg
new file mode 100644 (file)
index 0000000..a0f43ab
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><style>* { fill: #FFFFFF }</style>
+    <g id="article-redirect">
+        <path id="arrow" d="M5.9 14.2L1 18l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3"/>
+        <path id="page" d="M19 3v13c0 1.7-1.3 3-3 3h-3.375c.157-.205.3-.43.438-.656.42-.688.77-1.483.843-2.344H17v-1h-3.125l-.125-1H17v-1h-3.375l-.03-.188-.283.188H8v1h3.906l-.22.156c-.523.375-1.065.64-1.592.844H8v.406c-.208-.013-.418-.07-.625-.094-.068-1.294-.125-3.874-.125-3.874L6 12.405V3zm-2 2h-4v1h4zm-5 0H8v5h4zm5 2h-4v1h4zm0 2h-4v1h4zm0 2H8v1h9z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.png
new file mode 100644 (file)
index 0000000..709673f
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/articleRedirect-rtl.svg
new file mode 100644 (file)
index 0000000..6a9c683
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="article-redirect">
+        <path id="arrow" d="M5.9 14.2L1 18l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3"/>
+        <path id="page" d="M19 3v13c0 1.7-1.3 3-3 3h-3.375c.157-.205.3-.43.438-.656.42-.688.77-1.483.843-2.344H17v-1h-3.125l-.125-1H17v-1h-3.375l-.03-.188-.283.188H8v1h3.906l-.22.156c-.523.375-1.065.64-1.592.844H8v.406c-.208-.013-.418-.07-.625-.094-.068-1.294-.125-3.874-.125-3.874L6 12.405V3zm-2 2h-4v1h4zm-5 0H8v5h4zm5 2h-4v1h4zm0 2h-4v1h4zm0 2H8v1h9z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.png
deleted file mode 100644 (file)
index ad816df..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-invert.svg
deleted file mode 100644 (file)
index abc618e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?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>
-    <g id="language">
-        <path id="japanese" d="M17.533 9.81l.27-.59 1.042.407-.18.363c.66.27 1.1.468 1.312.59.33.21.618.513.86.904.21.393.316.846.316 1.358 0 .786-.302 1.48-.905 2.083-.604.634-1.66 1.057-3.17 1.268-.12-.36-.257-.68-.407-.95.97-.15 1.65-.333 2.04-.545.455-.21.786-.48 1-.813.21-.303.313-.663.313-1.087 0-.482-.135-.905-.406-1.27-.33-.33-.8-.588-1.402-.77-.332.635-.648 1.118-.95 1.45-.242.332-.694.906-1.358 1.72.09.394.18.71.272.952l-1.043.362-.09-.498c-.424.36-.802.617-1.134.77-.36.15-.664.226-.905.226-.303 0-.574-.136-.814-.407-.243-.3-.362-.68-.362-1.132 0-.6.137-1.143.408-1.63.24-.45.603-.89 1.086-1.31.273-.24.726-.53 1.36-.86 0-.27.03-.8.09-1.584-.514.03-.92.045-1.222.045-.393 0-.71-.015-.95-.045l-.047-1.04c.726.09 1.495.134 2.31.134 0-.15.076-.74.228-1.767l1.177.184c-.15.542-.256 1.04-.316 1.493.24-.03.542-.077.905-.138.36-.06.573-.09.634-.09s.647-.15 1.765-.453l.045 1.04c-.966.242-2.144.44-3.53.59-.063.662-.093 1.085-.093 1.265.664-.15 1.285-.225 1.858-.225zm-2.672 3.893c-.06-.48-.132-1.252-.223-2.31-.573.424-1.04.86-1.403 1.313-.302.423-.45.875-.45 1.358 0 .24.043.438.135.588.09.092.194.137.315.137.364 0 .908-.365 1.63-1.09zm.775-2.763c0 .483.03 1.088.09 1.81.604-.904 1.057-1.598 1.36-2.08-.575.06-1.06.15-1.45.27z"/>
-        <path id="english" d="M9.497 15.98h1.85L8.265 7.033h-1.85l-3.08 8.95h1.85L5.74 14h3.21l.547 1.98zm-3.49-3.376L7.34 8.822l1.343 3.782H6.008z"/>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png
new file mode 100644 (file)
index 0000000..36aaf52
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr-invert.svg
new file mode 100644 (file)
index 0000000..c67db52
--- /dev/null
@@ -0,0 +1,7 @@
+<?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>
+    <g id="translation">
+        <path id="english" d="M14.34 9l-3.53 10h2.064l.72-2.406h3.624l.72 2.406H20L16.465 9h-2.12zm1.065 1.53L16.75 15h-2.69z"/>
+        <path id="chinese" d="M8.97 4.22c-.43.29-.88.616-1.25.874l.186.312c.14.194.275.393.407.594H4.47v1.47h1.593c.43 1.41 1.11 2.624 2.03 3.624-1.008.664-2.192 1.248-3.624 1.75L4 13c.317.487.714.976 1.03 1.375l.25-.094c1.593-.59 2.91-1.266 4.032-2.06.818.628 1.71 1.158 2.657 1.592l.56-1.624c-.725-.334-1.36-.692-1.905-1.063.284-.28.59-.634.906-1.156.46-.717.777-1.572 1-2.5h1.658V6h-4.063c-.283-.552-.596-1.083-.97-1.53l-.186-.25zM7.72 7.47h3.186c-.32 1.075-.83 1.937-1.53 2.624-.713-.705-1.26-1.568-1.657-2.625zm6.31 5.31l-.467 1.658c.292-.514.577-1.075.812-1.532l-.344-.125z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png
new file mode 100644 (file)
index 0000000..ef61b8b
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-ltr.svg
new file mode 100644 (file)
index 0000000..4bf074d
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="translation">
+        <path id="english" d="M14.34 9l-3.53 10h2.064l.72-2.406h3.624l.72 2.406H20L16.465 9h-2.12zm1.065 1.53L16.75 15h-2.69z"/>
+        <path id="chinese" d="M8.97 4.22c-.43.29-.88.616-1.25.874l.186.312c.14.194.275.393.407.594H4.47v1.47h1.593c.43 1.41 1.11 2.624 2.03 3.624-1.008.664-2.192 1.248-3.624 1.75L4 13c.317.487.714.976 1.03 1.375l.25-.094c1.593-.59 2.91-1.266 4.032-2.06.818.628 1.71 1.158 2.657 1.592l.56-1.624c-.725-.334-1.36-.692-1.905-1.063.284-.28.59-.634.906-1.156.46-.717.777-1.572 1-2.5h1.658V6h-4.063c-.283-.552-.596-1.083-.97-1.53l-.186-.25zM7.72 7.47h3.186c-.32 1.075-.83 1.937-1.53 2.624-.713-.705-1.26-1.568-1.657-2.625zm6.31 5.31l-.467 1.658c.292-.514.577-1.075.812-1.532l-.344-.125z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png
new file mode 100644 (file)
index 0000000..aad12ac
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl-invert.svg
new file mode 100644 (file)
index 0000000..204f565
--- /dev/null
@@ -0,0 +1,7 @@
+<?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>
+    <g id="translation">
+        <path id="english" d="M7.53 9L4 19h2.063l.72-2.406h3.624l.72 2.406h2.062L9.653 9h-2.12zm1.064 1.53L9.938 15H7.25z"/>
+        <path id="chinese" d="M14.594 4.22c-.43.29-.88.616-1.25.874l.187.312c.14.194.276.393.408.594h-3.844v1.47h1.594c.43 1.41 1.11 2.624 2.03 3.624-.662.437-1.413.82-2.25 1.187l.563 1.564c1.11-.48 2.056-1.022 2.908-1.625 1.187.91 2.514 1.63 3.968 2.124l.282.094c.292-.514.577-1.075.812-1.532l-.375-.125c-1.38-.49-2.49-1.052-3.375-1.655.284-.28.59-.634.906-1.156.46-.717.776-1.572 1-2.5h1.657V6H15.75c-.283-.552-.596-1.083-.97-1.53l-.186-.25zm-1.25 3.25h3.187c-.318 1.075-.828 1.937-1.53 2.624-.712-.705-1.26-1.568-1.656-2.625zM9.97 12.874L9.624 13c.196.3.406.594.625.875l-.28-1z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png
new file mode 100644 (file)
index 0000000..8cd9282
Binary files /dev/null and b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.png differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language-rtl.svg
new file mode 100644 (file)
index 0000000..9b1ac39
--- /dev/null
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
+    <g id="translation">
+        <path id="english" d="M7.53 9L4 19h2.063l.72-2.406h3.624l.72 2.406h2.062L9.653 9h-2.12zm1.064 1.53L9.938 15H7.25z"/>
+        <path id="chinese" d="M14.594 4.22c-.43.29-.88.616-1.25.874l.187.312c.14.194.276.393.408.594h-3.844v1.47h1.594c.43 1.41 1.11 2.624 2.03 3.624-.662.437-1.413.82-2.25 1.187l.563 1.564c1.11-.48 2.056-1.022 2.908-1.625 1.187.91 2.514 1.63 3.968 2.124l.282.094c.292-.514.577-1.075.812-1.532l-.375-.125c-1.38-.49-2.49-1.052-3.375-1.655.284-.28.59-.634.906-1.156.46-.717.776-1.572 1-2.5h1.657V6H15.75c-.283-.552-.596-1.083-.97-1.53l-.186-.25zm-1.25 3.25h3.187c-.318 1.075-.828 1.937-1.53 2.624-.712-.705-1.26-1.568-1.656-2.625zM9.97 12.874L9.624 13c.196.3.406.594.625.875l-.28-1z"/>
+    </g>
+</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language.png
deleted file mode 100644 (file)
index b4f0875..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/language.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/language.svg
deleted file mode 100644 (file)
index 956aba1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="language">
-        <path id="japanese" d="M17.533 9.81l.27-.59 1.042.407-.18.363c.66.27 1.1.468 1.312.59.33.21.618.513.86.904.21.393.316.846.316 1.358 0 .786-.302 1.48-.905 2.083-.604.634-1.66 1.057-3.17 1.268-.12-.36-.257-.68-.407-.95.97-.15 1.65-.333 2.04-.545.455-.21.786-.48 1-.813.21-.303.313-.663.313-1.087 0-.482-.135-.905-.406-1.27-.33-.33-.8-.588-1.402-.77-.332.635-.648 1.118-.95 1.45-.242.332-.694.906-1.358 1.72.09.394.18.71.272.952l-1.043.362-.09-.498c-.424.36-.802.617-1.134.77-.36.15-.664.226-.905.226-.303 0-.574-.136-.814-.407-.243-.3-.362-.68-.362-1.132 0-.6.137-1.143.408-1.63.24-.45.603-.89 1.086-1.31.273-.24.726-.53 1.36-.86 0-.27.03-.8.09-1.584-.514.03-.92.045-1.222.045-.393 0-.71-.015-.95-.045l-.047-1.04c.726.09 1.495.134 2.31.134 0-.15.076-.74.228-1.767l1.177.184c-.15.542-.256 1.04-.316 1.493.24-.03.542-.077.905-.138.36-.06.573-.09.634-.09s.647-.15 1.765-.453l.045 1.04c-.966.242-2.144.44-3.53.59-.063.662-.093 1.085-.093 1.265.664-.15 1.285-.225 1.858-.225zm-2.672 3.893c-.06-.48-.132-1.252-.223-2.31-.573.424-1.04.86-1.403 1.313-.302.423-.45.875-.45 1.358 0 .24.043.438.135.588.09.092.194.137.315.137.364 0 .908-.365 1.63-1.09zm.775-2.763c0 .483.03 1.088.09 1.81.604-.904 1.057-1.598 1.36-2.08-.575.06-1.06.15-1.45.27z"/>
-        <path id="english" d="M9.497 15.98h1.85L8.265 7.033h-1.85l-3.08 8.95h1.85L5.74 14h3.21l.547 1.98zm-3.49-3.376L7.34 8.822l1.343 3.782H6.008z"/>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.png
deleted file mode 100644 (file)
index 066e17f..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr-invert.svg
deleted file mode 100644 (file)
index 0a4e04e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?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>
-    <g id="create_redirect">
-        <g>
-            <path d="M17.7 2.4c-.3-.3-.7-.4-1.2-.4H4.4v16.2c0 .5.1.8.4 1.1s.7.7 1.2.7h10.2c-.6-.2-1.2-.5-1.9-1-.4-.3-.8-.6-1.2-1l-.5-.6H6.4V16h5.4s-.4-1.5-.4-2h-5v-1h9v1c.4.1 1.1.1 1.5.1.4 0 .7 0 1.1-.1V3.5c.1-.5-.1-.9-.3-1.1zM12.5 4h3v4.5h-3V4zM6.4 4h4v1.6h-4V4zm0 3h4v1.5h-4V7zm0 3h9v1.5h-9V10zm12.7 3.1l4.9 3.8-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
-        </g>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.png
deleted file mode 100644 (file)
index 18ceb35..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-ltr.svg
deleted file mode 100644 (file)
index be25d43..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="create_redirect">
-        <g>
-            <path d="M17.7 2.4c-.3-.3-.7-.4-1.2-.4H4.4v16.2c0 .5.1.8.4 1.1s.7.7 1.2.7h10.2c-.6-.2-1.2-.5-1.9-1-.4-.3-.8-.6-1.2-1l-.5-.6H6.4V16h5.4s-.4-1.5-.4-2h-5v-1h9v1c.4.1 1.1.1 1.5.1.4 0 .7 0 1.1-.1V3.5c.1-.5-.1-.9-.3-1.1zM12.5 4h3v4.5h-3V4zM6.4 4h4v1.6h-4V4zm0 3h4v1.5h-4V7zm0 3h9v1.5h-9V10zm12.7 3.1l4.9 3.8-4.9 4.8v-2.2c-1.7 0-2.9-.2-4.3-1.2-1.2-.8-2.5-2.6-2.3-4.1 1.4 1 2.9 1.5 4.4 1.5.7 0 1.4-.1 2.1-.3l.1-2.3"/>
-        </g>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.png
deleted file mode 100644 (file)
index cdcd158..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl-invert.svg
deleted file mode 100644 (file)
index 431c5b8..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?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>
-    <g id="create_redirect">
-        <g id="g3264">
-            <path d="M6.3 2.4c.3-.3.7-.4 1.2-.4h12.1v16.2c0 .5-.1.8-.4 1.1-.3.3-.7.7-1.2.7H7.8c.6-.2 1.2-.5 1.9-1 .4-.3.8-.6 1.2-1l.5-.6h6.2V16h-5.4s.4-1.5.4-2h5v-1h-9v1c-.4.1-1.1.1-1.5.1-.4 0-.7 0-1.1-.1V3.5c-.1-.5.1-.9.3-1.1zM11.5 4h-3v4.5h3V4zm6.1 0h-4v1.6h4V4zm0 3h-4v1.5h4V7zm0 3h-9v1.5h9V10z" id="path3266"/>
-            <path d="M4.9 13.1L0 16.9l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3" id="path3268"/>
-        </g>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.png
deleted file mode 100644 (file)
index dc9b0e6..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/redirect-rtl.svg
deleted file mode 100644 (file)
index a41d178..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
-    <g id="create_redirect">
-        <g id="g3264">
-            <path d="M6.3 2.4c.3-.3.7-.4 1.2-.4h12.1v16.2c0 .5-.1.8-.4 1.1-.3.3-.7.7-1.2.7H7.8c.6-.2 1.2-.5 1.9-1 .4-.3.8-.6 1.2-1l.5-.6h6.2V16h-5.4s.4-1.5.4-2h5v-1h-9v1c-.4.1-1.1.1-1.5.1-.4 0-.7 0-1.1-.1V3.5c-.1-.5.1-.9.3-1.1zM11.5 4h-3v4.5h3V4zm6.1 0h-4v1.6h4V4zm0 3h-4v1.5h4V7zm0 3h-9v1.5h9V10z" id="path3266"/>
-            <path d="M4.9 13.1L0 16.9l4.9 4.8v-2.2c1.7 0 2.9-.2 4.3-1.2 1.2-.8 2.5-2.6 2.3-4.1-1.4 1-2.9 1.5-4.4 1.5-.7 0-1.4-.1-2.1-.3l-.1-2.3" id="path3268"/>
-        </g>
-    </g>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.png
deleted file mode 100644 (file)
index fde9f52..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr-invert.svg
deleted file mode 100644 (file)
index 30915b7..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?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.1 13.1C9.3 11 8.4 8.8 8.1 8h4.7l.7-2H8V3H6v3H1v2h5c-.2.9-1.3 4.8-5.1 7.6l1.2 1.6c2.7-2 4.3-4.5 5.1-6.4.7 1.3 1.7 3 3.2 4.5l.7-2.2zm1.4 6.9l1.3-4h5.3l1.3 4h2.2L18 6h-3l-4.7 14h2.2zm4-12l2 6h-4l2-6z"/>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.png
deleted file mode 100644 (file)
index 1025461..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-ltr.svg
deleted file mode 100644 (file)
index 8954a21..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?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.1 13.1C9.3 11 8.4 8.8 8.1 8h4.7l.7-2H8V3H6v3H1v2h5c-.2.9-1.3 4.8-5.1 7.6l1.2 1.6c2.7-2 4.3-4.5 5.1-6.4.7 1.3 1.7 3 3.2 4.5l.7-2.2zm1.4 6.9l1.3-4h5.3l1.3 4h2.2L18 6h-3l-4.7 14h2.2zm4-12l2 6h-4l2-6z"/>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.png
deleted file mode 100644 (file)
index 09ab631..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl-invert.svg
deleted file mode 100644 (file)
index de634a8..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?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.4 13.1c1.8-2.1 2.7-4.3 3-5.1h-4.7L10 6h5.5V3h2v3h5v2h-5c.2.9 1.3 4.8 5.1 7.6l-1.2 1.6c-2.7-2-4.3-4.5-5.1-6.4-.7 1.3-1.7 3-3.2 4.5l-.7-2.2zM11 20l-1.3-4H4.4l-1.3 4H.9L5.5 6h3l4.7 14H11zM7 8l-2 6h4L7 8z" id="path704"/>
-</svg>
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.png b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.png
deleted file mode 100644 (file)
index 38066d6..0000000
Binary files a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.png and /dev/null differ
diff --git a/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.svg b/resources/lib/oojs-ui/themes/mediawiki/images/icons/translation-rtl.svg
deleted file mode 100644 (file)
index 44ba971..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-<?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.4 13.1c1.8-2.1 2.7-4.3 3-5.1h-4.7L10 6h5.5V3h2v3h5v2h-5c.2.9 1.3 4.8 5.1 7.6l-1.2 1.6c-2.7-2-4.3-4.5-5.1-6.4-.7 1.3-1.7 3-3.2 4.5l-.7-2.2zM11 20l-1.3-4H4.4l-1.3 4H.9L5.5 6h3l4.7 14H11zM7 8l-2 6h4L7 8z" id="path704"/>
-</svg>
index 6471516..33d9a00 100644 (file)
        height: 6px;
 }
 /* @noflip */ .tipsy-n .tipsy-arrow {
-       top: 0px;
+       top: 0;
        left: 50%;
        margin-left: -5px;
 }
 /* @noflip */ .tipsy-nw .tipsy-arrow {
-       top: 1px;
+       top: 0;
        left: 10px;
 }
 /* @noflip */ .tipsy-ne .tipsy-arrow {
-       top: 1px;
+       top: 0;
        right: 10px;
 }
 /* @noflip */ .tipsy-s .tipsy-arrow {
-       bottom: 0px;
+       bottom: 0;
        left: 50%;
        margin-left: -5px;
        background-position: bottom left;
 }
 /* @noflip */ .tipsy-sw .tipsy-arrow {
-       bottom: 0px;
+       bottom: 0;
        left: 10px;
        background-position: bottom left;
 }
 /* @noflip */ .tipsy-se .tipsy-arrow {
-       bottom: 0px;
+       bottom: 0;
        right: 10px;
        background-position: bottom left;
 }
 /* @noflip */ .tipsy-e .tipsy-arrow {
        top: 50%;
        margin-top: -5px;
-       right: 1px;
-       width: 5px;
+       right: 0;
+       width: 6px;
        height: 11px;
        background-position: top right;
 }
 /* @noflip */ .tipsy-w .tipsy-arrow {
        top: 50%;
        margin-top: -5px;
-       left: 0px;
+       left: 0;
        width: 6px;
        height: 11px;
 }
index 29b7490..2c6a588 100644 (file)
             }
         },
 
-
         fixTitle: function() {
             var $e = this.$element;
             if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
index afa7201..519e39b 100644 (file)
@@ -5,8 +5,8 @@
  */
 ( function ( $, mw ) {
 
-// Cached access key prefix for used browser
-var cachedAccessKeyPrefix,
+// Cached access key modifiers for used browser
+var cachedAccessKeyModifiers,
 
        // Whether to use 'test-' instead of correct prefix (used for testing)
        useTestPrefix = false,
@@ -16,37 +16,39 @@ var cachedAccessKeyPrefix,
        labelable = 'button, input, textarea, keygen, meter, output, progress, select';
 
 /**
- * Get the prefix for the access key for browsers that don't support accessKeyLabel.
+ * Find the modifier keys that need to be pressed together with the accesskey to trigger the input.
  *
+ * The result is dependant on the ua paramater or the current platform.
  * For browsers that support accessKeyLabel, #getAccessKeyLabel never calls here.
+ * Valid key values that are returned can be: ctrl, alt, option, shift, esc
  *
  * @private
  * @param {Object} [ua] An object with a 'userAgent' and 'platform' property.
- * @return {string} Access key prefix
+ * @return {Array} Array with 0 or more of the string values: ctrl, option, alt, shift, esc
  */
-function getAccessKeyPrefix( ua ) {
+function getAccessKeyModifiers( ua ) {
        // use cached prefix if possible
-       if ( !ua && cachedAccessKeyPrefix ) {
-               return cachedAccessKeyPrefix;
+       if ( !ua && cachedAccessKeyModifiers ) {
+               return cachedAccessKeyModifiers;
        }
 
        var profile = $.client.profile( ua ),
-               accessKeyPrefix = 'alt-';
+               accessKeyModifiers = [ 'alt' ];
 
        // Classic Opera on any platform
        if ( profile.name === 'opera' && profile.versionNumber < 15 ) {
-               accessKeyPrefix = 'shift-esc-';
+               accessKeyModifiers = [ 'shift', 'esc' ];
 
        // Chrome and modern Opera on any platform
        } else if ( profile.name === 'chrome' || profile.name === 'opera' ) {
-               accessKeyPrefix = (
+               accessKeyModifiers = (
                        profile.platform === 'mac'
                                // Chrome on Mac
-                               ? 'ctrl-option-'
+                               ? [ 'ctrl', 'option' ]
                                // Chrome on Windows or Linux
                                // (both alt- and alt-shift work, but alt with E, D, F etc does not
                                // work since they are browser shortcuts)
-                               : 'alt-shift-'
+                               : [ 'alt', 'shift' ]
                );
 
        // Non-Windows Safari with webkit_version > 526
@@ -54,7 +56,7 @@ function getAccessKeyPrefix( ua ) {
                && profile.name === 'safari'
                && profile.layoutVersion > 526
        ) {
-               accessKeyPrefix = 'ctrl-alt-';
+               accessKeyModifiers = [ 'ctrl', 'alt' ];
 
        // Safari/Konqueror on any platform, or any browser on Mac
        // (but not Safari on Windows)
@@ -63,27 +65,27 @@ function getAccessKeyPrefix( ua ) {
                || profile.platform === 'mac'
                || profile.name === 'konqueror' )
        ) {
-               accessKeyPrefix = 'ctrl-';
+               accessKeyModifiers = [ 'ctrl' ];
 
        // Firefox/Iceweasel 2.x and later
        } else if ( ( profile.name === 'firefox' || profile.name === 'iceweasel' )
                && profile.versionBase > '1'
        ) {
-               accessKeyPrefix = 'alt-shift-';
+               accessKeyModifiers = [ 'alt', 'shift' ];
        }
 
-       // cache prefix
+       // cache modifiers
        if ( !ua ) {
-               cachedAccessKeyPrefix = accessKeyPrefix;
+               cachedAccessKeyModifiers = accessKeyModifiers;
        }
-       return accessKeyPrefix;
+       return accessKeyModifiers;
 }
 
 /**
  * Get the access key label for an element.
  *
  * Will use native accessKeyLabel if available (currently only in Firefox 8+),
- * falls back to #getAccessKeyPrefix.
+ * falls back to #getAccessKeyModifiers.
  *
  * @private
  * @param {HTMLElement} element Element to get the label for
@@ -99,7 +101,7 @@ function getAccessKeyLabel( element ) {
        if ( !useTestPrefix && element.accessKeyLabel ) {
                return element.accessKeyLabel;
        }
-       return ( useTestPrefix ? 'test-' : getAccessKeyPrefix() ) + element.accessKey;
+       return ( useTestPrefix ? 'test' : getAccessKeyModifiers().join( '-' ) ) + '-' + element.accessKey;
 }
 
 /**
@@ -175,12 +177,30 @@ $.fn.updateTooltipAccessKeys = function () {
 };
 
 /**
- * Exposed for testing.
+ * getAccessKeyModifiers
+ *
+ * @method updateTooltipAccessKeys_getAccessKeyModifiers
+ * @inheritdoc #getAccessKeyModifiers
+ */
+$.fn.updateTooltipAccessKeys.getAccessKeyModifiers = getAccessKeyModifiers;
+
+/**
+ * getAccessKeyLabel
+ *
+ * @method updateTooltipAccessKeys_getAccessKeyLabel
+ * @inheritdoc #getAccessKeyLabel
+ */
+$.fn.updateTooltipAccessKeys.getAccessKeyLabel = getAccessKeyLabel;
+
+/**
+ * getAccessKeyPrefix
  *
  * @method updateTooltipAccessKeys_getAccessKeyPrefix
- * @inheritdoc #getAccessKeyPrefix
+ * @deprecated 1.27 Use #getAccessKeyModifiers
  */
-$.fn.updateTooltipAccessKeys.getAccessKeyPrefix = getAccessKeyPrefix;
+$.fn.updateTooltipAccessKeys.getAccessKeyPrefix = function ( ua ) {
+       return getAccessKeyModifiers( ua ).join( '-' ) + '-';
+};
 
 /**
  * Switch test mode on and off.
index 1c6428f..baba348 100644 (file)
@@ -51,4 +51,3 @@
        /* @embed */
        background: url(images/marker.png) no-repeat;
 }
-
index 6de537a..1d4d0e9 100644 (file)
@@ -41,6 +41,7 @@
 
                copySelectors = [
                        // Main
+                       '.mw-indicators',
                        '#firstHeading',
                        '#wikiPreview',
                        '#wikiDiff',
                        $wikiDiff.hide();
 
                        $.extend( postData, {
-                               prop: 'text|displaytitle|modules|jsconfigvars|categorieshtml|templates|langlinks|limitreporthtml',
+                               prop: 'text|indicators|displaytitle|modules|jsconfigvars|categorieshtml|templates|langlinks|limitreporthtml',
                                text: $textbox.textSelection( 'getContents' ),
                                pst: true,
                                preview: true,
                                                response.parse.modulestyles
                                        ) );
                                }
+
+                               newList = [];
+                               $.each( response.parse.indicators, function ( i, indicator ) {
+                                       newList.push(
+                                               $( '<div>' )
+                                                       .addClass( 'mw-indicator' )
+                                                       .attr( 'id', mw.util.escapeId( 'mw-indicator-' + indicator.name ) )
+                                                       .html( indicator[ '*' ] )
+                                                       .get( 0 ),
+                                               // Add a whitespace between the <div>s because
+                                               // they get displayed with display: inline-block
+                                               document.createTextNode( '\n' )
+                                       );
+                               } );
+                               $( '.mw-indicators' ).empty().append( newList );
+
                                if ( response.parse.displaytitle ) {
                                        $displaytitle = $( $.parseHTML( response.parse.displaytitle ) );
                                        $( '#firstHeading' ).msg(
                                        );
                                }
                                if ( response.parse.categorieshtml ) {
-                                       $( '#catlinks' ).replaceWith( response.parse.categorieshtml[ '*' ] );
+                                       $content = $( $.parseHTML( response.parse.categorieshtml[ '*' ] ) );
+                                       mw.hook( 'wikipage.categories' ).fire( $content );
+                                       $( '.catlinks[data-mw="interface"]' ).replaceWith( $content );
                                }
                                if ( response.parse.templates ) {
                                        newList = [];
index 453d928..4803a0a 100644 (file)
@@ -117,22 +117,16 @@ pre, .mw-code {
        border: 1px solid #aaaaaa;
        background-color: #f9f9f9;
        padding: 5px;
-       display: inline-block;
        display: table;
-       /* IE7 and earlier */
-       zoom: 1;
-       *display: inline;
 }
 
 /* Separate columns for tocnumber and toctext */
-/* Ignored by IE7 and lower */
 .tocnumber,
 .toctext {
        display: table-cell;
 }
 
 /* Space between the columns for tocnumber and toctext */
-/* Ignored by IE7 and lower */
 .tocnumber:after {
        content: "";
        padding-right: 0.5em;
@@ -326,7 +320,6 @@ a.sortheader {
        list-style: none;
        list-style-type: none;
        list-style-image: none;
-       vertical-align: middle !ie;
 }
 
 .catlinks li {
@@ -335,8 +328,6 @@ a.sortheader {
        padding: 0 .4em;
        border-left: 1px solid #AAA;
        margin: 0.1em 0;
-       zoom: 1;
-       display: inline !ie;
 }
 
 .catlinks li:first-child {
index 06c18e6..f6c407a 100644 (file)
@@ -19,7 +19,6 @@
        background-size: @width @height;
 }
 
-
 .vertical-gradient(@startColor: gray, @endColor: white, @startPos: 0, @endPos: 100%) {
        background-color: @endColor;
        background-image: -moz-linear-gradient( top, @startColor @startPos, @endColor @endPos ); // Firefox 3.6+
 }
 
 .column-width(@value) {
-       -webkit-column-width: @value; // Chrome Any, Safari 3+, Opera 11.1+
+       -webkit-column-width: @value; // Chrome Any, Safari 3+, Opera 15+
        -moz-column-width: @value; // Firefox 1.5+
-       column-width: @value; // IE 10+
+       column-width: @value; // IE 10+, Opera 11.1-12.1
 }
 
 .column-break-inside-avoid() {
-       -webkit-column-break-inside: avoid; // Chrome Any, Safari 3+, Opera 11.1+
+       -webkit-column-break-inside: avoid; // Chrome Any, Safari 3+, Opera 15+
        page-break-inside: avoid; // Firefox 1.5+
-       break-inside: avoid-column; // IE 10+
+       break-inside: avoid-column; // IE 10+, Opera 11.1-12.1
 }
 
 .flex-display(@display: flex) {
index e28b333..85b6bd4 100644 (file)
        .rotate-frames;
 }
 
-@-o-keyframes rotate {
-       .rotate-frames;
-}
-
 @keyframes rotate {
        .rotate-frames;
 }
index 4b6bb48..507109a 100644 (file)
 @colorGray7: #777;
 @colorGray8: #888;
 @colorGray9: #999;
-@colorGray10: #AAA;
-@colorGray11: #BBB;
-@colorGray12: #CCC;
-@colorGray13: #DDD;
-@colorGray14: #EEE;
-@colorGray15: #F9F9F9; // lightest
+@colorGray10: #aaa;
+@colorGray11: #bbb;
+@colorGray12: #ccc;
+@colorGray13: #ddd;
+@colorGray14: #eee;
+@colorGray15: #f9f9f9; // lightest
 
 // Semantic background colors
 // Blue; for contextual use of a continuing action
 @colorProgressive: #347bff;
-@colorProgressiveHighlight: #2962CC;
-@colorProgressiveActive: #2962CC;
+@colorProgressiveHighlight: #2962cc;
+@colorProgressiveActive: #2962cc;
 // Green; for contextual use of a positive finalizing action
 @colorConstructive: #00af89;
-@colorConstructiveHighlight: #008C6D;
-@colorConstructiveActive: #008C6D;
+@colorConstructiveHighlight: #008c6d;
+@colorConstructiveActive: #008c6d;
 // Orange; for contextual use of returning to a past action
-@colorRegressive: #FF5D00;
+@colorRegressive: #ff5d00;
 // Red; for contextual use of a negative action of high severity
 @colorDestructive: #d11d13;
-@colorDestructiveHighlight: #A7170F;
-@colorDestructiveActive: #A7170F;
+@colorDestructiveHighlight: #a7170f;
+@colorDestructiveActive: #a7170f;
 // Orange; for contextual use of a potentially negative action of medium severity
-@colorMediumSevere: #FF5D00;
+@colorMediumSevere: #ff5d00;
 // Yellow; for contextual use of a potentially negative action of low severity
-@colorLowSevere: #FFB50D;
+@colorLowSevere: #ffb50d;
 
 // Used in mixins to darken contextual colors by the same amount (eg. focus)
 @colorDarkenPercentage: 13.5%;
@@ -50,7 +50,7 @@
 @colorButtonTextHighlight: @colorGray7;
 @colorButtonTextActive: @colorGray7;
 @colorDisabledText: @colorGray12;
-@colorErrorText: #CC0000;
+@colorErrorText: #c00;
 
 // UI colors
 @colorFieldBorder: @colorGray12;
 // Global border radius to be used to buttons and inputs
 @borderRadius: 2px;
 
-
 // Icon related variables
 @iconSize: 1.5em;
 @iconGutterWidth: 1em;
+
+// Form input sizes
+@checkboxSize: 2em;
+@radioSize: 2em;
index 113fb00..c51a07a 100644 (file)
@@ -17,7 +17,9 @@
         * Post a message (with subject and body) to a talk page.
         *
         * @abstract
-        * @param {string} subject Subject/topic title; plaintext only (no wikitext or HTML)
+        * @param {string} subject Subject/topic title.  The amount of wikitext supported is
+        *   implementation-specific. It is recommended to only use basic wikilink syntax for
+        *   maximum compatibility.
         * @param {string} body Body, as wikitext.  Signature code will automatically be added
         *   by MessagePosters that require one, unless the message already contains the string
         *   ~~~.
index 69655a6..68fb2aa 100644 (file)
@@ -1,52 +1,54 @@
-/*global OO*/
+/*global OO */
 ( function ( mw, $ ) {
        /**
-        * This is a factory for MessagePoster objects, which allows a pluggable to way to script leaving a
-        * talk page message.
+        * Factory for MessagePoster objects. This provides a pluggable to way to script the action
+        * of adding a message to someone's talk page.
         *
         * @class mw.messagePoster.factory
         * @singleton
         */
-       function MwMessagePosterFactory() {
+       function MessagePosterFactory() {
                this.contentModelToClass = {};
        }
 
-       OO.initClass( MwMessagePosterFactory );
+       OO.initClass( MessagePosterFactory );
 
        // Note: This registration scheme is currently not compatible with LQT, since that doesn't
-       // have its own content model, just islqttalkpage.  LQT pages will be passed to the wikitext
+       // have its own content model, just islqttalkpage. LQT pages will be passed to the wikitext
        // MessagePoster.
        /**
-        * Registers a MessagePoster subclass for a given content model.
+        * Register a MessagePoster subclass for a given content model.
         *
         * @param {string} contentModel Content model of pages this MessagePoster can post to
-        * @param {Function} messagePosterConstructor Constructor for MessagePoster
+        * @param {Function} constructor Constructor of a MessagePoster subclass
         */
-       MwMessagePosterFactory.prototype.register = function ( contentModel, messagePosterConstructor ) {
+       MessagePosterFactory.prototype.register = function ( contentModel, constructor ) {
                if ( this.contentModelToClass[ contentModel ] !== undefined ) {
-                       throw new Error( 'The content model \'' + contentModel + '\' is already registered.' );
+                       throw new Error( 'Content model "' + contentModel + '" is already registered' );
                }
 
-               this.contentModelToClass[ contentModel ] = messagePosterConstructor;
+               this.contentModelToClass[ contentModel ] = constructor;
        };
 
        /**
-        * Unregisters a given content model
-        * This is exposed for testing and should not normally be needed.
+        * Unregister a given content model.
+        * This is exposed for testing and should not normally be used.
         *
         * @param {string} contentModel Content model to unregister
         */
-       MwMessagePosterFactory.prototype.unregister = function ( contentModel ) {
+       MessagePosterFactory.prototype.unregister = function ( contentModel ) {
                delete this.contentModelToClass[ contentModel ];
        };
 
        /**
-        * Creates a MessagePoster, given a title.  A promise for this is returned.
-        * This works by determining the content model, then loading the corresponding
-        * module (which will register the MessagePoster class), and finally constructing it.
+        * Create a MessagePoster for given a title.
         *
-        * This does not require the message and should be called as soon as possible, so it does the
-        * API and ResourceLoader requests in the background.
+        * A promise for this is returned. It works by determining the content model, then loading
+        * the corresponding module (which registers the MessagePoster class), and finally constructing
+        * an object for the given title.
+        *
+        * This does not require the message and should be called as soon as possible, so that the
+        * API and ResourceLoader requests run in the background.
         *
         * @param {mw.Title} title Title that will be posted to
         * @param {string} [apiUrl] api.php URL if the title is on another wiki
         *   - error Error explanation
         *   - details Further error details
         */
-       MwMessagePosterFactory.prototype.create = function ( title, apiUrl ) {
-               var pageId, page, contentModel, moduleName, api,
-                       factory = this;
-
-               if ( apiUrl ) {
-                       api = new mw.ForeignApi( apiUrl );
-               } else {
-                       api = new mw.Api();
-               }
+       MessagePosterFactory.prototype.create = function ( title, apiUrl ) {
+               var factory = this,
+                       api = apiUrl ? new mw.ForeignApi( apiUrl ) : new mw.Api();
 
                return api.get( {
                        action: 'query',
                        prop: 'info',
                        indexpageids: true,
                        titles: title.getPrefixedDb()
-               } ).then( function ( result ) {
-                       if ( result.query.pageids && result.query.pageids.length > 0 ) {
-                               pageId = result.query.pageids[ 0 ];
-                               page = result.query.pages[ pageId ];
-
-                               contentModel = page.contentmodel;
-                               moduleName = 'mediawiki.messagePoster.' + contentModel;
-                               return mw.loader.using( moduleName ).then( function () {
-                                       return factory.createForContentModel(
-                                               contentModel,
-                                               title,
-                                               api
-                                       );
-                               }, function () {
-                                       return $.Deferred().reject( 'failed-to-load-module', 'Failed to load the \'' + moduleName + '\' module' );
-                               } );
-                       } else {
+               } ).then( function ( data ) {
+                       var pageId, page, contentModel, moduleName;
+                       if ( !data.query.pageids[ 0 ] ) {
                                return $.Deferred().reject( 'unexpected-response', 'Unexpected API response' );
                        }
-               }, function ( errorCode, details ) {
-                       return $.Deferred().reject( 'content-model-query-failed', errorCode, details );
-               } ).promise();
+                       pageId = data.query.pageids[ 0 ];
+                       page = data.query.pages[ pageId ];
+
+                       contentModel = page.contentmodel;
+                       moduleName = 'mediawiki.messagePoster.' + contentModel;
+                       return mw.loader.using( moduleName ).then( function () {
+                               return factory.createForContentModel(
+                                       contentModel,
+                                       title,
+                                       api
+                               );
+                       }, function () {
+                               return $.Deferred().reject( 'failed-to-load-module', 'Failed to load "' + moduleName + '"' );
+                       } );
+               }, function ( error, details ) {
+                       return $.Deferred().reject( 'content-model-query-failed', error, details );
+               } );
        };
 
        /**
         * Creates a MessagePoster instance, given a title and content model
         *
         * @private
-        *
         * @param {string} contentModel Content model of title
         * @param {mw.Title} title Title being posted to
         * @param {mw.Api} api mw.Api instance that the instance should use
         * @return {mw.messagePoster.MessagePoster}
         *
         */
-       MwMessagePosterFactory.prototype.createForContentModel = function ( contentModel, title, api ) {
+       MessagePosterFactory.prototype.createForContentModel = function ( contentModel, title, api ) {
                return new this.contentModelToClass[ contentModel ]( title, api );
        };
 
        mw.messagePoster = {
-               factory: new MwMessagePosterFactory()
+               factory: new MessagePosterFactory()
        };
 }( mediaWiki, jQuery ) );
index a6515d2..66b3fb2 100644 (file)
@@ -100,6 +100,11 @@ figure[typeof*='mw:Image'] {
                /* taken from .thumbcaption, plus .thumbinner */
                padding: 1px 5px 5px;
                background-color: #f9f9f9;
+
+               table {
+                       /* reset caption side for tables inside figcaptions */
+                       caption-side: top;
+               }
        }
 }
 
index d706d26..7872085 100644 (file)
@@ -25,6 +25,14 @@ a:hover, a:focus {
        text-decoration: underline;
 }
 
+a:lang(ar),
+a:lang(kk-arab),
+a:lang(mzn),
+a:lang(ps),
+a:lang(ur) {
+       text-decoration: none;
+}
+
 a.stub {
        color: #772233;
 }
diff --git a/resources/src/mediawiki.special/mediawiki.special.blocklist.css b/resources/src/mediawiki.special/mediawiki.special.blocklist.css
new file mode 100644 (file)
index 0000000..b7513b6
--- /dev/null
@@ -0,0 +1,4 @@
+.mw-htmlform-flatlist-item {\r
+       /* FIXME: There should be an option in OOUI to do that */\r
+       display: inline-block !important;\r
+}
\ No newline at end of file
diff --git a/resources/src/mediawiki.special/mediawiki.special.comparepages.styles.less b/resources/src/mediawiki.special/mediawiki.special.comparepages.styles.less
new file mode 100644 (file)
index 0000000..45d0485
--- /dev/null
@@ -0,0 +1,19 @@
+@import "mediawiki.mixins";
+
+.mw-special-ComparePages .mw-htmlform-ooui-wrapper {
+       width: 100%;
+}
+
+.mw-special-ComparePages .oo-ui-layout.oo-ui-panelLayout.oo-ui-panelLayout-padded.oo-ui-panelLayout-framed {
+       float: left;
+       width: 49%;
+       .box-sizing( border-box );
+}
+
+.mw-special-ComparePages .oo-ui-layout.oo-ui-panelLayout.oo-ui-panelLayout-padded.oo-ui-panelLayout-framed:nth-of-type(2) {
+       margin-left: 2%;
+}
+
+.mw-special-ComparePages .mw-htmlform-submit-buttons {
+       clear: both;
+}
index 5622b32..3357461 100644 (file)
@@ -2,7 +2,8 @@
  * JavaScript for Special:Import
  */
 ( function ( $ ) {
-       function updateImportSubprojectList( firstTime ) {
+       var subprojectListAlreadyShown;
+       function updateImportSubprojectList() {
                var $projectField = $( '#mw-import-table-interwiki #interwiki' ),
                        $subprojectField = $projectField.parent().find( '#subproject' ),
                        $selected = $projectField.find( ':selected' ),
                                option = document.createElement( 'option' );
                                option.appendChild( document.createTextNode( el ) );
                                option.setAttribute( 'value', el );
-                               if ( oldValue === el && firstTime !== true ) {
+                               if ( oldValue === el && subprojectListAlreadyShown === true ) {
                                        option.setAttribute( 'selected', 'selected' );
                                }
                                return option;
                        } );
                        $subprojectField.show().empty().append( options );
+                       subprojectListAlreadyShown = true;
                } else {
                        $subprojectField.hide();
                }
@@ -29,7 +31,7 @@
                var $projectField = $( '#mw-import-table-interwiki #interwiki' );
                if ( $projectField.length ) {
                        $projectField.change( updateImportSubprojectList );
-                       updateImportSubprojectList( true );
+                       updateImportSubprojectList();
                }
        } );
 }( jQuery ) );
diff --git a/resources/src/mediawiki.special/mediawiki.special.javaScriptTest.js b/resources/src/mediawiki.special/mediawiki.special.javaScriptTest.js
deleted file mode 100644 (file)
index fb74e4e..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-/*!
- * JavaScript for Special:JavaScriptTest
- */
-( function ( mw, $ ) {
-       $( function () {
-
-               // Create useskin dropdown menu and reload onchange to the selected skin
-               // (only if a framework was found, not on error pages).
-               $( '#mw-javascripttest-summary' ).append( function () {
-
-                       var $html = $( '<p><label for="useskin">'
-                                       + mw.message( 'javascripttest-pagetext-skins' ).escaped()
-                                       + ' '
-                                       + '</label></p>' ),
-                               select = '<select name="useskin" id="useskin">';
-
-                       // Build <select> further
-                       $.each( mw.config.get( 'wgAvailableSkins' ), function ( id ) {
-                               select += '<option value="' + id + '"'
-                                       + ( mw.config.get( 'skin' ) === id ? ' selected="selected"' : '' )
-                                       + '>' + mw.message( 'skinname-' + id ).escaped() + '</option>';
-                       } );
-                       select += '</select>';
-
-                       // Bind onchange event handler and append to form
-                       $html.append(
-                               $( select ).change( function () {
-                                       var url = new mw.Uri();
-                                       location.href = url.extend( { useskin: $( this ).val() } );
-                               } )
-                       );
-
-                       return $html;
-               } );
-       } );
-
-}( mediaWiki, jQuery ) );
index f90f859..29322f4 100644 (file)
                                                notif = null;
                                        }
                                } );
-
-                               // Remove now-unnecessary success=1 querystring to prevent reappearance of notification on reload
-                               if ( history.replaceState ) {
-                                       history.replaceState( {}, document.title, location.href.replace( /&?success=1/, '' ) );
-                               }
                        }
                }
 
                        var minuteDiff, localTime,
                                type = $tzSelect.val();
 
-                       if ( type === 'guess' ) {
-                               // Get browser timezone & fill it in
-                               minuteDiff = -( new Date().getTimezoneOffset() );
-                               $tzTextbox.val( minutesToHours( minuteDiff ) );
-                               $tzSelect.val( 'other' );
-                               $tzTextbox.prop( 'disabled', false );
-                       } else if ( type === 'other' ) {
+                       if ( type === 'other' ) {
+                               // User specified time zone manually in <input>
                                // Grab data from the textbox, parse it.
                                minuteDiff = hoursToMinutes( $tzTextbox.val() );
                        } else {
-                               // Grab data from the $tzSelect value
-                               minuteDiff = parseInt( type.split( '|' )[ 1 ], 10 ) || 0;
-                               $tzTextbox.val( minutesToHours( minuteDiff ) );
+                               // Time zone not manually specified by user
+                               if ( type === 'guess' ) {
+                                       // Get browser timezone & fill it in
+                                       minuteDiff = -( new Date().getTimezoneOffset() );
+                                       $tzTextbox.val( minutesToHours( minuteDiff ) );
+                                       $tzSelect.val( 'other' );
+                                       $tzTextbox.prop( 'disabled', false );
+                               } else {
+                                       // Grab data from the $tzSelect value
+                                       minuteDiff = parseInt( type.split( '|' )[ 1 ], 10 ) || 0;
+                                       $tzTextbox.val( minutesToHours( minuteDiff ) );
+                               }
+
+                               // Set defaultValue prop on the generated box so we don't trigger the
+                               // unsaved preferences check
+                               $tzTextbox.prop( 'defaultValue', $tzTextbox.val() );
                        }
 
                        // Determine local time from server time and minutes difference, for display.
                        } );
                }
 
+               // Check if all of the form values are unchanged
+               function isPrefsChanged() {
+                       var inputs = $( '#mw-prefs-form :input' ),
+                               input, $input, inputType,
+                               index, optIndex,
+                               opt;
+
+                       for ( index = 0; index < inputs.length; index++ ) {
+                               input = inputs[ index ];
+                               $input = $( input );
+
+                               // Different types of inputs have different methods for accessing defaults
+                               if ( $input.is( 'select' ) ) { // <select> has the property defaultSelected for each option
+                                       for ( optIndex = 0; optIndex < input.options.length; optIndex++ ) {
+                                               opt = input.options[ optIndex ];
+                                               if ( opt.selected !== opt.defaultSelected ) {
+                                                       return true;
+                                               }
+                                       }
+                               } else if ( $input.is( 'input' ) ) { // <input> has defaultValue or defaultChecked
+                                       inputType = input.type;
+                                       if ( inputType === 'radio' || inputType === 'checkbox' ) {
+                                               if ( input.checked !== input.defaultChecked ) {
+                                                       return true;
+                                               }
+                                       } else if ( input.value !== input.defaultValue ) {
+                                               return true;
+                                       }
+                               }
+                       }
+
+                       return false;
+               }
+
+               // Disable the button to save preferences unless preferences have changed
+               // Check if preferences have been changed before JS has finished loading
+               if ( !isPrefsChanged() ) {
+                       $( '#prefcontrol' ).prop( 'disabled', true );
+                       $( '#preferences > fieldset' ).one( 'change keydown mousedown', function () {
+                               $( '#prefcontrol' ).prop( 'disabled', false );
+                       } );
+               }
+
                // Set up a message to notify users if they try to leave the page without
                // saving.
-               $( '#mw-prefs-form' ).data( 'origdata', $( '#mw-prefs-form' ).serialize() );
                allowCloseWindow = mw.confirmCloseWindow( {
-                       test: function () {
-                               return $( '#mw-prefs-form' ).serialize() !== $( '#mw-prefs-form' ).data( 'origdata' );
-                       },
-
+                       test: isPrefsChanged,
                        message: mw.msg( 'prefswarning-warning', mw.msg( 'saveprefs' ) ),
                        namespace: 'prefswarning'
                } );
index f0fb7b9..5bb69b8 100644 (file)
@@ -11,7 +11,8 @@
                color: lighten( @mainColor, @colorLightenPercentage );
        }
        // Focus and active states
-       &:focus, &:active {
+       &:focus,
+       &:active {
                color: darken( @mainColor, @colorDarkenPercentage );
                outline: none; // outline fix
        }
@@ -74,7 +75,8 @@ Styleguide 6.2.1.
        &:hover {
                color: @mainColor;
        }
-       &:focus, &:active {
+       &:focus,
+       &:active {
                color: darken( @mainColor, @colorDarkenPercentage );
        }
 }
index 600b771..4ffaeee 100644 (file)
        display: inline-block;
        padding: .5em 1em;
        margin: 0;
-       .box-sizing(border-box);
+       .box-sizing( border-box );
 
        // Disable weird iOS styling
        -webkit-appearance: none;
 
-       // IE6/IE7 hack
-       // http://stackoverflow.com/a/5838575/365238
+       // IE 6 & 7 hack
+       // https://stackoverflow.com/a/5838575/365238
        *display: inline;
        zoom: 1;
 
        // Container styling
-       .button-colors(#FFF, #CCC, #777);
+       .button-colors( #fff, #ccc, #777 );
        border-radius: @borderRadius;
        min-width: 4em;
 
        // Styleguide 2.1.1.
        &.mw-ui-progressive,
        &.mw-ui-primary {
-               .button-colors(@colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive);
+               .button-colors( @colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive );
 
                &.mw-ui-quiet {
-                       .button-colors-quiet(@colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive);
+                       .button-colors-quiet( @colorProgressive, @colorProgressiveHighlight, @colorProgressiveActive );
                }
        }
 
        //
        // Styleguide 2.1.2.
        &.mw-ui-constructive {
-               .button-colors(@colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive);
+               .button-colors( @colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive );
 
                &.mw-ui-quiet {
-                       .button-colors-quiet(@colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive);
+                       .button-colors-quiet( @colorConstructive, @colorConstructiveHighlight, @colorConstructiveActive );
                }
        }
 
        //
        // Styleguide 2.1.3.
        &.mw-ui-destructive {
-               .button-colors(@colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive);
+               .button-colors( @colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive );
 
                &.mw-ui-quiet {
-                       .button-colors-quiet(@colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive);
+                       .button-colors-quiet( @colorDestructive, @colorDestructiveHighlight, @colorDestructiveActive );
                }
        }
 
        // Styleguide 2.1.4.
        &.mw-ui-quiet {
                background: transparent;
-               border: none;
+               border: 0;
                text-shadow: none;
-               .button-colors-quiet(@colorButtonText, @colorButtonTextHighlight, @colorButtonTextActive);
+               .button-colors-quiet( @colorButtonText, @colorButtonTextHighlight, @colorButtonTextActive );
 
                &:hover,
                &:focus {
@@ -269,8 +269,8 @@ a.mw-ui-button {
                border-bottom-left-radius: @borderRadius;
        }
 
-       &:not(:first-child) {
-               border-left: none;
+       &:not( :first-child ) {
+               border-left: 0;
        }
 
        &:last-child{
index bd5dd4a..d44e5d7 100644 (file)
@@ -5,8 +5,8 @@
 //
 // Styling checkboxes in a way that works cross browser is a tricky problem to solve.
 // In MediaWiki UI put a checkbox and label inside a mw-ui-checkbox div.
-// This renders in all browsers except IE6-8 which do not support the :checked selector;
-// these are kept backwards-compatible using the :not(#noop) selector.
+// This renders in all browsers except IE 6-8 which do not support the :checked selector;
+// these are kept backwards-compatible using the `:not( #noop )` selector.
 // You should give the checkbox and label matching "id" and "for" attributes, respectively.
 //
 // Markup:
        vertical-align: middle;
 }
 
-@checkboxSize: 2em;
-
 // We use the not selector to cancel out styling on IE 8 and below
-// We also disable this styling on javascript disabled devices. This fixes the issue with
+// We also disable this styling on JavaScript disabled devices. This fixes the issue with
 // Opera Mini where checking/unchecking doesn't apply styling but potentially leaves other
 // more capable browsers with unstyled checkboxes.
-.client-js .mw-ui-checkbox:not(#noop) {
+.client-js .mw-ui-checkbox:not( #noop ) {
        // Position relatively so we can make use of absolute pseudo elements
        position: relative;
        display: table;
@@ -62,8 +60,7 @@
                height: @checkboxSize;
                // This is needed for Firefox mobile (See bug 71750 to workaround default Firefox stylesheet)
                max-width: none;
-               margin: 0;
-               margin-right: 0.4em;
+               margin: 0 0.4em 0 0;
                display: table-cell;
 
                & + label {
                // the pseudo before element of the label after the checkbox now looks like a checkbox
                & + label::before {
                        content: '';
-                       cursor: pointer;
-                       .box-sizing(border-box);
+                       background-color: #fff;
+                       .background-image-svg( 'images/checked.svg', 'images/checked.png' );
+                       background-position: center center;
+                       background-origin: border-box;
+                       background-repeat: no-repeat;
+                       .background-size( @checkboxSize - 0.2em, @checkboxSize - 0.2em );
+                       background-size: 0 0;
+                       .box-sizing( border-box );
                        position: absolute;
+                       // align the checkbox to middle of the text
+                       top: 50%;
                        left: 0;
-                       border-radius: @borderRadius;
                        width: @checkboxSize;
                        height: @checkboxSize;
-                       line-height: @checkboxSize;
-                       background-color: #fff;
-                       border: 1px solid @colorGray7;
-                       // align the checkbox to middle of the text
-                       top: 50%;
                        margin-top: -1em;
-                       .background-image-svg('images/checked.svg', 'images/checked.png');
-                       .background-size( @checkboxSize - 0.2em, @checkboxSize - 0.2em );
-                       background-repeat: no-repeat;
-                       background-position: center center;
-                       background-origin: border-box;
-                       background-size: 0 0;
+                       border: 1px solid @colorGray7;
+                       border-radius: @borderRadius;
+                       line-height: @checkboxSize;
+                       cursor: pointer;
                }
 
                // when the input is checked, style the label pseudo before element that followed as a checked checkbox
 
                // disabled and checked checkboxes have a white circle
                &:disabled:checked + label::before {
-                       .background-image-svg('images/checked_disabled.svg', 'images/checked_disabled.png');
+                       .background-image-svg( 'images/checked_disabled.svg', 'images/checked_disabled.png' );
                }
        }
 }
index dc49e20..cc96a5c 100644 (file)
@@ -36,7 +36,7 @@
 //
 // Styleguide 5.1.
 .mw-ui-vform {
-       .box-sizing(border-box);
+       .box-sizing( border-box );
 
        width: @defaultFormWidth;
 
@@ -44,7 +44,7 @@
        select,
        .mw-ui-button {
                display: block;
-               .box-sizing(border-box);
+               .box-sizing( border-box );
                margin: 0;
                width: 100%;
        }
        // Give dropdown lists the same spacing as input fields for consistency.
        // Values taken from .agora-field-styling() in mixins/form.less
        select {
-               padding: 0.35em 0.5em 0.35em 0.5em;
+               padding: 0.35em 0.5em;
                vertical-align: middle;
        }
 
        > label {
                display: block;
-               .box-sizing(border-box);
+               .box-sizing( border-box );
                .agora-label-styling();
                width: auto;
                margin: 0 0 0.2em;
        // Override input styling just for checkboxes and radio inputs.
        input[type="radio"] {
                display: inline;
-               .box-sizing(content-box);
+               .box-sizing( content-box );
                width: auto;
        }
 
-
        // Styles for information boxes
        //
        // Regular HTMLForm uses .error class, some special pages like
        .errorbox,
        .warningbox,
        .successbox {
-               .box-sizing(border-box);
+               .box-sizing( border-box );
                font-size: 0.9em;
                margin: 0 0 1em 0;
                padding: 0.5em;
 
        // Colours taken from those for .errorbox in shared.css
        .error {
-               color: #cc0000;
+               color: @colorErrorText;
                border: 1px solid #fac5c5;
                background-color: #fae3e3;
                text-shadow: 0 1px #fae3e3;
index d9e8c42..9b9d324 100644 (file)
@@ -2,10 +2,10 @@
 @import "mediawiki.ui/variables";
 
 // Mixins
-.mixin-mw-ui-icon-bgimage(@iconSvg, @iconPng) {
+.mixin-mw-ui-icon-bgimage( @iconSvg, @iconPng ) {
        &.mw-ui-icon {
                &:before {
-                       .background-image-svg(@iconSvg, @iconPng);
+                       .background-image-svg( @iconSvg, @iconPng );
                }
        }
 }
@@ -13,7 +13,7 @@
 // Icons
 //
 // To use icons you must be using a browser that supports pseudo elements.
-// This includes support for IE8.
+// This includes support for IE 8.
 // http://caniuse.com/#feat=css-gencontent
 //
 // For elements that are intended to have both an icon and text, browsers that
@@ -45,6 +45,7 @@
                width: @width;
                min-width: @width;
                max-width: @width;
+
                &:before {
                        left: 0;
                        right: 0;
        &.mw-ui-icon-before:before,
        &.mw-ui-icon-element:before {
                background-position: 50% 50%;
-               float: left;
-               display: block;
                background-repeat: no-repeat;
                background-size: 100% auto;
+               float: left;
+               display: block;
                min-height: @iconSize;
                content: '';
        }
 
-
        // Icons with text
        //
        // Markup:
index 62f0e83..d0633ae 100644 (file)
 .mw-ui-input {
        // turn off default input styling for input[type="search"] fields
        -webkit-appearance: none;
-       border: 1px solid @colorFieldBorder;
-       .box-sizing(border-box);
-       width: 100%;
-       padding: .3em .3em .3em .6em;
+       .box-sizing( border-box );
        display: block;
-       vertical-align: middle;
+       width: 100%;
+       border: 1px solid @colorFieldBorder;
        border-radius: @borderRadius;
+       padding: 0.3em 0.3em 0.3em 0.6em;
        font-family: inherit;
        font-size: inherit;
        line-height: inherit;
+       vertical-align: middle;
 
        // Placeholder text styling must be set individually for each browser @winter
        &::-webkit-input-placeholder { // webkit
index 52effd6..448390a 100644 (file)
@@ -5,8 +5,8 @@
 //
 // Styling radios in a way that works cross browser is a tricky problem to solve.
 // In MediaWiki UI put a radio and label inside a mw-ui-radio div.
-// This renders in all browsers except IE6-8 which do not support the :checked selector;
-// these are kept backwards-compatible using the :not(#noop) selector.
+// This renders in all browsers except IE 6-8 which do not support the :checked selector;
+// these are kept backwards-compatible using the `:not( #noop )` selector.
 // You should give the radio and label matching "id" and "for" attributes, respectively.
 //
 // Markup:
        vertical-align: middle;
 }
 
-@radioSize: 2em;
-
 // We use the not selector to cancel out styling on IE 8 and below.
-// We also disable this styling on javascript disabled devices. This fixes the issue with
+// We also disable this styling on JavaScript disabled devices. This fixes the issue with
 // Opera Mini where checking/unchecking doesn't apply styling but potentially leaves other
 // more capable browsers with unstyled radio buttons.
-.client-js .mw-ui-radio:not(#noop) {
+.client-js .mw-ui-radio:not( #noop ) {
        // Position relatively so we can make use of absolute pseudo elements
        position: relative;
        line-height: @radioSize;
                // the pseudo before element of the label after the radio now looks like a radio
                & + label::before {
                        content: '';
-                       cursor: pointer;
-                       .box-sizing(border-box);
+                       background-color: #fff;
+                       .background-image-svg( 'images/radio_checked.svg', 'images/radio_checked.png' );
+                       background-origin: border-box;
+                       background-position: center center;
+                       background-repeat: no-repeat;
+                       .background-size( @radioSize, @radioSize );
+                       background-size: 0 0;
+                       .box-sizing( border-box );
                        position: absolute;
                        left: 0;
-                       border-radius: 100%;
                        width: @radioSize;
                        height: @radioSize;
-                       background-color: #fff;
                        border: 1px solid @colorGray7;
-                       .background-image-svg('images/radio_checked.svg', 'images/radio_checked.png');
-                       .background-size( @radioSize, @radioSize );
-                       background-repeat: no-repeat;
-                       background-position: center center;
-                       background-origin: border-box;
-                       background-size: 0 0;
+                       border-radius: 100%;
+                       cursor: pointer;
                }
 
                // when the input is checked, style the label pseudo before element that followed as a checked radio
 
                // disabled radios have a gray background
                &:disabled + label::before {
-                       cursor: default;
                        background-color: @colorGray14;
                        border-color: @colorGray14;
+                       cursor: default;
                }
 
                // disabled and checked radios have a white circle
                &:disabled:checked + label::before {
-                       .background-image-svg('images/radio_disabled.svg', 'images/radio_disabled.png');
+                       .background-image-svg( 'images/radio_disabled.svg', 'images/radio_disabled.png' );
                }
        }
 }
index 500d42c..cc27e9e 100644 (file)
@@ -26,7 +26,7 @@ Styleguide 6.1.
 */
 
 .mw-ui-text {
-       // The selector order is like this on purpose; IE6 ignores the second selector,
+       // The selector order is like this on purpose; IE 6 ignores the second selector,
        // so we don't want to accidentally apply this color on all mw-ui-CONTEXT classes
        .mw-ui-progressive& {
                color: @colorProgressive;
diff --git a/resources/src/mediawiki.widgets.datetime/CalendarWidget.js b/resources/src/mediawiki.widgets.datetime/CalendarWidget.js
new file mode 100644 (file)
index 0000000..31b1cd5
--- /dev/null
@@ -0,0 +1,593 @@
+( function ( $, mw ) {
+
+       /**
+        * CalendarWidget displays a calendar that can be used to select a date. It
+        * uses {@link mw.widgets.datetime.DateTimeFormatter DateTimeFormatter} to get the details of
+        * the calendar.
+        *
+        * This widget is mainly intended to be used as a popup from a
+        * {@link mw.widgets.datetime.DateTimeInputWidget DateTimeInputWidget}, but may also be used
+        * standalone.
+        *
+        * @class
+        * @extends OO.ui.Widget
+        * @mixins OO.ui.mixin.TabIndexedElement
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for
+        *  mw.widgets.datetime.ProlepticGregorianDateTimeFormatter, or an mw.widgets.datetime.DateTimeFormatter
+        *  instance to use.
+        * @cfg {OO.ui.Widget|null} [widget=null] Widget associated with the calendar.
+        *  Specifying this configures the calendar to be used as a popup from the
+        *  specified widget (e.g. absolute positioning, automatic hiding when clicked
+        *  outside).
+        * @cfg {Date|null} [min=null] Minimum allowed date
+        * @cfg {Date|null} [max=null] Maximum allowed date
+        * @cfg {Date} [focusedDate] Initially focused date.
+        * @cfg {Date|Date[]|null} [selected=null] Selected date(s).
+        */
+       mw.widgets.datetime.CalendarWidget = function MwWidgetsDatetimeCalendarWidget( config ) {
+               var $colgroup, $headTR, headings, i;
+
+               // Configuration initialization
+               config = $.extend( {
+                       min: null,
+                       max: null,
+                       focusedDate: new Date(),
+                       selected: null,
+                       formatter: {}
+               }, config );
+
+               // Parent constructor
+               mw.widgets.datetime.CalendarWidget[ 'super' ].call( this, config );
+
+               // Mixin constructors
+               OO.ui.mixin.TabIndexedElement.call( this, $.extend( {}, config, { $tabIndexed: this.$element } ) );
+
+               // Properties
+               if ( config.min instanceof Date && config.min.getTime() >= -62167219200000 ) {
+                       this.min = config.min;
+               } else {
+                       this.min = new Date( -62167219200000 ); // 0000-01-01T00:00:00.000Z
+               }
+               if ( config.max instanceof Date && config.max.getTime() <= 253402300799999 ) {
+                       this.max = config.max;
+               } else {
+                       this.max = new Date( 253402300799999 ); // 9999-12-31T12:59:59.999Z
+               }
+
+               if ( config.focusedDate instanceof Date ) {
+                       this.focusedDate = config.focusedDate;
+               } else {
+                       this.focusedDate = new Date();
+               }
+
+               this.selected = [];
+
+               if ( config.formatter instanceof mw.widgets.datetime.DateTimeFormatter ) {
+                       this.formatter = config.formatter;
+               } else if ( $.isPlainObject( config.formatter ) ) {
+                       this.formatter = new mw.widgets.datetime.ProlepticGregorianDateTimeFormatter( config.formatter );
+               } else {
+                       throw new Error( '"formatter" must be an mw.widgets.datetime.DateTimeFormatter or a plain object' );
+               }
+
+               this.calendarData = null;
+
+               this.widget = config.widget;
+               this.$widget = config.widget ? config.widget.$element : null;
+               this.onDocumentMouseDownHandler = this.onDocumentMouseDown.bind( this );
+
+               this.$head = $( '<div>' );
+               this.$header = $( '<span>' );
+               this.$table = $( '<table>' );
+               this.cols = [];
+               this.colNullable = [];
+               this.headings = [];
+               this.$tableBody = $( '<tbody>' );
+               this.rows = [];
+               this.buttons = {};
+               this.minWidth = 1;
+               this.daysPerWeek = 0;
+
+               // Events
+               this.$element.on( {
+                       keydown: this.onKeyDown.bind( this )
+               } );
+               this.formatter.connect( this, {
+                       local: 'onLocalChange'
+               } );
+               if ( this.$widget ) {
+                       this.checkFocusHandler = this.checkFocus.bind( this );
+                       this.$element.on( {
+                               focusout: this.onFocusOut.bind( this )
+                       } );
+                       this.$widget.on( {
+                               focusout: this.onFocusOut.bind( this )
+                       } );
+               }
+
+               // Initialization
+               this.$head
+                       .addClass( 'mw-widgets-datetime-calendarWidget-heading' )
+                       .append(
+                               new OO.ui.ButtonWidget( {
+                                       icon: 'previous',
+                                       framed: false,
+                                       classes: [ 'mw-widgets-datetime-calendarWidget-previous' ],
+                                       tabIndex: -1
+                               } ).connect( this, { click: 'onPrevClick' } ).$element,
+                               new OO.ui.ButtonWidget( {
+                                       icon: 'next',
+                                       framed: false,
+                                       classes: [ 'mw-widgets-datetime-calendarWidget-next' ],
+                                       tabIndex: -1
+                               } ).connect( this, { click: 'onNextClick' } ).$element,
+                               this.$header
+                       );
+               $colgroup = $( '<colgroup>' );
+               $headTR = $( '<tr>' );
+               this.$table
+                       .addClass( 'mw-widgets-datetime-calendarWidget-grid' )
+                       .append( $colgroup )
+                       .append( $( '<thead>' ).append( $headTR ) )
+                       .append( this.$tableBody );
+
+               headings = this.formatter.getCalendarHeadings();
+               for ( i = 0; i < headings.length; i++ ) {
+                       this.cols[ i ] = $( '<col>' );
+                       this.headings[ i ] = $( '<th>' );
+                       this.colNullable[ i ] = headings[ i ] === null;
+                       if ( headings[ i ] !== null ) {
+                               this.headings[ i ].text( headings[ i ] );
+                               this.minWidth = Math.max( this.minWidth, headings[ i ].length );
+                               this.daysPerWeek++;
+                       }
+                       $colgroup.append( this.cols[ i ] );
+                       $headTR.append( this.headings[ i ] );
+               }
+
+               this.setSelected( config.selected );
+               this.$element
+                       .addClass( 'mw-widgets-datetime-calendarWidget' )
+                       .append( this.$head, this.$table );
+
+               if ( this.widget ) {
+                       this.$element.addClass( 'mw-widgets-datetime-calendarWidget-dependent' );
+
+                       // Initially hidden - using #toggle may cause errors if subclasses override toggle with methods
+                       // that reference properties not initialized at that time of parent class construction
+                       // TODO: Find a better way to handle post-constructor setup
+                       this.visible = false;
+                       this.$element.addClass( 'oo-ui-element-hidden' );
+               } else {
+                       this.updateUI();
+               }
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.CalendarWidget, OO.ui.Widget );
+       OO.mixinClass( mw.widgets.datetime.CalendarWidget, OO.ui.mixin.TabIndexedElement );
+
+       /* Events */
+
+       /**
+        * A `change` event is emitted when the selected dates change
+        *
+        * @event change
+        */
+
+       /**
+        * A `focusChange` event is emitted when the focused date changes
+        *
+        * @event focusChange
+        */
+
+       /**
+        * A `page` event is emitted when the current "month" changes
+        *
+        * @event page
+        */
+
+       /* Methods */
+
+       /**
+        * Return the current selected dates
+        *
+        * @return {Date[]}
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.getSelected = function () {
+               return this.selected;
+       };
+
+       /**
+        * Set the selected dates
+        *
+        * @param {Date|Date[]|null} dates
+        * @fires change
+        * @chainable
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.setSelected = function ( dates ) {
+               var i, changed = false;
+
+               if ( dates instanceof Date ) {
+                       dates = [ dates ];
+               } else if ( Array.isArray( dates ) ) {
+                       dates = $.grep( dates, function ( dt ) { return dt instanceof Date; } );
+                       dates.sort();
+               } else {
+                       dates = [];
+               }
+
+               if ( this.selected.length !== dates.length ) {
+                       changed = true;
+               } else {
+                       for ( i = 0; i < dates.length; i++ ) {
+                               if ( dates[ i ].getTime() !== this.selected[ i ].getTime() ) {
+                                       changed = true;
+                                       break;
+                               }
+                       }
+               }
+
+               if ( changed ) {
+                       this.selected = dates;
+                       this.emit( 'change', dates );
+                       this.updateUI();
+               }
+
+               return this;
+       };
+
+       /**
+        * Return the currently-focused date
+        *
+        * @return {Date}
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.getFocusedDate = function () {
+               return this.focusedDate;
+       };
+
+       /**
+        * Set the currently-focused date
+        *
+        * @param {Date} date
+        * @fires page
+        * @chainable
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.setFocusedDate = function ( date ) {
+               var changePage = false,
+                       updateUI = false;
+
+               if ( this.focusedDate.getTime() === date.getTime() ) {
+                       return this;
+               }
+
+               if ( !this.formatter.sameCalendarGrid( this.focusedDate, date ) ) {
+                       changePage = true;
+                       updateUI = true;
+               } else if (
+                       !this.formatter.timePartIsEqual( this.focusedDate, date ) ||
+                       !this.formatter.datePartIsEqual( this.focusedDate, date )
+               ) {
+                       updateUI = true;
+               }
+
+               this.focusedDate = date;
+               this.emit( 'focusChanged', this.focusedDate );
+               if ( changePage ) {
+                       this.emit( 'page', date );
+               }
+               if ( updateUI ) {
+                       this.updateUI();
+               }
+
+               return this;
+       };
+
+       /**
+        * Adjust a date
+        *
+        * @protected
+        * @param {Date} date Date to adjust
+        * @param {string} component Component: 'month', 'week', or 'day'
+        * @param {number} delta Integer, usually -1 or 1
+        * @param {boolean} [enforceRange=true] Whether to enforce this.min and this.max
+        * @return {Date}
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.adjustDate = function ( date, component, delta ) {
+               var newDate,
+                       data = this.calendarData;
+
+               if ( !data ) {
+                       return date;
+               }
+
+               switch ( component ) {
+                       case 'month':
+                               newDate = this.formatter.adjustComponent( date, data.monthComponent, delta, 'overflow' );
+                               break;
+
+                       case 'week':
+                               if ( data.weekComponent === undefined ) {
+                                       newDate = this.formatter.adjustComponent(
+                                               date, data.dayComponent, delta * this.daysPerWeek, 'overflow' );
+                               } else {
+                                       newDate = this.formatter.adjustComponent( date, data.weekComponent, delta, 'overflow' );
+                               }
+                               break;
+
+                       case 'day':
+                               newDate = this.formatter.adjustComponent( date, data.dayComponent, delta, 'overflow' );
+                               break;
+
+                       default:
+                               throw new Error( 'Unknown component' );
+               }
+
+               while ( newDate < this.min ) {
+                       newDate = this.formatter.adjustComponent( newDate, data.dayComponent, 1, 'overflow' );
+               }
+               while ( newDate > this.max ) {
+                       newDate = this.formatter.adjustComponent( newDate, data.dayComponent, -1, 'overflow' );
+               }
+
+               return newDate;
+       };
+
+       /**
+        * Update the user interface
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.updateUI = function () {
+               var r, c, row, day, k, $cell,
+                       width = this.minWidth,
+                       nullCols = [],
+                       focusedDate = this.getFocusedDate(),
+                       selected = this.getSelected(),
+                       datePartIsEqual = this.formatter.datePartIsEqual.bind( this.formatter ),
+                       isSelected = function ( dt ) {
+                               return datePartIsEqual( this, dt );
+                       };
+
+               this.calendarData = this.formatter.getCalendarData( focusedDate );
+
+               this.$header.text( this.calendarData.header );
+
+               for ( c = 0; c < this.colNullable.length; c++ ) {
+                       nullCols[ c ] = this.colNullable[ c ];
+                       if ( nullCols[ c ] ) {
+                               for ( r = 0; r < this.calendarData.rows.length; r++ ) {
+                                       if ( this.calendarData.rows[ r ][ c ] ) {
+                                               nullCols[ c ] = false;
+                                               break;
+                                       }
+                               }
+                       }
+               }
+
+               this.$tableBody.children().detach();
+               for ( r = 0; r < this.calendarData.rows.length; r++ ) {
+                       if ( !this.rows[ r ] ) {
+                               this.rows[ r ] = $( '<tr>' );
+                       } else {
+                               this.rows[ r ].children().detach();
+                       }
+                       this.$tableBody.append( this.rows[ r ] );
+                       row = this.calendarData.rows[ r ];
+                       for ( c = 0; c < row.length; c++ ) {
+                               day = row[ c ];
+                               if ( day === null ) {
+                                       k = 'empty-' + r + '-' + c;
+                                       if ( !this.buttons[ k ] ) {
+                                               this.buttons[ k ] = $( '<td>' );
+                                       }
+                                       $cell = this.buttons[ k ];
+                                       $cell.toggleClass( 'oo-ui-element-hidden', nullCols[ c ] );
+                               } else {
+                                       k = ( day.extra ? day.extra : '' ) + day.display;
+                                       width = Math.max( width, day.display.length );
+                                       if ( !this.buttons[ k ] ) {
+                                               this.buttons[ k ] = new OO.ui.ButtonWidget( {
+                                                       $element: $( '<td>' ),
+                                                       classes: [
+                                                               'mw-widgets-datetime-calendarWidget-cell',
+                                                               day.extra ? 'mw-widgets-datetime-calendarWidget-extra' : ''
+                                                       ],
+                                                       framed: true,
+                                                       label: day.display,
+                                                       tabIndex: -1
+                                               } );
+                                               this.buttons[ k ].connect( this, { click: [ 'onDayClick', this.buttons[ k ] ] } );
+                                       }
+                                       this.buttons[ k ]
+                                               .setData( day.date )
+                                               .setDisabled( day.date < this.min || day.date > this.max );
+                                       $cell = this.buttons[ k ].$element;
+                                       $cell.toggleClass( 'mw-widgets-datetime-calendarWidget-focused',
+                                               this.formatter.datePartIsEqual( focusedDate, day.date ) );
+                                       $cell.toggleClass( 'mw-widgets-datetime-calendarWidget-selected',
+                                               selected.some( isSelected, day.date ) );
+                               }
+                               this.rows[ r ].append( $cell );
+                       }
+               }
+
+               for ( c = 0; c < this.cols.length; c++ ) {
+                       if ( nullCols[ c ] ) {
+                               this.cols[ c ].width( 0 );
+                       } else {
+                               this.cols[ c ].width( width + 'em' );
+                       }
+                       this.cols[ c ].toggleClass( 'oo-ui-element-hidden', nullCols[ c ] );
+                       this.headings[ c ].toggleClass( 'oo-ui-element-hidden', nullCols[ c ] );
+               }
+       };
+
+       /**
+        * Handles formatter 'local' flag changing
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onLocalChange = function () {
+               if ( this.formatter.localChangesDatePart( this.getFocusedDate() ) ) {
+                       this.emit( 'page', this.getFocusedDate() );
+               }
+
+               this.updateUI();
+       };
+
+       /**
+        * Handles previous button click
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onPrevClick = function () {
+               this.setFocusedDate( this.adjustDate( this.getFocusedDate(), 'month', -1 ) );
+               if ( !this.$widget || OO.ui.contains( this.$element[ 0 ], document.activeElement, true ) ) {
+                       this.$element.focus();
+               }
+       };
+
+       /**
+        * Handles next button click
+        *
+        * @protected
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onNextClick = function () {
+               this.setFocusedDate( this.adjustDate( this.getFocusedDate(), 'month', 1 ) );
+               if ( !this.$widget || OO.ui.contains( this.$element[ 0 ], document.activeElement, true ) ) {
+                       this.$element.focus();
+               }
+       };
+
+       /**
+        * Handles day button click
+        *
+        * @protected
+        * @param {OO.ui.ButtonWidget} $button
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onDayClick = function ( $button ) {
+               this.setFocusedDate( $button.getData() );
+               this.setSelected( [ $button.getData() ] );
+               if ( !this.$widget || OO.ui.contains( this.$element[ 0 ], document.activeElement, true ) ) {
+                       this.$element.focus();
+               }
+       };
+
+       /**
+        * Handles document mouse down events.
+        *
+        * @protected
+        * @param {jQuery.Event} e Mouse down event
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onDocumentMouseDown = function ( e ) {
+               if ( this.$widget &&
+                       !OO.ui.contains( this.$element[ 0 ], e.target, true ) &&
+                       !OO.ui.contains( this.$widget[ 0 ], e.target, true )
+               ) {
+                       this.toggle( false );
+               }
+       };
+
+       /**
+        * Handles key presses.
+        *
+        * @protected
+        * @param {jQuery.Event} e Key down event
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onKeyDown = function ( e ) {
+               var focusedDate = this.getFocusedDate();
+
+               if ( !this.isDisabled() ) {
+                       switch ( e.which ) {
+                               case OO.ui.Keys.ENTER:
+                               case OO.ui.Keys.SPACE:
+                                       this.setSelected( [ focusedDate ] );
+                                       return false;
+
+                               case OO.ui.Keys.LEFT:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'day', -1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.RIGHT:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'day', 1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.UP:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'week', -1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.DOWN:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'week', 1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.PAGEUP:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'month', -1 ) );
+                                       return false;
+
+                               case OO.ui.Keys.PAGEDOWN:
+                                       this.setFocusedDate( this.adjustDate( focusedDate, 'month', 1 ) );
+                                       return false;
+                       }
+               }
+       };
+
+       /**
+        * Handles focusout events in dependent mode
+        *
+        * @private
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.onFocusOut = function () {
+               setTimeout( this.checkFocusHandler );
+       };
+
+       /**
+        * When we or our widget lost focus, check if the calendar should be hidden.
+        *
+        * @private
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.checkFocus = function () {
+               var containers = [ this.$element[ 0 ], this.$widget[ 0 ] ],
+                       activeElement = document.activeElement;
+
+               if ( !activeElement || !OO.ui.contains( containers, activeElement, true ) ) {
+                       this.toggle( false );
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.CalendarWidget.prototype.toggle = function ( visible ) {
+               var change;
+
+               visible = ( visible === undefined ? !this.visible : !!visible );
+               change = visible !== this.isVisible();
+
+               // Parent method
+               mw.widgets.datetime.CalendarWidget[ 'super' ].prototype.toggle.call( this, visible );
+
+               if ( change ) {
+                       if ( visible ) {
+                               // Auto-hide
+                               if ( this.$widget ) {
+                                       this.getElementDocument().addEventListener(
+                                               'mousedown', this.onDocumentMouseDownHandler, true
+                                       );
+                               }
+                               this.updateUI();
+                       } else {
+                               this.getElementDocument().removeEventListener(
+                                       'mousedown', this.onDocumentMouseDownHandler, true
+                               );
+                       }
+               }
+
+               return this;
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/CalendarWidget.less b/resources/src/mediawiki.widgets.datetime/CalendarWidget.less
new file mode 100644 (file)
index 0000000..a7beb0d
--- /dev/null
@@ -0,0 +1,74 @@
+@import "mediawiki.widgets.datetime.definitions";
+
+.mw-widgets-datetime-calendarWidget {
+       display: inline-block;
+       position: relative;
+       vertical-align: middle;
+       padding: .5em;
+
+       &.mw-widgets-datetime-calendarWidget-dependent {
+               display: block;
+               position: absolute;
+               z-index: 4;
+       }
+
+       &-grid {
+               table-layout: fixed;
+
+               .mw-widgets-datetime-calendarWidget-cell {
+                       display: table-cell;
+                       white-space: nowrap;
+               }
+       }
+
+       background-color: white;
+       border: 1px solid #ccc;
+
+       &.mw-widgets-datetime-calendarWidget-dependent {
+               margin-top: -1px;
+               border-top: 1px solid white;
+       }
+
+       &-heading {
+               text-align: center;
+               vertical-align: middle;
+               font-weight: bold;
+               white-space: nowrap;
+
+               .mw-widgets-datetime-calendarWidget-previous {
+                       float: left;
+               }
+               .mw-widgets-datetime-calendarWidget-next {
+                       float: right;
+               }
+       }
+
+       &-grid {
+               margin: 0 auto;
+
+               .mw-widgets-datetime-calendarWidget-cell {
+                       text-align: center;
+
+                       .oo-ui-buttonElement-button {
+                               width: 100%;
+                               border: 1px dotted rgba(255,255,255,0.0);
+                               .oo-ui-box-sizing( border-box );
+                       }
+
+                       &.mw-widgets-datetime-calendarWidget-extra .oo-ui-buttonElement-button .oo-ui-labelElement-label {
+                               color: #bbb;
+                       }
+
+                       &.mw-widgets-datetime-calendarWidget-selected .oo-ui-buttonElement-button {
+                               background-color: #def;
+                               .oo-ui-labelElement-label {
+                                       color: #38f;
+                               }
+                       }
+               }
+       }
+
+       &:focus &-grid &-cell&-focused .oo-ui-buttonElement-button {
+               border-color: rgba(0,0,0,0.3);
+       }
+}
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/DateTimeFormatter.js
new file mode 100644 (file)
index 0000000..1c54234
--- /dev/null
@@ -0,0 +1,623 @@
+( function ( $, mw ) {
+
+       /**
+        * Provides various methods needed for formatting dates and times.
+        *
+        * @class
+        * @abstract
+        * @mixins OO.EventEmitter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {string} [format='@default'] May be a key from the {@link #static-formats static formats},
+        *  or a format specification as defined by {@link #method-parseFieldSpec parseFieldSpec}
+        *  and {@link #method-getFieldForTag getFieldForTag}.
+        * @cfg {boolean} [local=false] Whether dates are local time or UTC
+        * @cfg {string[]} [fullZones] Time zone indicators. Array of 2 strings, for
+        *  UTC and local time.
+        * @cfg {string[]} [shortZones] Abbreviated time zone indicators. Array of 2
+        *  strings, for UTC and local time.
+        * @cfg {Date} [defaultDate] Default date, for filling unspecified components.
+        *  Defaults to the current date and time (with 0 milliseconds).
+        */
+       mw.widgets.datetime.DateTimeFormatter = function MwWidgetsDatetimeDateTimeFormatter( config ) {
+               var statick = this.constructor[ 'static' ];
+
+               statick.setupDefaults();
+
+               config = $.extend( {
+                       format: '@default',
+                       local: false,
+                       fullZones: statick.fullZones,
+                       shortZones: statick.shortZones
+               }, config );
+
+               // Mixin constructors
+               OO.EventEmitter.call( this );
+
+               // Properties
+               if ( statick.formats[ config.format ] ) {
+                       this.format = statick.formats[ config.format ];
+               } else {
+                       this.format = config.format;
+               }
+               this.local = !!config.local;
+               this.fullZones = config.fullZones;
+               this.shortZones = config.shortZones;
+               if ( config.defaultDate instanceof Date ) {
+                       this.defaultDate = config.defaultDate;
+               } else {
+                       this.defaultDate = new Date();
+                       if ( this.local ) {
+                               this.defaultDate.setMilliseconds( 0 );
+                       } else {
+                               this.defaultDate.setUTCMilliseconds( 0 );
+                       }
+               }
+       };
+
+       /* Setup */
+
+       OO.initClass( mw.widgets.datetime.DateTimeFormatter );
+       OO.mixinClass( mw.widgets.datetime.DateTimeFormatter, OO.EventEmitter );
+
+       /* Static */
+
+       /**
+        * Default format specifications. See the {@link #format format} parameter.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].formats = {};
+
+       /**
+        * Default time zone indicators
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].fullZones = null;
+
+       /**
+        * Default abbreviated time zone indicators
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].shortZones = null;
+
+       mw.widgets.datetime.DateTimeFormatter[ 'static' ].setupDefaults = function () {
+               if ( !this.fullZones ) {
+                       this.fullZones = [
+                               mw.msg( 'timezone-utc' ),
+                               mw.msg( 'timezone-local' )
+                       ];
+               }
+               if ( !this.shortZones ) {
+                       this.shortZones = [
+                               'Z',
+                               this.fullZones[ 1 ].substr( 0, 1 ).toUpperCase()
+                       ];
+                       if ( this.shortZones[ 1 ] === 'Z' ) {
+                               this.shortZones[ 1 ] = 'L';
+                       }
+               }
+       };
+
+       /* Events */
+
+       /**
+        * A `local` event is emitted when the 'local' flag is changed.
+        *
+        * @event local
+        */
+
+       /* Methods */
+
+       /**
+        * Whether dates are in local time or UTC
+        *
+        * @return {boolean} True if local time
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getLocal = function () {
+               return this.local;
+       };
+
+       /**
+        * Toggle whether dates are in local time or UTC
+        *
+        * @param {boolean} [flag] Set the flag instead of toggling it
+        * @fires local
+        * @chainable
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.toggleLocal = function ( flag ) {
+               if ( flag === undefined ) {
+                       flag = !this.local;
+               } else {
+                       flag = !!flag;
+               }
+               if ( this.local !== flag ) {
+                       this.local = flag;
+                       this.emit( 'local', this.local );
+               }
+               return this;
+       };
+
+       /**
+        * Get the default date
+        *
+        * @return {Date}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getDefaultDate = function () {
+               return new Date( this.defaultDate.getTime() );
+       };
+
+       /**
+        * Fetch the field specification array for this object.
+        *
+        * See {@link #parseFieldSpec parseFieldSpec} for details on the return value structure.
+        *
+        * @return {Array}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getFieldSpec = function () {
+               return this.parseFieldSpec( this.format );
+       };
+
+       /**
+        * Parse a format string into a field specification
+        *
+        * The input is a string containing tags formatted as ${tag|param|param...}
+        * (for editable fields) and $!{tag|param|param...} (for non-editable fields).
+        * Most tags are defined by {@link #getFieldForTag getFieldForTag}, but a few
+        * are defined here:
+        * - ${intercalary|X|text}: Text that is only displayed when the 'intercalary'
+        *   component is X.
+        * - ${not-intercalary|X|text}: Text that is displayed unless the 'intercalary'
+        *   component is X.
+        *
+        * Elements of the returned array are strings or objects. Strings are meant to
+        * be displayed as-is. Objects are as returned by {@link #getFieldForTag getFieldForTag}.
+        *
+        * @protected
+        * @param {string} format
+        * @return {Array}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.parseFieldSpec = function ( format ) {
+               var m, last, tag, params, spec,
+                       ret = [],
+                       re = /(.*?)(\$(!?)\{([^}]+)\})/g;
+
+               last = 0;
+               while ( ( m = re.exec( format ) ) !== null ) {
+                       last = re.lastIndex;
+
+                       if ( m[ 1 ] !== '' ) {
+                               ret.push( m[ 1 ] );
+                       }
+
+                       params = m[ 4 ].split( '|' );
+                       tag = params.shift();
+                       spec = this.getFieldForTag( tag, params );
+                       if ( spec ) {
+                               if ( m[ 3 ] === '!' ) {
+                                       spec.editable = false;
+                               }
+                               ret.push( spec );
+                       } else {
+                               ret.push( m[ 2 ] );
+                       }
+               }
+               if ( last < format.length ) {
+                       ret.push( format.substr( last ) );
+               }
+
+               return ret;
+       };
+
+       /**
+        * Turn a tag into a field specification object
+        *
+        * Fields implemented here are:
+        * - ${intercalary|X|text}: Text that is only displayed when the 'intercalary'
+        *   component is X.
+        * - ${not-intercalary|X|text}: Text that is displayed unless the 'intercalary'
+        *   component is X.
+        * - ${zone|#}: Timezone offset, "+0000" format.
+        * - ${zone|:}: Timezone offset, "+00:00" format.
+        * - ${zone|short}: Timezone from 'shortZones' configuration setting.
+        * - ${zone|full}: Timezone from 'fullZones' configuration setting.
+        *
+        * @protected
+        * @abstract
+        * @param {string} tag
+        * @param {string[]} params
+        * @return {Object|null} Field specification object, or null if the tag+params are unrecognized.
+        * @return {string|null} return.component Date component corresponding to this field, if any.
+        * @return {boolean} return.editable Whether this field is editable.
+        * @return {string} return.type What kind of field this is:
+        *  - 'static': The field is a static string; component will be null.
+        *  - 'number': The field is generally numeric.
+        *  - 'string': The field is generally textual.
+        *  - 'boolean': The field is a boolean.
+        *  - 'toggleLocal': The field represents {@link #getLocal this.getLocal()}.
+        *    Editing should directly call {@link #toggleLocal this.toggleLocal()}.
+        * @return {number} return.size Maximum number of characters in the field (when
+        *  the 'intercalary' component is falsey). If 0, the field should be hidden entirely.
+        * @return {Object.<string,number>} return.intercalarySize Map from
+        *  'intercalary' component values to overridden sizes.
+        * @return {string} return.value For type='static', the string to display.
+        * @return {function(Mixed): string} return.formatValue A function to format a
+        *  component value as a display string.
+        * @return {function(string): Mixed} return.parseValue A function to parse a
+        *  display string into a component value. If parsing fails, returns undefined.
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+               var c, spec = null;
+
+               switch ( tag ) {
+                       case 'intercalary':
+                       case 'not-intercalary':
+                               if ( params.length < 2 || !params[ 0 ] ) {
+                                       return null;
+                               }
+                               spec = {
+                                       component: null,
+                                       editable: false,
+                                       type: 'static',
+                                       value: params.slice( 1 ).join( '|' ),
+                                       size: 0,
+                                       intercalarySize: {}
+                               };
+                               if ( tag === 'intercalary' ) {
+                                       spec.intercalarySize[ params[ 0 ] ] = spec.value.length;
+                               } else {
+                                       spec.size = spec.value.length;
+                                       spec.intercalarySize[ params[ 0 ] ] = 0;
+                               }
+                               return spec;
+
+                       case 'zone':
+                               switch ( params[ 0 ] ) {
+                                       case '#':
+                                       case ':':
+                                               c = params[ 0 ] === '#' ? '' : ':';
+                                               return {
+                                                       component: 'zone',
+                                                       editable: true,
+                                                       type: 'toggleLocal',
+                                                       size: 5 + c.length,
+                                                       formatValue: function ( v ) {
+                                                               var o, r;
+                                                               if ( v ) {
+                                                                       o = new Date().getTimezoneOffset();
+                                                                       r = String( Math.abs( o ) % 60 );
+                                                                       while ( r.length < 2 ) {
+                                                                               r = '0' + r;
+                                                                       }
+                                                                       r = String( Math.floor( Math.abs( o ) / 60 ) ) + c + r;
+                                                                       while ( r.length < 4 + c.length ) {
+                                                                               r = '0' + r;
+                                                                       }
+                                                                       return ( o <= 0 ? '+' : '−' ) + r;
+                                                               } else {
+                                                                       return '+00' + c + '00';
+                                                               }
+                                                       },
+                                                       parseValue: function ( v ) {
+                                                               var m;
+                                                               v = String( v ).trim();
+                                                               if ( ( m = /^([+-−])([0-9]{1,2}):?([0-9]{2})$/.test( v ) ) ) {
+                                                                       return ( m[ 2 ] * 60 + m[ 3 ] ) * ( m[ 1 ] === '+' ? -1 : 1 );
+                                                               } else {
+                                                                       return undefined;
+                                                               }
+                                                       }
+                                               };
+
+                                       case 'short':
+                                       case 'full':
+                                               spec = {
+                                                       component: 'zone',
+                                                       editable: true,
+                                                       type: 'toggleLocal',
+                                                       values: params[ 0 ] === 'short' ? this.shortZones : this.fullZones,
+                                                       formatValue: this.formatSpecValue,
+                                                       parseValue: this.parseSpecValue
+                                               };
+                                               spec.size = Math.max.apply(
+                                                       null, $.map( spec.values, function ( v ) { return v.length; } )
+                                               );
+                                               return spec;
+                               }
+                               return null;
+
+                       default:
+                               return null;
+               }
+       };
+
+       /**
+        * Format a value for a field specification
+        *
+        * 'this' must be the field specification object. The intention is that you
+        * could just assign this function as the 'formatValue' for each field spec.
+        *
+        * Besides the publicly-documented fields, uses the following:
+        * - values: Enumerated values for the field
+        * - zeropad: Whether to pad the number with zeros.
+        *
+        * @protected
+        * @param {Mixed} v
+        * @return {string}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.formatSpecValue = function ( v ) {
+               if ( v === undefined || v === null ) {
+                       return '';
+               }
+
+               if ( typeof v === 'boolean' || this.type === 'toggleLocal' ) {
+                       v = v ? 1 : 0;
+               }
+
+               if ( this.values ) {
+                       return this.values[ v ];
+               }
+
+               v = String( v );
+               if ( this.zeropad ) {
+                       while ( v.length < this.size ) {
+                               v = '0' + v;
+                       }
+               }
+               return v;
+       };
+
+       /**
+        * Parse a value for a field specification
+        *
+        * 'this' must be the field specification object. The intention is that you
+        * could just assign this function as the 'parseValue' for each field spec.
+        *
+        * Besides the publicly-documented fields, uses the following:
+        * - values: Enumerated values for the field
+        *
+        * @protected
+        * @param {string} v
+        * @return {number|string|null}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.parseSpecValue = function ( v ) {
+               var k, re;
+
+               if ( v === '' ) {
+                       return null;
+               }
+
+               if ( !this.values ) {
+                       v = +v;
+                       if ( this.type === 'boolean' || this.type === 'toggleLocal' ) {
+                               return isNaN( v ) ? undefined : !!v;
+                       } else {
+                               return isNaN( v ) ? undefined : v;
+                       }
+               }
+
+               if ( v.normalize ) {
+                       v = v.normalize();
+               }
+               re = new RegExp( '^\\s*' + v.replace( /([\\{}()|.?*+\-\^$\[\]])/g, '\\$1' ), 'i' );
+               for ( k in this.values ) {
+                       k = +k;
+                       if ( !isNaN( k ) && re.test( this.values[ k ] ) ) {
+                               if ( this.type === 'boolean' || this.type === 'toggleLocal' ) {
+                                       return !!k;
+                               } else {
+                                       return k;
+                               }
+                       }
+               }
+               return undefined;
+       };
+
+       /**
+        * Get components from a Date object
+        *
+        * Most specific components are defined by the subclass. "Global" components
+        * are:
+        * - intercalary: {string} Non-falsey values are used to indicate intercalary days.
+        * - zone: {number} Timezone offset in minutes.
+        *
+        * @abstract
+        * @param {Date|null} date
+        * @return {Object} Components
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+               // Should be overridden by subclass
+               return {
+                       zone: this.local ? date.getTimezoneOffset() : 0
+               };
+       };
+
+       /**
+        * Get a Date object from components
+        *
+        * @param {Object} components Date components
+        * @return {Date}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getDateFromComponents = function ( /* components */ ) {
+               // Should be overridden by subclass
+               return new Date();
+       };
+
+       /**
+        * Adjust a date
+        *
+        * @param {Date|null} date To be adjusted
+        * @param {string} component To adjust
+        * @param {number} delta Adjustment amount
+        * @param {string} mode Adjustment mode:
+        *  - 'overflow': "Jan 32" => "Feb 1", "Jan 33" => "Feb 2", "Feb 0" => "Jan 31", etc.
+        *  - 'wrap': "Jan 32" => "Jan 1", "Jan 33" => "Jan 2", "Jan 0" => "Jan 31", etc.
+        *  - 'clip': "Jan 32" => "Jan 31", "Feb 32" => "Feb 28" (or 29), "Feb 0" => "Feb 1", etc.
+        * @return {Date} Adjusted date
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.adjustComponent = function ( date /*, component, delta, mode */ ) {
+               // Should be overridden by subclass
+               return date;
+       };
+
+       /**
+        * Get the column headings (weekday abbreviations) for a calendar grid
+        *
+        * Null-valued columns are hidden if getCalendarData() returns no "day" object
+        * for all days in that column.
+        *
+        * @abstract
+        * @return {Array} string or null
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getCalendarHeadings = function () {
+               // Should be overridden by subclass
+               return [];
+       };
+
+       /**
+        * Test whether two dates are in the same calendar grid
+        *
+        * @abstract
+        * @param {Date} date1
+        * @param {Date} date2
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+               // Should be overridden by subclass
+               return date1.getTime() === date2.getTime();
+       };
+
+       /**
+        * Test whether the date parts of two Dates are equal
+        *
+        * @param {Date} date1
+        * @param {Date} date2
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.datePartIsEqual = function ( date1, date2 ) {
+               if ( this.local ) {
+                       return (
+                               date1.getFullYear() === date2.getFullYear() &&
+                               date1.getMonth() === date2.getMonth() &&
+                               date1.getDate() === date2.getDate()
+                       );
+               } else {
+                       return (
+                               date1.getUTCFullYear() === date2.getUTCFullYear() &&
+                               date1.getUTCMonth() === date2.getUTCMonth() &&
+                               date1.getUTCDate() === date2.getUTCDate()
+                       );
+               }
+       };
+
+       /**
+        * Test whether the time parts of two Dates are equal
+        *
+        * @param {Date} date1
+        * @param {Date} date2
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.timePartIsEqual = function ( date1, date2 ) {
+               if ( this.local ) {
+                       return (
+                               date1.getHours() === date2.getHours() &&
+                               date1.getMinutes() === date2.getMinutes() &&
+                               date1.getSeconds() === date2.getSeconds() &&
+                               date1.getMilliseconds() === date2.getMilliseconds()
+                       );
+               } else {
+                       return (
+                               date1.getUTCHours() === date2.getUTCHours() &&
+                               date1.getUTCMinutes() === date2.getUTCMinutes() &&
+                               date1.getUTCSeconds() === date2.getUTCSeconds() &&
+                               date1.getUTCMilliseconds() === date2.getUTCMilliseconds()
+                       );
+               }
+       };
+
+       /**
+        * Test whether toggleLocal() changes the date part
+        *
+        * @param {Date} date
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.localChangesDatePart = function ( date ) {
+               return (
+                       date.getUTCFullYear() !== date.getFullYear() ||
+                       date.getUTCMonth() !== date.getMonth() ||
+                       date.getUTCDate() !== date.getDate()
+               );
+       };
+
+       /**
+        * Create a new Date by merging the date part from one with the time part from
+        * another.
+        *
+        * @param {Date} datepart
+        * @param {Date} timepart
+        * @return {Date}
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.mergeDateAndTime = function ( datepart, timepart ) {
+               var ret = new Date( datepart.getTime() );
+
+               if ( this.local ) {
+                       ret.setHours(
+                               timepart.getHours(),
+                               timepart.getMinutes(),
+                               timepart.getSeconds(),
+                               timepart.getMilliseconds()
+                       );
+               } else {
+                       ret.setUTCHours(
+                               timepart.getUTCHours(),
+                               timepart.getUTCMinutes(),
+                               timepart.getUTCSeconds(),
+                               timepart.getUTCMilliseconds()
+                       );
+               }
+
+               return ret;
+       };
+
+       /**
+        * Get data for a calendar grid
+        *
+        * A "day" object is:
+        * - display: {string} Display text for the day.
+        * - date: {Date} Date to use when the day is selected.
+        * - extra: {string|null} 'prev' or 'next' on days used to fill out the weeks
+        *   at the start and end of the month.
+        *
+        * In any one result object, 'extra' + 'display' will always be unique.
+        *
+        * @abstract
+        * @param {Date|null} current Current date
+        * @return {Object} Data
+        * @return {string} return.header String to display as the calendar header
+        * @return {string} return.monthComponent Component to adjust by ±1 to change months.
+        * @return {string} return.dayComponent Component to adjust by ±1 to change days.
+        * @return {string} [return.weekComponent] Component to adjust by ±1 to change
+        *   weeks. If omitted, the dayComponent should be adjusted by ±the number of
+        *   non-nullable columns returned by this.getCalendarHeadings() to change weeks.
+        * @return {Array} return.rows Array of arrays of "day" objects or null/undefined.
+        */
+       mw.widgets.datetime.DateTimeFormatter.prototype.getCalendarData = function ( /* components */ ) {
+               // Should be overridden by subclass
+               return {
+                       header: '',
+                       monthComponent: 'month',
+                       dayComponent: 'day',
+                       rows: []
+               };
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.js
new file mode 100644 (file)
index 0000000..df148c7
--- /dev/null
@@ -0,0 +1,812 @@
+( function ( $, mw ) {
+
+       /**
+        * DateTimeInputWidgets can be used to input a date, a time, or a date and
+        * time, in either UTC or the user's local timezone.
+        * Please see the [OOjs UI documentation on MediaWiki] [1] for more information and examples.
+        *
+        * This widget can be used inside a HTML form, such as a OO.ui.FormLayout.
+        *
+        *     @example
+        *     // Example of a text input widget
+        *     var dateTimeInput = new mw.widgets.datetime.DateTimeInputWidget( {} )
+        *     $( 'body' ).append( dateTimeInput.$element );
+        *
+        * [1]: https://www.mediawiki.org/wiki/OOjs_UI/Widgets/Inputs
+        *
+        * @class
+        * @extends OO.ui.InputWidget
+        * @mixins OO.ui.mixin.IconElement
+        * @mixins OO.ui.mixin.IndicatorElement
+        * @mixins OO.ui.mixin.PendingElement
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {string} [type='datetime'] Whether to act like a 'date', 'time', or 'datetime' input.
+        *  Affects values stored in the relevant <input> and the formatting and
+        *  interpretation of values passed to/from getValue() and setValue(). It's up
+        *  to the user to configure the DateTimeFormatter correctly.
+        * @cfg {Object|mw.widgets.datetime.DateTimeFormatter} [formatter={}] Configuration options for
+        *  mw.widgets.datetime.ProlepticGregorianDateTimeFormatter (with 'format' defaulting to
+        *  '@date', '@time', or '@datetime' depending on 'type'), or an
+        *  mw.widgets.datetime.DateTimeFormatter instance to use.
+        * @cfg {Object|null} [calendar={}] Configuration options for
+        *  mw.widgets.datetime.CalendarWidget; note certain settings will be forced based on the
+        *  settings passed to this widget. Set null to disable the calendar.
+        * @cfg {boolean} [required=false] Whether a value is required.
+        * @cfg {boolean} [clearable=true] Whether to provide for blanking the value.
+        * @cfg {Date|null} [value=null] Default value for the widget
+        * @cfg {Date|string|null} [min=null] Minimum allowed date
+        * @cfg {Date|string|null} [max=null] Maximum allowed date
+        */
+       mw.widgets.datetime.DateTimeInputWidget = function MwWidgetsDatetimeDateTimeInputWidget( config ) {
+               // Configuration initialization
+               config = $.extend( {
+                       type: 'datetime',
+                       clearable: true,
+                       required: false,
+                       min: null,
+                       max: null,
+                       formatter: {},
+                       calendar: {}
+               }, config );
+
+               if ( $.isPlainObject( config.formatter ) && config.formatter.format === undefined ) {
+                       config.formatter.format = '@' + config.type;
+               }
+
+               // Parent constructor
+               mw.widgets.datetime.DateTimeInputWidget[ 'super' ].call( this, config );
+
+               // Mixin constructors
+               OO.ui.mixin.IconElement.call( this, config );
+               OO.ui.mixin.IndicatorElement.call( this, config );
+               OO.ui.mixin.PendingElement.call( this, config );
+
+               // Properties
+               this.type = config.type;
+               this.$handle = $( '<span>' );
+               this.$fields = $( '<span>' );
+               this.fields = [];
+               this.clearable = !!config.clearable;
+               this.required = !!config.required;
+
+               if ( typeof config.min === 'string' ) {
+                       config.min = this.parseDateValue( config.min );
+               }
+               if ( config.min instanceof Date && config.min.getTime() >= -62167219200000 ) {
+                       this.min = config.min;
+               } else {
+                       this.min = new Date( -62167219200000 ); // 0000-01-01T00:00:00.000Z
+               }
+
+               if ( typeof config.max === 'string' ) {
+                       config.max = this.parseDateValue( config.max );
+               }
+               if ( config.max instanceof Date && config.max.getTime() <= 253402300799999 ) {
+                       this.max = config.max;
+               } else {
+                       this.max = new Date( 253402300799999 ); // 9999-12-31T12:59:59.999Z
+               }
+
+               switch ( this.type ) {
+                       case 'date':
+                               this.min.setUTCHours( 0, 0, 0, 0 );
+                               this.max.setUTCHours( 23, 59, 59, 999 );
+                               break;
+                       case 'time':
+                               this.min.setUTCFullYear( 1970, 0, 1 );
+                               this.max.setUTCFullYear( 1970, 0, 1 );
+                               break;
+               }
+               if ( this.min > this.max ) {
+                       throw new Error(
+                               '"min" (' + this.min.toISOString() + ') must not be greater than ' +
+                               '"max" (' + this.max.toISOString() + ')'
+                       );
+               }
+
+               if ( config.formatter instanceof mw.widgets.datetime.DateTimeFormatter ) {
+                       this.formatter = config.formatter;
+               } else if ( $.isPlainObject( config.formatter ) ) {
+                       this.formatter = new mw.widgets.datetime.ProlepticGregorianDateTimeFormatter( config.formatter );
+               } else {
+                       throw new Error( '"formatter" must be an mw.widgets.datetime.DateTimeFormatter or a plain object' );
+               }
+
+               if ( this.type === 'time' || config.calendar === null ) {
+                       this.calendar = null;
+               } else {
+                       config.calendar = $.extend( {}, config.calendar, {
+                               formatter: this.formatter,
+                               widget: this,
+                               min: this.min,
+                               max: this.max
+                       } );
+                       this.calendar = new mw.widgets.datetime.CalendarWidget( config.calendar );
+               }
+
+               // Events
+               this.$handle.on( {
+                       click: this.onHandleClick.bind( this )
+               } );
+               this.connect( this, {
+                       change: 'onChange'
+               } );
+               this.formatter.connect( this, {
+                       local: 'onChange'
+               } );
+               if ( this.calendar ) {
+                       this.calendar.connect( this, {
+                               change: 'onCalendarChange'
+                       } );
+               }
+
+               // Initialization
+               this.setTabIndex( -1 );
+
+               this.$fields.addClass( 'mw-widgets-datetime-dateTimeInputWidget-fields' );
+               this.setupFields();
+
+               this.$handle
+                       .addClass( 'mw-widgets-datetime-dateTimeInputWidget-handle' )
+                       .append( this.$icon, this.$indicator, this.$fields );
+
+               this.$element
+                       .addClass( 'mw-widgets-datetime-dateTimeInputWidget' )
+                       .append( this.$handle );
+
+               if ( this.calendar ) {
+                       this.$element.append( this.calendar.$element );
+               }
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.InputWidget );
+       OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.IconElement );
+       OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.IndicatorElement );
+       OO.mixinClass( mw.widgets.datetime.DateTimeInputWidget, OO.ui.mixin.PendingElement );
+
+       /* Static properties */
+
+       mw.widgets.datetime.DateTimeInputWidget[ 'static' ].supportsSimpleLabel = false;
+
+       /* Events */
+
+       /* Methods */
+
+       /**
+        * Convert a date string to a Date
+        *
+        * @private
+        * @param {string} value
+        * @return {Date|null}
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.parseDateValue = function ( value ) {
+               var date, m;
+
+               value = String( value );
+               switch ( this.type ) {
+                       case 'date':
+                               value = value + 'T00:00:00Z';
+                               break;
+                       case 'time':
+                               value = '1970-01-01T' + value + 'Z';
+                               break;
+               }
+               m = /^(\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,3}))?Z$/.exec( value );
+               if ( m ) {
+                       if ( m[ 7 ] ) {
+                               while ( m[ 7 ].length < 3 ) {
+                                       m[ 7 ] += '0';
+                               }
+                       } else {
+                               m[ 7 ] = 0;
+                       }
+                       date = new Date();
+                       date.setUTCFullYear( m[ 1 ], m[ 2 ] - 1, m[ 3 ] );
+                       date.setUTCHours( m[ 4 ], m[ 5 ], m[ 6 ], m[ 7 ] );
+                       if ( date.getTime() < -62167219200000 || date.getTime() > 253402300799999 ||
+                               date.getUTCFullYear() !== +m[ 1 ] ||
+                               date.getUTCMonth() + 1 !== +m[ 2 ] ||
+                               date.getUTCDate() !== +m[ 3 ] ||
+                               date.getUTCHours() !== +m[ 4 ] ||
+                               date.getUTCMinutes() !== +m[ 5 ] ||
+                               date.getUTCSeconds() !== +m[ 6 ] ||
+                               date.getUTCMilliseconds() !== +m[ 7 ]
+                       ) {
+                               date = null;
+                       }
+               } else {
+                       date = null;
+               }
+
+               return date;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.cleanUpValue = function ( value ) {
+               var date, pad;
+
+               if ( value === '' ) {
+                       return '';
+               }
+
+               if ( value instanceof Date ) {
+                       date = value;
+               } else {
+                       date = this.parseDateValue( value );
+               }
+
+               if ( date instanceof Date ) {
+                       pad = function ( v, l ) {
+                               v = String( v );
+                               while ( v.length < l ) {
+                                       v = '0' + v;
+                               }
+                               return v;
+                       };
+
+                       switch ( this.type ) {
+                               case 'date':
+                                       value = pad( date.getUTCFullYear(), 4 ) +
+                                               '-' + pad( date.getUTCMonth() + 1, 2 ) +
+                                               '-' + pad( date.getUTCDate(), 2 );
+                                       break;
+
+                               case 'time':
+                                       value = pad( date.getUTCHours(), 2 ) +
+                                               ':' + pad( date.getUTCMinutes(), 2 ) +
+                                               ':' + pad( date.getUTCSeconds(), 2 ) +
+                                               '.' + pad( date.getUTCMilliseconds(), 3 );
+                                       value = value.replace( /\.?0+$/, '' );
+                                       break;
+
+                               default:
+                                       value = date.toISOString();
+                                       break;
+                       }
+               } else {
+                       value = '';
+               }
+
+               return value;
+       };
+
+       /**
+        * Get the value of the input as a Date object
+        *
+        * @return {Date|null}
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.getValueAsDate = function () {
+               return this.parseDateValue( this.getValue() );
+       };
+
+       /**
+        * Set up the UI fields
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.setupFields = function () {
+               var i, $field, spec, placeholder, sz, maxlength,
+                       spanValFunc = function ( v ) {
+                               if ( v === undefined ) {
+                                       return this.data( 'mw-widgets-datetime-dateTimeInputWidget-value' );
+                               } else {
+                                       v = String( v );
+                                       this.data( 'mw-widgets-datetime-dateTimeInputWidget-value', v );
+                                       if ( v === '' ) {
+                                               v = this.data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder' );
+                                       }
+                                       this.text( v );
+                                       return this;
+                               }
+                       },
+                       reduceFunc = function ( k, v ) {
+                               maxlength = Math.max( maxlength, v );
+                       },
+                       disabled = this.isDisabled(),
+                       specs = this.formatter.getFieldSpec();
+
+               this.$fields.empty();
+               this.clearButton = null;
+               this.fields = [];
+
+               for ( i = 0; i < specs.length; i++ ) {
+                       spec = specs[ i ];
+                       if ( typeof spec === 'string' ) {
+                               $( '<span>' )
+                                       .addClass( 'mw-widgets-datetime-dateTimeInputWidget-field' )
+                                       .text( spec )
+                                       .appendTo( this.$fields );
+                               continue;
+                       }
+
+                       placeholder = '';
+                       while ( placeholder.length < spec.size ) {
+                               placeholder += '_';
+                       }
+
+                       if ( spec.type === 'number' ) {
+                               // Numbers ''should'' be the same width. But we need some extra for
+                               // IE, apparently.
+                               sz = ( spec.size * 1.15 ) + 'ch';
+                       } else {
+                               // Add a little for padding
+                               sz = ( spec.size * 1.15 ) + 'ch';
+                       }
+                       if ( spec.editable && spec.type !== 'static' ) {
+                               if ( spec.type === 'boolean' || spec.type === 'toggleLocal' ) {
+                                       $field = $( '<span>' )
+                                               .attr( {
+                                                       tabindex: disabled ? -1 : 0
+                                               } )
+                                               .width( sz )
+                                               .data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder', placeholder );
+                                       $field.on( {
+                                               keydown: this.onFieldKeyDown.bind( this, $field ),
+                                               focus: this.onFieldFocus.bind( this, $field ),
+                                               click: this.onFieldClick.bind( this, $field ),
+                                               'wheel mousewheel DOMMouseScroll': this.onFieldWheel.bind( this, $field )
+                                       } );
+                                       $field.val = spanValFunc;
+                               } else {
+                                       maxlength = spec.size;
+                                       if ( spec.intercalarySize ) {
+                                               $.each( spec.intercalarySize, reduceFunc );
+                                       }
+                                       $field = $( '<input type="text">' )
+                                               .attr( {
+                                                       tabindex: disabled ? -1 : 0,
+                                                       size: spec.size,
+                                                       maxlength: maxlength
+                                               } )
+                                               .prop( {
+                                                       disabled: disabled,
+                                                       placeholder: placeholder
+                                               } )
+                                               .width( sz );
+                                       $field.on( {
+                                               keydown: this.onFieldKeyDown.bind( this, $field ),
+                                               click: this.onFieldClick.bind( this, $field ),
+                                               focus: this.onFieldFocus.bind( this, $field ),
+                                               blur: this.onFieldBlur.bind( this, $field ),
+                                               change: this.onFieldChange.bind( this, $field ),
+                                               'wheel mousewheel DOMMouseScroll': this.onFieldWheel.bind( this, $field )
+                                       } );
+                               }
+                               $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-editField' );
+                       } else {
+                               $field = $( '<span>' )
+                                       .width( sz )
+                                       .data( 'mw-widgets-datetime-dateTimeInputWidget-placeholder', placeholder );
+                               if ( spec.type === 'static' ) {
+                                       $field.text( spec.value );
+                               } else {
+                                       $field.val = spanValFunc;
+                               }
+                       }
+
+                       this.fields.push( $field );
+                       $field
+                               .addClass( 'mw-widgets-datetime-dateTimeInputWidget-field' )
+                               .data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec', spec )
+                               .appendTo( this.$fields );
+               }
+
+               if ( this.clearable ) {
+                       this.clearButton = new OO.ui.ButtonWidget( {
+                               classes: [ 'mw-widgets-datetime-dateTimeInputWidget-field', 'mw-widgets-datetime-dateTimeInputWidget-clearButton' ],
+                               framed: false,
+                               icon: 'remove',
+                               disabled: disabled
+                       } ).connect( this, {
+                               click: 'onClearClick'
+                       } );
+                       this.$fields.append( this.clearButton.$element );
+               }
+
+               this.updateFieldsFromValue();
+       };
+
+       /**
+        * Update the UI fields from the current value
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.updateFieldsFromValue = function () {
+               var i, $field, spec, intercalary, sz,
+                       date = this.getValueAsDate();
+
+               if ( date === null ) {
+                       this.components = null;
+
+                       for ( i = 0; i < this.fields.length; i++ ) {
+                               $field = this.fields[ i ];
+                               spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+                               $field
+                                       .removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid oo-ui-element-hidden' )
+                                       .val( '' );
+
+                               if ( spec.intercalarySize ) {
+                                       if ( spec.type === 'number' ) {
+                                               // Numbers ''should'' be the same width. But we need some extra for
+                                               // IE, apparently.
+                                               $field.width( ( spec.size * 1.15 ) + 'ch' );
+                                       } else {
+                                               // Add a little for padding
+                                               $field.width( ( spec.size * 1.15 ) + 'ch' );
+                                       }
+                               }
+                       }
+
+                       this.setFlags( { invalid: this.required } );
+               } else {
+                       this.components = this.formatter.getComponentsFromDate( date );
+                       intercalary = this.components.intercalary;
+
+                       for ( i = 0; i < this.fields.length; i++ ) {
+                               $field = this.fields[ i ];
+                               $field.removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                               spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+                               if ( spec.type !== 'static' ) {
+                                       $field.val( spec.formatValue( this.components[ spec.component ] ) );
+                               }
+                               if ( spec.intercalarySize ) {
+                                       if ( intercalary && spec.intercalarySize[ intercalary ] !== undefined ) {
+                                               sz = spec.intercalarySize[ intercalary ];
+                                       } else {
+                                               sz = spec.size;
+                                       }
+                                       $field.toggleClass( 'oo-ui-element-hidden', sz <= 0 );
+                                       if ( spec.type === 'number' ) {
+                                               // Numbers ''should'' be the same width. But we need some extra for
+                                               // IE, apparently.
+                                               this.fields[ i ].width( ( sz * 1.15 ) + 'ch' );
+                                       } else {
+                                               // Add a little for padding
+                                               this.fields[ i ].width( ( sz * 1.15 ) + 'ch' );
+                                       }
+                               }
+                       }
+
+                       this.setFlags( { invalid: date < this.min || date > this.max } );
+               }
+
+               this.$element.toggleClass( 'mw-widgets-datetime-dateTimeInputWidget-empty', date === null );
+       };
+
+       /**
+        * Update the value with data from the UI fields
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.updateValueFromFields = function () {
+               var i, v, $field, spec, curDate, newDate,
+                       components = {},
+                       anyInvalid = false,
+                       anyEmpty = false,
+                       allEmpty = true;
+
+               for ( i = 0; i < this.fields.length; i++ ) {
+                       $field = this.fields[ i ];
+                       spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+                       if ( spec.editable ) {
+                               $field.removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                               v = $field.val();
+                               if ( v === '' ) {
+                                       $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                                       anyEmpty = true;
+                               } else {
+                                       allEmpty = false;
+                                       v = spec.parseValue( v );
+                                       if ( v === undefined ) {
+                                               $field.addClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                                               anyInvalid = true;
+                                       } else {
+                                               components[ spec.component ] = v;
+                                       }
+                               }
+                       }
+               }
+
+               if ( allEmpty ) {
+                       for ( i = 0; i < this.fields.length; i++ ) {
+                               this.fields[ i ].removeClass( 'mw-widgets-datetime-dateTimeInputWidget-invalid' );
+                       }
+               } else if ( anyEmpty ) {
+                       anyInvalid = true;
+               }
+
+               if ( !anyInvalid ) {
+                       curDate = this.getValueAsDate();
+                       newDate = this.formatter.getDateFromComponents( components );
+                       if ( !curDate || !newDate || curDate.getTime() !== newDate.getTime() ) {
+                               this.setValue( newDate );
+                       }
+               }
+       };
+
+       /**
+        * Handle change event
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onChange = function () {
+               var date;
+
+               this.updateFieldsFromValue();
+
+               if ( this.calendar ) {
+                       date = this.getValueAsDate();
+                       this.calendar.setSelected( date );
+                       if ( date ) {
+                               this.calendar.setFocusedDate( date );
+                       }
+               }
+       };
+
+       /**
+        * Handle clear button click event
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onClearClick = function () {
+               this.blur();
+               this.setValue( '' );
+       };
+
+       /**
+        * Handle click on the widget background
+        *
+        * @private
+        * @param {jQuery.Event} e Click event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onHandleClick = function () {
+               this.focus();
+       };
+
+       /**
+        * Handle key down events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Key down event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldKeyDown = function ( $field, e ) {
+               var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               if ( !this.isDisabled() ) {
+                       switch ( e.which ) {
+                               case OO.ui.Keys.ENTER:
+                               case OO.ui.Keys.SPACE:
+                                       if ( spec.type === 'boolean' ) {
+                                               this.setValue(
+                                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component, 1, 'wrap' )
+                                               );
+                                               return false;
+                                       } else if ( spec.type === 'toggleLocal' ) {
+                                               this.formatter.toggleLocal();
+                                       }
+                                       break;
+
+                               case OO.ui.Keys.UP:
+                               case OO.ui.Keys.DOWN:
+                                       if ( spec.type === 'toggleLocal' ) {
+                                               this.formatter.toggleLocal();
+                                       } else {
+                                               this.setValue(
+                                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component,
+                                                               e.keyCode === OO.ui.Keys.UP ? -1 : 1, 'wrap' )
+                                               );
+                                       }
+                                       if ( $field.is( ':input' ) ) {
+                                               $field.select();
+                                       }
+                                       return false;
+                       }
+               }
+       };
+
+       /**
+        * Handle focus events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Focus event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldFocus = function ( $field ) {
+               if ( !this.isDisabled() ) {
+                       if ( this.getValueAsDate() === null ) {
+                               this.setValue( this.formatter.getDefaultDate() );
+                       }
+                       if ( $field.is( ':input' ) ) {
+                               $field.select();
+                       }
+
+                       if ( this.calendar ) {
+                               this.calendar.toggle( true );
+                       }
+               }
+       };
+
+       /**
+        * Handle click events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Click event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldClick = function ( $field ) {
+               var spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               if ( !this.isDisabled() ) {
+                       if ( spec.type === 'boolean' ) {
+                               this.setValue(
+                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component, 1, 'wrap' )
+                               );
+                       } else if ( spec.type === 'toggleLocal' ) {
+                               this.formatter.toggleLocal();
+                       }
+               }
+       };
+
+       /**
+        * Handle blur events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Blur event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldBlur = function ( $field ) {
+               var v, date,
+                       spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               this.updateValueFromFields();
+
+               // Normalize
+               date = this.getValueAsDate();
+               if ( !date ) {
+                       $field.val( '' );
+               } else {
+                       v = spec.formatValue( this.formatter.getComponentsFromDate( date )[ spec.component ] );
+                       if ( v !== $field.val() ) {
+                               $field.val( v );
+                       }
+               }
+       };
+
+       /**
+        * Handle change events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Change event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldChange = function () {
+               this.updateValueFromFields();
+       };
+
+       /**
+        * Handle wheel events on our field inputs.
+        *
+        * @private
+        * @param {jQuery} $field
+        * @param {jQuery.Event} e Change event
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onFieldWheel = function ( $field, e ) {
+               var delta = 0,
+                       spec = $field.data( 'mw-widgets-datetime-dateTimeInputWidget-fieldSpec' );
+
+               if ( this.isDisabled() ) {
+                       return;
+               }
+
+               // Standard 'wheel' event
+               if ( e.originalEvent.deltaMode !== undefined ) {
+                       this.sawWheelEvent = true;
+               }
+               if ( e.originalEvent.deltaY ) {
+                       delta = -e.originalEvent.deltaY;
+               } else if ( e.originalEvent.deltaX ) {
+                       delta = e.originalEvent.deltaX;
+               }
+
+               // Non-standard events
+               if ( !this.sawWheelEvent ) {
+                       if ( e.originalEvent.wheelDeltaX ) {
+                               delta = -e.originalEvent.wheelDeltaX;
+                       } else if ( e.originalEvent.wheelDeltaY ) {
+                               delta = e.originalEvent.wheelDeltaY;
+                       } else if ( e.originalEvent.wheelDelta ) {
+                               delta = e.originalEvent.wheelDelta;
+                       } else if ( e.originalEvent.detail ) {
+                               delta = -e.originalEvent.detail;
+                       }
+               }
+
+               if ( delta && spec ) {
+                       if ( spec.type === 'toggleLocal' ) {
+                               this.formatter.toggleLocal();
+                       } else {
+                               this.setValue(
+                                       this.formatter.adjustComponent( this.getValueAsDate(), spec.component, delta < 0 ? -1 : 1, 'wrap' )
+                               );
+                       }
+                       return false;
+               }
+       };
+
+       /**
+        * Handle calendar change event
+        *
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.onCalendarChange = function () {
+               var curDate = this.getValueAsDate(),
+                       newDate = this.calendar.getSelected()[ 0 ];
+
+               if ( newDate ) {
+                       if ( !curDate || newDate.getTime() !== curDate.getTime() ) {
+                               this.setValue( newDate );
+                       }
+               }
+       };
+
+       /**
+        * @inheritdoc
+        * @private
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.getInputElement = function () {
+               return $( '<input type="hidden" />' );
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.setDisabled = function ( disabled ) {
+               mw.widgets.datetime.DateTimeInputWidget[ 'super' ].prototype.setDisabled.call( this, disabled );
+
+               // Flag all our fields as disabled
+               if ( this.$fields ) {
+                       this.$fields.find( 'input' ).prop( 'disabled', this.isDisabled() );
+                       this.$fields.find( '[tabindex]' ).attr( 'tabindex', this.isDisabled() ? -1 : 0 );
+               }
+
+               if ( this.clearButton ) {
+                       this.clearButton.setDisabled( disabled );
+               }
+
+               return this;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.focus = function () {
+               if ( !this.$fields.find( document.activeElement ).length ) {
+                       this.$fields.find( '.mw-widgets-datetime-dateTimeInputWidget-editField' ).first().focus();
+               }
+               return this;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.blur = function () {
+               this.$fields.find( document.activeElement ).blur();
+               return this;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DateTimeInputWidget.prototype.simulateLabelClick = function () {
+               this.focus();
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less b/resources/src/mediawiki.widgets.datetime/DateTimeInputWidget.less
new file mode 100644 (file)
index 0000000..bc387df
--- /dev/null
@@ -0,0 +1,155 @@
+@import "mediawiki.widgets.datetime.definitions";
+
+.mw-widgets-datetime-dateTimeInputWidget {
+       display: inline-block;
+       position: relative;
+       vertical-align: middle;
+
+       &-fields {
+               position: relative;
+               display: table;
+               z-index: 2;
+               .oo-ui-unselectable();
+
+               > .mw-widgets-datetime-dateTimeInputWidget-field {
+                       .oo-ui-box-sizing(border-box);
+
+                       display: table-cell;
+                       white-space: pre;
+               }
+       }
+
+       &-handle {
+               width: 100%;
+               display: inline-block;
+               overflow: hidden;
+
+               // Needed for proper behavior with overflow: hidden.
+               vertical-align: bottom;
+
+               .oo-ui-unselectable();
+               .oo-ui-box-sizing(border-box);
+
+               > .oo-ui-indicatorElement-indicator,
+               > .oo-ui-iconElement-icon {
+                       position: absolute;
+                       background-position: center center;
+                       background-repeat: no-repeat;
+                       z-index: 1;
+               }
+       }
+
+       margin: 0.25em 0;
+       width: 100%;
+       max-width: 50em;
+
+       .oo-ui-inline-spacing(0.5em);
+
+       &-handle {
+               height: 2.5em;
+               border: 1px solid #ccc;
+               padding: 0 1em;
+               margin: 0;
+               background-color: #fff;
+               color: black;
+               border: solid 1px #ccc;
+               box-shadow: inset 0 0 0 0 @progressive;
+               border-radius: 0.1em;
+               .oo-ui-transition(box-shadow @quick-ease);
+               .oo-ui-box-sizing(border-box);
+
+               > .oo-ui-indicatorElement-indicator {
+                       right: 0;
+               }
+
+               > .oo-ui-iconElement-icon {
+                       left: 0.25em;
+               }
+
+               > .oo-ui-indicatorElement-indicator {
+                       top: 0;
+                       width: @indicator-size;
+                       height: @indicator-size;
+                       margin: 0.775em;
+               }
+
+               > .oo-ui-iconElement-icon {
+                       top: 0;
+                       width: @icon-size;
+                       height: @icon-size;
+                       margin: 0.3em;
+               }
+       }
+
+       &-empty &-handle {
+               color: #777;
+       }
+
+       &-field {
+               padding: 0;
+               margin: 0;
+               font-size: inherit;
+               font-family: inherit;
+               background-color: transparent;
+               color: inherit;
+               border: none;
+               box-shadow: none;
+               text-align: center;
+               vertical-align: middle;
+               .oo-ui-box-sizing(border-box);
+       }
+
+       &.oo-ui-widget-disabled {
+               .mw-widgets-datetime-dateTimeInputWidget-handle {
+                       color: #ccc;
+                       text-shadow: 0 1px 1px #fff;
+                       border-color: #ddd;
+                       background-color: #f3f3f3;
+
+                       > .oo-ui-iconElement-icon,
+                       > .oo-ui-indicatorElement-indicator {
+                               opacity: 0.2;
+                       }
+               }
+       }
+
+       &.oo-ui-widget-enabled {
+               .mw-widgets-datetime-dateTimeInputWidget-editField:hover {
+                       background-color: #eee;
+               }
+
+               &.oo-ui-flaggedElement-invalid {
+                       .mw-widgets-datetime-dateTimeInputWidget-handle {
+                               border-color: red;
+                               box-shadow: inset 0 0 0 0 red;
+                       }
+
+                       .mw-widgets-datetime-dateTimeInputWidget-handle:focus {
+                               border-color: red;
+                               box-shadow: inset 0 0 0 0.1em red;
+                       }
+               }
+       }
+
+       input.mw-widgets-datetime-dateTimeInputWidget-field {
+               padding: 0.5em 0;
+       }
+
+       &-editField.mw-widgets-datetime-dateTimeInputWidget-invalid {
+               border: 1px solid red;
+               box-shadow: inset 0 0 0 0 red;
+
+               &:focus {
+                       border: 1px solid red;
+                       box-shadow: inset 0 0 0 0.1em red;
+               }
+       }
+
+       &.oo-ui-iconElement .mw-widgets-datetime-dateTimeInputWidget-handle {
+               padding-left: 3em;
+       }
+
+       &.oo-ui-indicatorElement .mw-widgets-datetime-dateTimeInputWidget-handle {
+               padding-right: 2em;
+       }
+}
diff --git a/resources/src/mediawiki.widgets.datetime/DiscordianDateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/DiscordianDateTimeFormatter.js
new file mode 100644 (file)
index 0000000..fbf3238
--- /dev/null
@@ -0,0 +1,562 @@
+( function ( $, mw ) {
+
+       /**
+        * Provides various methods needed for formatting dates and times. This
+        * implementation implments the [Discordian calendar][1], mainly for testing with
+        * something very different from the usual Gregorian calendar.
+        *
+        * Being intended mainly for testing, niceties like i18n and better
+        * configurability have been omitted.
+        *
+        * [1]: https://en.wikipedia.org/wiki/Discordian_calendar
+        *
+        * @class
+        * @extends mw.widgets.datetime.DateTimeFormatter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter = function MwWidgetsDatetimeDiscordianDateTimeFormatter( config ) {
+               config = $.extend( {}, config );
+
+               // Parent constructor
+               mw.widgets.datetime.DiscordianDateTimeFormatter[ 'super' ].call( this, config );
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.DiscordianDateTimeFormatter, mw.widgets.datetime.DateTimeFormatter );
+
+       /* Static */
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter[ 'static' ].formats = {
+               '@time': '${hour|0}:${minute|0}:${second|0}',
+               '@date': '$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#}',
+               '@datetime': '$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}',
+               '@default': '$!{dow|full}${not-intercalary|1|, }${season|full}${not-intercalary|1| }${day|#}, ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
+       };
+
+       /* Methods */
+
+       /**
+        * @inheritdoc
+        *
+        * Additional fields implemented here are:
+        * - ${year|#}: Year as a number
+        * - ${season|#}: Season as a number
+        * - ${season|full}: Season as a string
+        * - ${day|#}: Day of the month as a number
+        * - ${day|0}: Day of the month as a number with leading 0
+        * - ${dow|full}: Day of the week as a string
+        * - ${hour|#}: Hour as a number
+        * - ${hour|0}: Hour as a number with leading 0
+        * - ${minute|#}: Minute as a number
+        * - ${minute|0}: Minute as a number with leading 0
+        * - ${second|#}: Second as a number
+        * - ${second|0}: Second as a number with leading 0
+        * - ${millisecond|#}: Millisecond as a number
+        * - ${millisecond|0}: Millisecond as a number, zero-padded to 3 digits
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+               var spec = null;
+
+               switch ( tag + '|' + params[ 0 ] ) {
+                       case 'year|#':
+                               spec = {
+                                       component: 'Year',
+                                       type: 'number',
+                                       size: 4,
+                                       zeropad: false
+                               };
+                               break;
+
+                       case 'season|#':
+                               spec = {
+                                       component: 'Season',
+                                       type: 'number',
+                                       size: 1,
+                                       intercalarySize: { 1: 0 },
+                                       zeropad: false
+                               };
+                               break;
+
+                       case 'season|full':
+                               spec = {
+                                       component: 'Season',
+                                       type: 'string',
+                                       intercalarySize: { 1: 0 },
+                                       values: {
+                                               1: 'Chaos',
+                                               2: 'Discord',
+                                               3: 'Confusion',
+                                               4: 'Bureaucracy',
+                                               5: 'The Aftermath'
+                                       }
+                               };
+                               break;
+
+                       case 'dow|full':
+                               spec = {
+                                       component: 'DOW',
+                                       editable: false,
+                                       type: 'string',
+                                       intercalarySize: { 1: 0 },
+                                       values: {
+                                               '-1': 'N/A',
+                                               0: 'Sweetmorn',
+                                               1: 'Boomtime',
+                                               2: 'Pungenday',
+                                               3: 'Prickle-Prickle',
+                                               4: 'Setting Orange'
+                                       }
+                               };
+                               break;
+
+                       case 'day|#':
+                       case 'day|0':
+                               spec = {
+                                       component: 'Day',
+                                       type: 'string',
+                                       size: 2,
+                                       intercalarySize: { 1: 13 },
+                                       zeropad: params[ 0 ] === '0',
+                                       formatValue: function ( v ) {
+                                               if ( v === 'tib' ) {
+                                                       return 'St. Tib\'s Day';
+                                               }
+                                               return mw.widgets.datetime.DateTimeFormatter.prototype.formatSpecValue.call( this, v );
+                                       },
+                                       parseValue: function ( v ) {
+                                               if ( /^\s*(st.?\s*)?tib('?s)?(\s*day)?\s*$/i.test( v ) ) {
+                                                       return 'tib';
+                                               }
+                                               return mw.widgets.datetime.DateTimeFormatter.prototype.parseSpecValue.call( this, v );
+                                       }
+                               };
+                               break;
+
+                       case 'hour|#':
+                       case 'hour|0':
+                       case 'minute|#':
+                       case 'minute|0':
+                       case 'second|#':
+                       case 'second|0':
+                               spec = {
+                                       component: tag.charAt( 0 ).toUpperCase() + tag.slice( 1 ),
+                                       type: 'number',
+                                       size: 2,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       case 'millisecond|#':
+                       case 'millisecond|0':
+                               spec = {
+                                       component: 'Millisecond',
+                                       type: 'number',
+                                       size: 3,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       default:
+                               return mw.widgets.datetime.DiscordianDateTimeFormatter[ 'super' ].prototype.getFieldForTag.call( this, tag, params );
+               }
+
+               if ( spec ) {
+                       if ( spec.editable === undefined ) {
+                               spec.editable = true;
+                       }
+                       if ( spec.component !== 'Day' ) {
+                               spec.formatValue = this.formatSpecValue;
+                               spec.parseValue = this.parseSpecValue;
+                       }
+                       if ( spec.values ) {
+                               spec.size = Math.max.apply(
+                                       null, $.map( spec.values, function ( v ) { return v.length; } )
+                               );
+                       }
+               }
+
+               return spec;
+       };
+
+       /**
+        * Get components from a Date object
+        *
+        * Components are:
+        * - Year {number}
+        * - Season {number} 1-5
+        * - Day {number|string} 1-73 or 'tib'
+        * - DOW {number} 0-4, or -1 on St. Tib's Day
+        * - Hour {number} 0-23
+        * - Minute {number} 0-59
+        * - Second {number} 0-59
+        * - Millisecond {number} 0-999
+        * - intercalary {string} '1' on St. Tib's Day
+        *
+        * @param {Date|null} date
+        * @return {Object} Components
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+               var ret, day, month,
+                       monthDays = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 ];
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               if ( this.local ) {
+                       day = date.getDate();
+                       month = date.getMonth();
+                       ret = {
+                               Year: date.getFullYear() + 1166,
+                               Hour: date.getHours(),
+                               Minute: date.getMinutes(),
+                               Second: date.getSeconds(),
+                               Millisecond: date.getMilliseconds(),
+                               zone: date.getTimezoneOffset()
+                       };
+               } else {
+                       day = date.getUTCDate();
+                       month = date.getUTCMonth();
+                       ret = {
+                               Year: date.getUTCFullYear() + 1166,
+                               Hour: date.getUTCHours(),
+                               Minute: date.getUTCMinutes(),
+                               Second: date.getUTCSeconds(),
+                               Millisecond: date.getUTCMilliseconds(),
+                               zone: 0
+                       };
+               }
+
+               if ( month === 1 && day === 29 ) {
+                       ret.Season = 1;
+                       ret.Day = 'tib';
+                       ret.DOW = -1;
+                       ret.intercalary = '1';
+               } else {
+                       day = monthDays[ month ] + day - 1;
+                       ret.Season = Math.floor( day / 73 ) + 1;
+                       ret.Day = ( day % 73 ) + 1;
+                       ret.DOW = day % 5;
+                       ret.intercalary = '';
+               }
+
+               return ret;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.adjustComponent = function ( date, component, delta, mode ) {
+               return this.getDateFromComponents(
+                       this.adjustComponentInternal(
+                               this.getComponentsFromDate( date ), component, delta, mode
+                       )
+               );
+       };
+
+       /**
+        * Adjust the components directly
+        *
+        * @private
+        * @param {Object} components Modified in place
+        * @param {string} component
+        * @param {number} delta
+        * @param {string} mode
+        * @return {Object} components
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.adjustComponentInternal = function ( components, component, delta, mode ) {
+               var i, min, max, range, next, preTib, postTib, wasTib;
+
+               if ( delta === 0 ) {
+                       return components;
+               }
+
+               switch ( component ) {
+                       case 'Year':
+                               min = 1166;
+                               max = 11165;
+                               next = null;
+                               break;
+                       case 'Season':
+                               min = 1;
+                               max = 5;
+                               next = 'Year';
+                               break;
+                       case 'Week':
+                               if ( components.Day === 'tib' ) {
+                                       components.Day = 59; // Could choose either one...
+                                       components.Season = 1;
+                               }
+                               min = 1;
+                               max = 73;
+                               next = 'Season';
+                               break;
+                       case 'Day':
+                               min = 1;
+                               max = 73;
+                               next = 'Season';
+                               break;
+                       case 'Hour':
+                               min = 0;
+                               max = 23;
+                               next = 'Day';
+                               break;
+                       case 'Minute':
+                               min = 0;
+                               max = 59;
+                               next = 'Hour';
+                               break;
+                       case 'Second':
+                               min = 0;
+                               max = 59;
+                               next = 'Minute';
+                               break;
+                       case 'Millisecond':
+                               min = 0;
+                               max = 999;
+                               next = 'Second';
+                               break;
+                       default:
+                               return components;
+               }
+
+               switch ( mode ) {
+                       case 'overflow':
+                       case 'clip':
+                       case 'wrap':
+               }
+
+               if ( component === 'Day' ) {
+                       i = Math.abs( delta );
+                       delta = delta < 0 ? -1 : 1;
+                       preTib = delta > 0 ? 59 : 60;
+                       postTib = delta > 0 ? 60 : 59;
+                       while ( i-- > 0 ) {
+                               if ( components.Day === preTib && components.Season === 1 && this.isLeapYear( components.Year ) ) {
+                                       components.Day = 'tib';
+                               } else if ( components.Day === 'tib' ) {
+                                       components.Day = postTib;
+                                       components.Season = 1;
+                               } else {
+                                       components.Day += delta;
+                                       if ( components.Day < min ) {
+                                               switch ( mode ) {
+                                                       case 'overflow':
+                                                               components.Day = max;
+                                                               this.adjustComponentInternal( components, 'Season', -1, mode );
+                                                               break;
+                                                       case 'wrap':
+                                                               components.Day = max;
+                                                               break;
+                                                       case 'clip':
+                                                               components.Day = min;
+                                                               i = 0;
+                                                               break;
+                                               }
+                                       }
+                                       if ( components.Day > max ) {
+                                               switch ( mode ) {
+                                                       case 'overflow':
+                                                               components.Day = min;
+                                                               this.adjustComponentInternal( components, 'Season', 1, mode );
+                                                               break;
+                                                       case 'wrap':
+                                                               components.Day = min;
+                                                               break;
+                                                       case 'clip':
+                                                               components.Day = max;
+                                                               i = 0;
+                                                               break;
+                                               }
+                                       }
+                               }
+                       }
+               } else {
+                       if ( component === 'Week' ) {
+                               component = 'Day';
+                               delta *= 5;
+                       }
+                       if ( components.Day === 'tib' ) {
+                               // For sanity
+                               components.Season = 1;
+                       }
+                       switch ( mode ) {
+                               case 'overflow':
+                                       if ( components.Day === 'tib' && ( component === 'Season' || component === 'Year' ) ) {
+                                               components.Day = 59; // Could choose either one...
+                                               wasTib = true;
+                                       } else {
+                                               wasTib = false;
+                                       }
+                                       i = Math.abs( delta );
+                                       delta = delta < 0 ? -1 : 1;
+                                       while ( i-- > 0 ) {
+                                               components[ component ] += delta;
+                                               if ( components[ component ] < min ) {
+                                                       components[ component ] = max;
+                                                       components = this.adjustComponentInternal( components, next, -1, mode );
+                                               }
+                                               if ( components[ component ] > max ) {
+                                                       components[ component ] = min;
+                                                       components = this.adjustComponentInternal( components, next, 1, mode );
+                                               }
+                                       }
+                                       if ( wasTib && components.Season === 1 && this.isLeapYear( components.Year ) ) {
+                                               components.Day = 'tib';
+                                       }
+                                       break;
+                               case 'wrap':
+                                       range = max - min + 1;
+                                       components[ component ] += delta;
+                                       while ( components[ component ] < min ) {
+                                               components[ component ] += range;
+                                       }
+                                       while ( components[ component ] > max ) {
+                                               components[ component ] -= range;
+                                       }
+                                       break;
+                               case 'clip':
+                                       components[ component ] += delta;
+                                       if ( components[ component ] < min ) {
+                                               components[ component ] = min;
+                                       }
+                                       if ( components[ component ] > max ) {
+                                               components[ component ] = max;
+                                       }
+                                       break;
+                       }
+                       if ( components.Day === 'tib' &&
+                               ( components.Season !== 1 || !this.isLeapYear( components.Year ) )
+                       ) {
+                               components.Day = 59; // Could choose either one...
+                       }
+               }
+
+               return components;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getDateFromComponents = function ( components ) {
+               var month, day, days,
+                       date = new Date(),
+                       monthDays = [ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 ];
+
+               components = $.extend( {}, this.getComponentsFromDate( null ), components );
+               if ( components.Day === 'tib' ) {
+                       month = 1;
+                       day = 29;
+               } else {
+                       days = components.Season * 73 + components.Day - 74;
+                       month = 0;
+                       while ( days >= monthDays[ month + 1 ] ) {
+                               month++;
+                       }
+                       day = days - monthDays[ month ] + 1;
+               }
+
+               if ( components.zone ) {
+                       // Can't just use the constructor because that's stupid about ancient years.
+                       date.setFullYear( components.Year - 1166, month, day );
+                       date.setHours( components.Hour, components.Minute, components.Second, components.Millisecond );
+               } else {
+                       // Date.UTC() is stupid about ancient years too.
+                       date.setUTCFullYear( components.Year - 1166, month, day );
+                       date.setUTCHours( components.Hour, components.Minute, components.Second, components.Millisecond );
+               }
+
+               return date;
+       };
+
+       /**
+        * Get whether the year is a leap year
+        *
+        * @private
+        * @param {number} year
+        * @return {boolean}
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.isLeapYear = function ( year ) {
+               year -= 1166;
+               if ( year % 4 ) {
+                       return false;
+               } else if ( year % 100 ) {
+                       return true;
+               }
+               return ( year % 400 ) === 0;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getCalendarHeadings = function () {
+               return [ 'SM', 'BT', 'PD', 'PP', null, 'SO' ];
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+               var components1 = this.getComponentsFromDate( date1 ),
+                       components2 = this.getComponentsFromDate( date2 );
+
+               return components1.Year === components2.Year && components1.Season === components2.Season;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.DiscordianDateTimeFormatter.prototype.getCalendarData = function ( date ) {
+               var dt, components, season, i, row,
+                       ret = {
+                               dayComponent: 'Day',
+                               weekComponent: 'Week',
+                               monthComponent: 'Season'
+                       },
+                       seasons = [ 'Chaos', 'Discord', 'Confusion', 'Bureaucracy', 'The Aftermath' ],
+                       seasonStart = [ 0, -3, -1, -4, -2 ];
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               components = this.getComponentsFromDate( date );
+               components.Day = 1;
+               season = components.Season;
+
+               ret.header = seasons[ season - 1 ] + ' ' + components.Year;
+
+               if ( seasonStart[ season - 1 ] ) {
+                       this.adjustComponentInternal( components, 'Day', seasonStart[ season - 1 ], 'overflow' );
+               }
+
+               ret.rows = [];
+               do {
+                       row = [];
+                       for ( i = 0; i < 6; i++ ) {
+                               dt = this.getDateFromComponents( components );
+                               row[ i ] = {
+                                       display: components.Day === 'tib' ? 'Tib' : String( components.Day ),
+                                       date: dt,
+                                       extra: components.Season < season ? 'prev' : components.Season > season ? 'next' : null
+                               };
+
+                               this.adjustComponentInternal( components, 'Day', 1, 'overflow' );
+                               if ( components.Day !== 'tib' && i === 3 ) {
+                                       row[ ++i ] = null;
+                               }
+                       }
+
+                       ret.rows.push( row );
+               } while ( components.Season === season );
+
+               return ret;
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js b/resources/src/mediawiki.widgets.datetime/ProlepticGregorianDateTimeFormatter.js
new file mode 100644 (file)
index 0000000..f60b34b
--- /dev/null
@@ -0,0 +1,661 @@
+( function ( $, mw ) {
+
+       /**
+        * Provides various methods needed for formatting dates and times. This
+        * implementation implments the proleptic Gregorian calendar over years
+        * 0000–9999.
+        *
+        * @class
+        * @extends mw.widgets.datetime.DateTimeFormatter
+        *
+        * @constructor
+        * @param {Object} [config] Configuration options
+        * @cfg {Object} [fullMonthNames] Mapping 1–12 to full month names.
+        * @cfg {Object} [shortMonthNames] Mapping 1–12 to abbreviated month names.
+        *  If {@link #fullMonthNames fullMonthNames} is given and this is not,
+        *  defaults to the first three characters from that setting.
+        * @cfg {Object} [fullDayNames] Mapping 0–6 to full day of week names. 0 is Sunday, 6 is Saturday.
+        * @cfg {Object} [shortDayNames] Mapping 0–6 to abbreviated day of week names. 0 is Sunday, 6 is Saturday.
+        *  If {@link #fullDayNames fullDayNames} is given and this is not, defaults to
+        *  the first three characters from that setting.
+        * @cfg {string[]} [dayLetters] Weekday column headers for a calendar. Array of 7 strings.
+        *  If {@link #fullDayNames fullDayNames} or {@link #shortDayNames shortDayNames}
+        *  are given and this is not, defaults to the first character from
+        *  shortDayNames.
+        * @cfg {string[]} [hour12Periods] AM and PM texts. Array of 2 strings, AM and PM.
+        * @cfg {number} [weekStartsOn=0] What day the week starts on: 0 is Sunday, 1 is Monday, 6 is Saturday.
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter = function MwWidgetsDatetimeProlepticGregorianDateTimeFormatter( config ) {
+               var statick = this.constructor[ 'static' ];
+
+               statick.setupDefaults();
+
+               config = $.extend( {
+                       weekStartsOn: 0,
+                       hour12Periods: statick.hour12Periods
+               }, config );
+
+               if ( config.fullMonthNames && !config.shortMonthNames ) {
+                       config.shortMonthNames = {};
+                       $.each( config.fullMonthNames, function ( k, v ) {
+                               config.shortMonthNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+               if ( config.shortDayNames && !config.dayLetters ) {
+                       config.dayLetters = [];
+                       $.each( config.shortDayNames, function ( k, v ) {
+                               config.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( config.fullDayNames && !config.dayLetters ) {
+                       config.dayLetters = [];
+                       $.each( config.fullDayNames, function ( k, v ) {
+                               config.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( config.fullDayNames && !config.shortDayNames ) {
+                       config.shortDayNames = {};
+                       $.each( config.fullDayNames, function ( k, v ) {
+                               config.shortDayNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+               config = $.extend( {
+                       fullMonthNames: statick.fullMonthNames,
+                       shortMonthNames: statick.shortMonthNames,
+                       fullDayNames: statick.fullDayNames,
+                       shortDayNames: statick.shortDayNames,
+                       dayLetters: statick.dayLetters
+               }, config );
+
+               // Parent constructor
+               mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'super' ].call( this, config );
+
+               // Properties
+               this.weekStartsOn = config.weekStartsOn % 7;
+               this.fullMonthNames = config.fullMonthNames;
+               this.shortMonthNames = config.shortMonthNames;
+               this.fullDayNames = config.fullDayNames;
+               this.shortDayNames = config.shortDayNames;
+               this.dayLetters = config.dayLetters;
+               this.hour12Periods = config.hour12Periods;
+       };
+
+       /* Setup */
+
+       OO.inheritClass( mw.widgets.datetime.ProlepticGregorianDateTimeFormatter, mw.widgets.datetime.DateTimeFormatter );
+
+       /* Static */
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].formats = {
+               '@time': '${hour|0}:${minute|0}:${second|0}',
+               '@date': '$!{dow|short} ${day|#} ${month|short} ${year|#}',
+               '@datetime': '$!{dow|short} ${day|#} ${month|short} ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}',
+               '@default': '$!{dow|short} ${day|#} ${month|short} ${year|#} ${hour|0}:${minute|0}:${second|0} $!{zone|short}'
+       };
+
+       /**
+        * Default full month names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].fullMonthNames = null;
+
+       /**
+        * Default abbreviated month names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].shortMonthNames = null;
+
+       /**
+        * Default full day of week names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].fullDayNames = null;
+
+       /**
+        * Default abbreviated day of week names.
+        *
+        * @static
+        * @inheritable
+        * @property {Object}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].shortDayNames = null;
+
+       /**
+        * Default day letters.
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].dayLetters = null;
+
+       /**
+        * Default AM/PM indicators
+        *
+        * @static
+        * @inheritable
+        * @property {string[]}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].hour12Periods = null;
+
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'static' ].setupDefaults = function () {
+               mw.widgets.datetime.DateTimeFormatter[ 'static' ].setupDefaults.call( this );
+
+               if ( this.fullMonthNames && !this.shortMonthNames ) {
+                       this.shortMonthNames = {};
+                       $.each( this.fullMonthNames, function ( k, v ) {
+                               this.shortMonthNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+               if ( this.shortDayNames && !this.dayLetters ) {
+                       this.dayLetters = [];
+                       $.each( this.shortDayNames, function ( k, v ) {
+                               this.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( this.fullDayNames && !this.dayLetters ) {
+                       this.dayLetters = [];
+                       $.each( this.fullDayNames, function ( k, v ) {
+                               this.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+               if ( this.fullDayNames && !this.shortDayNames ) {
+                       this.shortDayNames = {};
+                       $.each( this.fullDayNames, function ( k, v ) {
+                               this.shortDayNames[ k ] = v.substr( 0, 3 );
+                       }.bind( this ) );
+               }
+
+               if ( !this.fullMonthNames ) {
+                       this.fullMonthNames = {
+                               1: mw.msg( 'january' ),
+                               2: mw.msg( 'february' ),
+                               3: mw.msg( 'march' ),
+                               4: mw.msg( 'april' ),
+                               5: mw.msg( 'may_long' ),
+                               6: mw.msg( 'june' ),
+                               7: mw.msg( 'july' ),
+                               8: mw.msg( 'august' ),
+                               9: mw.msg( 'september' ),
+                               10: mw.msg( 'october' ),
+                               11: mw.msg( 'november' ),
+                               12: mw.msg( 'december' )
+                       };
+               }
+               if ( !this.shortMonthNames ) {
+                       this.shortMonthNames = {
+                               1: mw.msg( 'jan' ),
+                               2: mw.msg( 'feb' ),
+                               3: mw.msg( 'mar' ),
+                               4: mw.msg( 'apr' ),
+                               5: mw.msg( 'may' ),
+                               6: mw.msg( 'jun' ),
+                               7: mw.msg( 'jul' ),
+                               8: mw.msg( 'aug' ),
+                               9: mw.msg( 'sep' ),
+                               10: mw.msg( 'oct' ),
+                               11: mw.msg( 'nov' ),
+                               12: mw.msg( 'dec' )
+                       };
+               }
+
+               if ( !this.fullDayNames ) {
+                       this.fullDayNames = {
+                               0: mw.msg( 'sunday' ),
+                               1: mw.msg( 'monday' ),
+                               2: mw.msg( 'tuesday' ),
+                               3: mw.msg( 'wednesday' ),
+                               4: mw.msg( 'thursday' ),
+                               5: mw.msg( 'friday' ),
+                               6: mw.msg( 'saturday' )
+                       };
+               }
+               if ( !this.shortDayNames ) {
+                       this.shortDayNames = {
+                               0: mw.msg( 'sun' ),
+                               1: mw.msg( 'mon' ),
+                               2: mw.msg( 'tue' ),
+                               3: mw.msg( 'wed' ),
+                               4: mw.msg( 'thu' ),
+                               5: mw.msg( 'fri' ),
+                               6: mw.msg( 'sat' )
+                       };
+               }
+               if ( !this.dayLetters ) {
+                       this.dayLetters = [];
+                       $.each( this.shortDayNames, function ( k, v ) {
+                               this.dayLetters[ k ] = v.substr( 0, 1 );
+                       }.bind( this ) );
+               }
+
+               if ( !this.hour12Periods ) {
+                       this.hour12Periods = [
+                               mw.msg( 'period-am' ),
+                               mw.msg( 'period-pm' )
+                       ];
+               }
+       };
+
+       /* Methods */
+
+       /**
+        * @inheritdoc
+        *
+        * Additional fields implemented here are:
+        * - ${year|#}: Year as a number
+        * - ${year|0}: Year as a number, zero-padded to 4 digits
+        * - ${month|#}: Month as a number
+        * - ${month|0}: Month as a number with leading 0
+        * - ${month|short}: Month from 'shortMonthNames' configuration setting
+        * - ${month|full}: Month from 'fullMonthNames' configuration setting
+        * - ${day|#}: Day of the month as a number
+        * - ${day|0}: Day of the month as a number with leading 0
+        * - ${dow|short}: Day of the week from 'shortDayNames' configuration setting
+        * - ${dow|full}: Day of the week from 'fullDayNames' configuration setting
+        * - ${hour|#}: Hour as a number
+        * - ${hour|0}: Hour as a number with leading 0
+        * - ${hour|12}: Hour in a 12-hour clock as a number
+        * - ${hour|012}: Hour in a 12-hour clock as a number, with leading 0
+        * - ${hour|period}: Value from 'hour12Periods' configuration setting
+        * - ${minute|#}: Minute as a number
+        * - ${minute|0}: Minute as a number with leading 0
+        * - ${second|#}: Second as a number
+        * - ${second|0}: Second as a number with leading 0
+        * - ${millisecond|#}: Millisecond as a number
+        * - ${millisecond|0}: Millisecond as a number, zero-padded to 3 digits
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getFieldForTag = function ( tag, params ) {
+               var spec = null;
+
+               switch ( tag + '|' + params[ 0 ] ) {
+                       case 'year|#':
+                       case 'year|0':
+                               spec = {
+                                       component: 'year',
+                                       type: 'number',
+                                       size: 4,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       case 'month|short':
+                       case 'month|full':
+                               spec = {
+                                       component: 'month',
+                                       type: 'string',
+                                       values: params[ 0 ] === 'short' ? this.shortMonthNames : this.fullMonthNames
+                               };
+                               break;
+
+                       case 'dow|short':
+                       case 'dow|full':
+                               spec = {
+                                       component: 'dow',
+                                       editable: false,
+                                       type: 'string',
+                                       values: params[ 0 ] === 'short' ? this.shortDayNames : this.fullDayNames
+                               };
+                               break;
+
+                       case 'month|#':
+                       case 'month|0':
+                       case 'day|#':
+                       case 'day|0':
+                       case 'hour|#':
+                       case 'hour|0':
+                       case 'minute|#':
+                       case 'minute|0':
+                       case 'second|#':
+                       case 'second|0':
+                               spec = {
+                                       component: tag,
+                                       type: 'number',
+                                       size: 2,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       case 'hour|12':
+                       case 'hour|012':
+                               spec = {
+                                       component: 'hour12',
+                                       type: 'number',
+                                       size: 2,
+                                       zeropad: params[ 0 ] === '012'
+                               };
+                               break;
+
+                       case 'hour|period':
+                               spec = {
+                                       component: 'hour12period',
+                                       type: 'boolean',
+                                       values: this.hour12Periods
+                               };
+                               break;
+
+                       case 'millisecond|#':
+                       case 'millisecond|0':
+                               spec = {
+                                       component: 'millisecond',
+                                       type: 'number',
+                                       size: 3,
+                                       zeropad: params[ 0 ] === '0'
+                               };
+                               break;
+
+                       default:
+                               return mw.widgets.datetime.ProlepticGregorianDateTimeFormatter[ 'super' ].prototype.getFieldForTag.call( this, tag, params );
+               }
+
+               if ( spec ) {
+                       if ( spec.editable === undefined ) {
+                               spec.editable = true;
+                       }
+                       spec.formatValue = this.formatSpecValue;
+                       spec.parseValue = this.parseSpecValue;
+                       if ( spec.values ) {
+                               spec.size = Math.max.apply(
+                                       null, $.map( spec.values, function ( v ) { return v.length; } )
+                               );
+                       }
+               }
+
+               return spec;
+       };
+
+       /**
+        * Get components from a Date object
+        *
+        * Components are:
+        * - year {number}
+        * - month {number} (1-12)
+        * - day {number} (1-31)
+        * - dow {number} (0-6, 0 is Sunday)
+        * - hour {number} (0-23)
+        * - hour12 {number} (1-12)
+        * - hour12period {boolean}
+        * - minute {number} (0-59)
+        * - second {number} (0-59)
+        * - millisecond {number} (0-999)
+        * - zone {number}
+        *
+        * @param {Date|null} date
+        * @return {Object} Components
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getComponentsFromDate = function ( date ) {
+               var ret;
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               if ( this.local ) {
+                       ret = {
+                               year: date.getFullYear(),
+                               month: date.getMonth() + 1,
+                               day: date.getDate(),
+                               dow: date.getDay() % 7,
+                               hour: date.getHours(),
+                               minute: date.getMinutes(),
+                               second: date.getSeconds(),
+                               millisecond: date.getMilliseconds(),
+                               zone: date.getTimezoneOffset()
+                       };
+               } else {
+                       ret = {
+                               year: date.getUTCFullYear(),
+                               month: date.getUTCMonth() + 1,
+                               day: date.getUTCDate(),
+                               dow: date.getUTCDay() % 7,
+                               hour: date.getUTCHours(),
+                               minute: date.getUTCMinutes(),
+                               second: date.getUTCSeconds(),
+                               millisecond: date.getUTCMilliseconds(),
+                               zone: 0
+                       };
+               }
+
+               ret.hour12period = ret.hour >= 12 ? 1 : 0;
+               ret.hour12 = ret.hour % 12;
+               if ( ret.hour12 === 0 ) {
+                       ret.hour12 = 12;
+               }
+
+               return ret;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getDateFromComponents = function ( components ) {
+               var date = new Date();
+
+               components = $.extend( {}, components );
+               if ( components.hour === undefined && components.hour12 !== undefined && components.hour12period !== undefined ) {
+                       components.hour = ( components.hour12 % 12 ) + ( components.hour12period ? 12 : 0 );
+               }
+               components = $.extend( {}, this.getComponentsFromDate( null ), components );
+
+               if ( components.zone ) {
+                       // Can't just use the constructor because that's stupid about ancient years.
+                       date.setFullYear( components.year, components.month - 1, components.day );
+                       date.setHours( components.hour, components.minute, components.second, components.millisecond );
+               } else {
+                       // Date.UTC() is stupid about ancient years too.
+                       date.setUTCFullYear( components.year, components.month - 1, components.day );
+                       date.setUTCHours( components.hour, components.minute, components.second, components.millisecond );
+               }
+
+               return date;
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.adjustComponent = function ( date, component, delta, mode ) {
+               var min, max, range, components;
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+               components = this.getComponentsFromDate( date );
+
+               switch ( component ) {
+                       case 'year':
+                               min = 0;
+                               max = 9999;
+                               break;
+                       case 'month':
+                               min = 1;
+                               max = 12;
+                               break;
+                       case 'day':
+                               min = 1;
+                               max = this.getDaysInMonth( components.month, components.year );
+                               break;
+                       case 'hour':
+                               min = 0;
+                               max = 23;
+                               break;
+                       case 'minute':
+                       case 'second':
+                               min = 0;
+                               max = 59;
+                               break;
+                       case 'millisecond':
+                               min = 0;
+                               max = 999;
+                               break;
+                       case 'hour12period':
+                               component = 'hour';
+                               min = 0;
+                               max = 23;
+                               delta *= 12;
+                               break;
+                       case 'hour12':
+                               component = 'hour';
+                               min = components.hour12period ? 12 : 0;
+                               max = components.hour12period ? 23 : 11;
+                               break;
+                       default:
+                               return new Date( date.getTime() );
+               }
+
+               components[ component ] += delta;
+               range = max - min + 1;
+               switch ( mode ) {
+                       case 'overflow':
+                               // Date() will mostly handle it automatically. But months need
+                               // manual handling to prevent e.g. Jan 31 => Mar 3.
+                               if ( component === 'month' || component === 'year' ) {
+                                       while ( components.month < 1 ) {
+                                               components[ component ] += 12;
+                                               components.year--;
+                                       }
+                                       while ( components.month > 12 ) {
+                                               components[ component ] -= 12;
+                                               components.year++;
+                                       }
+                               }
+                               break;
+                       case 'wrap':
+                               while ( components[ component ] < min ) {
+                                       components[ component ] += range;
+                               }
+                               while ( components[ component ] > max ) {
+                                       components[ component ] -= range;
+                               }
+                               break;
+                       case 'clip':
+                               if ( components[ component ] < min ) {
+                                       components[ component ] = min;
+                               }
+                               if ( components[ component ] < max ) {
+                                       components[ component ] = max;
+                               }
+                               break;
+               }
+               if ( component === 'month' || component === 'year' ) {
+                       components.day = Math.min( components.day, this.getDaysInMonth( components.month, components.year ) );
+               }
+
+               return this.getDateFromComponents( components );
+       };
+
+       /**
+        * Get the number of days in a month
+        *
+        * @protected
+        * @param {number} month
+        * @param {number} year
+        * @return {number}
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getDaysInMonth = function ( month, year ) {
+               switch ( month ) {
+                       case 4:
+                       case 6:
+                       case 9:
+                       case 11:
+                               return 30;
+                       case 2:
+                               if ( year % 4 ) {
+                                       return 28;
+                               } else if ( year % 100 ) {
+                                       return 29;
+                               }
+                               return ( year % 400 ) ? 28 : 29;
+                       default:
+                               return 31;
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getCalendarHeadings = function () {
+               var a = this.dayLetters;
+
+               if ( this.weekStartsOn ) {
+                       return a.slice( this.weekStartsOn ).concat( a.slice( 0, this.weekStartsOn ) );
+               } else {
+                       return a.slice( 0 ); // clone
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.sameCalendarGrid = function ( date1, date2 ) {
+               if ( this.local ) {
+                       return date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth();
+               } else {
+                       return date1.getUTCFullYear() === date2.getUTCFullYear() && date1.getUTCMonth() === date2.getUTCMonth();
+               }
+       };
+
+       /**
+        * @inheritdoc
+        */
+       mw.widgets.datetime.ProlepticGregorianDateTimeFormatter.prototype.getCalendarData = function ( date ) {
+               var dt, t, d, e, i, row,
+                       getDate = this.local ? 'getDate' : 'getUTCDate',
+                       setDate = this.local ? 'setDate' : 'setUTCDate',
+                       ret = {
+                               dayComponent: 'day',
+                               monthComponent: 'month'
+                       };
+
+               if ( !( date instanceof Date ) ) {
+                       date = this.defaultDate;
+               }
+
+               dt = new Date( date.getTime() );
+               dt[ setDate ]( 1 );
+               t = dt.getTime();
+
+               if ( this.local ) {
+                       ret.header = this.fullMonthNames[ dt.getMonth() + 1 ] + ' ' + dt.getFullYear();
+                       d = dt.getDay() % 7;
+                       e = this.getDaysInMonth( dt.getMonth() + 1, dt.getFullYear() );
+               } else {
+                       ret.header = this.fullMonthNames[ dt.getUTCMonth() + 1 ] + ' ' + dt.getUTCFullYear();
+                       d = dt.getUTCDay() % 7;
+                       e = this.getDaysInMonth( dt.getUTCMonth() + 1, dt.getUTCFullYear() );
+               }
+
+               if ( this.weekStartsOn ) {
+                       d = ( d + 7 - this.weekStartsOn ) % 7;
+               }
+               d = 1 - d;
+
+               ret.rows = [];
+               while ( d <= e ) {
+                       row = [];
+                       for ( i = 0; i < 7; i++, d++ ) {
+                               dt = new Date( t );
+                               dt[ setDate ]( d );
+                               row[ i ] = {
+                                       display: String( dt[ getDate ]() ),
+                                       date: dt,
+                                       extra: d < 1 ? 'prev' : d > e ? 'next' : null
+                               };
+                       }
+                       ret.rows.push( row );
+               }
+
+               return ret;
+       };
+
+}( jQuery, mediaWiki ) );
diff --git a/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less b/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.definitions.less
new file mode 100644 (file)
index 0000000..ee0e66e
--- /dev/null
@@ -0,0 +1,37 @@
+/*!
+ * OOJS-UI defines used by the existing CSS (will make it easier to put this
+ * widget in OOJS-UI once OOJS-UI is capable of handling it)
+ */
+
+.oo-ui-box-sizing( @type: border-box ) {
+       -webkit-box-sizing: @type;
+       -moz-box-sizing: @type;
+       box-sizing: @type;
+}
+
+.oo-ui-unselectable() {
+       -webkit-touch-callout: none;
+       -webkit-user-select: none;
+       -moz-user-select: none;
+       -ms-user-select: none;
+       user-select: none;
+}
+
+.oo-ui-inline-spacing( @spacing, @cancelled-spacing: 0 ) {
+       margin-right: @spacing;
+       &:last-child {
+               margin-right: @cancelled-spacing;
+       }
+}
+
+.oo-ui-transition( @value1, @value2: X, ... ) {
+       @value: ~`"@{arguments}".replace(/[\[\]]|\,\sX/g, '')`;
+       -webkit-transition: @value;
+       -moz-transition: @value;
+       transition: @value;
+}
+
+@indicator-size: unit(12 / 16 / 0.8, em);
+@icon-size: unit(24 / 16 / 0.8, em);
+@quick-ease: 100ms ease;
+@progressive: #347bff;
diff --git a/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js b/resources/src/mediawiki.widgets.datetime/mediawiki.widgets.datetime.js
new file mode 100644 (file)
index 0000000..8d4be8c
--- /dev/null
@@ -0,0 +1,2 @@
+// Create the namespace object
+mediaWiki.widgets.datetime = {};
index 59f1d50..dffcbdd 100644 (file)
        CSP.getNewMenuItems = function ( input ) {
                var i,
                        promises = [],
-                       deferred = new $.Deferred();
+                       deferred = $.Deferred();
 
                if ( $.trim( input ) === '' ) {
                        deferred.resolve( [] );
                this.pushPending();
 
                $.when.apply( $, promises ).done( function () {
-                       var categories, categoryNames,
+                       var categoryNames,
                                allData = [],
                                dataSets = Array.prototype.slice.apply( arguments );
 
                        // Collect values from all results
                        allData = allData.concat.apply( allData, dataSets );
 
-                       // Remove duplicates
-                       categories = allData.filter( function ( value, index, self ) {
-                               return self.indexOf( value ) === index;
-                       } );
-
-                       // Get titles
-                       categoryNames = categories.map( function ( name ) {
-                               return mw.Title.newFromText( name, NS_CATEGORY ).getMainText();
-                       } );
+                       categoryNames = allData
+                               // Remove duplicates
+                               .filter( function ( value, index, self ) {
+                                       return self.indexOf( value ) === index;
+                               } )
+                               // Get Title objects
+                               .map( function ( name ) {
+                                       return mw.Title.newFromText( name );
+                               } )
+                               // Keep only titles from 'Category' namespace
+                               .filter( function ( title ) {
+                                       return title && title.getNamespaceId() === NS_CATEGORY;
+                               } )
+                               // Convert back to strings, strip 'Category:' prefix
+                               .map( function ( title ) {
+                                       return title.getMainText();
+                               } );
 
                        deferred.resolve( categoryNames );
 
        CSP.createItemWidget = function ( data ) {
                return new mw.widgets.CategoryCapsuleItemWidget( {
                        apiUrl: this.api.apiUrl || undefined,
-                       title: mw.Title.newFromText( data, NS_CATEGORY )
+                       title: mw.Title.makeTitle( NS_CATEGORY, data )
                } );
        };
 
+       /**
+        * @inheritdoc
+        */
+       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 );
+       };
+
        /**
         * Validates the values in `this.searchType`.
         *
         * @return {jQuery.Promise} Resolves with an array of categories
         */
        CSP.searchCategories = function ( input, searchType ) {
-               var deferred = new $.Deferred();
+               var deferred = $.Deferred();
 
                switch ( searchType ) {
                        case CategorySelector.SearchType.OpenSearch:
index 13eb85f..605af75 100644 (file)
@@ -1,32 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-       .st1{fill-rule:evenodd;clip-rule:evenodd;}\r
-</style>\r
-<g>\r
-       <circle class="st0" cx="74.9" cy="75.1" r="58.1"/>\r
-       <g>\r
-               <path class="st1" d="M74.1,63.4c-4-7.3-10.8-10.2-18.7-10.2c-11.5,0-20.6,8.1-20.6,21.9c0,14,8.6,21.9,21,21.9\r
-                       c8,0,14.8-4.4,18.5-11l-8.8-4.5c-2,4.7-4.9,6.1-8.7,6.1c-6.5,0-9.5-5.4-9.5-12.5c0-7.1,2.5-12.5,9.5-12.5c1.9,0,5.6,1,7.8,5.7\r
-                       L74.1,63.4z"/>\r
-               <path class="st1" d="M114.8,63.4c-4-7.3-10.8-10.2-18.7-10.2c-11.5,0-20.6,8.1-20.6,21.9c0,14,8.6,21.9,21,21.9\r
-                       c8,0,14.8-4.4,18.5-11l-8.8-4.5c-2,4.7-4.9,6.1-8.7,6.1c-6.5,0-9.5-5.4-9.5-12.5c0-7.1,2.5-12.5,9.5-12.5c1.9,0,5.6,1,7.8,5.7\r
-                       L114.8,63.4z"/>\r
-       </g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+       .st1{fill-rule:evenodd;clip-rule:evenodd;}
+</style>
+<g>
+       <circle class="st0" cx="74.9" cy="75.1" r="58.1"/>
+       <g>
+               <path class="st1" d="M74.1,63.4c-4-7.3-10.8-10.2-18.7-10.2c-11.5,0-20.6,8.1-20.6,21.9c0,14,8.6,21.9,21,21.9
+                       c8,0,14.8-4.4,18.5-11l-8.8-4.5c-2,4.7-4.9,6.1-8.7,6.1c-6.5,0-9.5-5.4-9.5-12.5c0-7.1,2.5-12.5,9.5-12.5c1.9,0,5.6,1,7.8,5.7
+                       L74.1,63.4z"/>
+               <path class="st1" d="M114.8,63.4c-4-7.3-10.8-10.2-18.7-10.2c-11.5,0-20.6,8.1-20.6,21.9c0,14,8.6,21.9,21,21.9
+                       c8,0,14.8-4.4,18.5-11l-8.8-4.5c-2,4.7-4.9,6.1-8.7,6.1c-6.5,0-9.5-5.4-9.5-12.5c0-7.1,2.5-12.5,9.5-12.5c1.9,0,5.6,1,7.8,5.7
+                       L114.8,63.4z"/>
+       </g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
index c3ee55b..96d8084 100644 (file)
@@ -1,32 +1,32 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-       .st1{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-       .st2{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-</style>\r
-<g>\r
-       \r
-               <rect x="7.8" y="34.6" transform="matrix(0.2182 0.9759 -0.9759 0.2182 113.5065 3.8061)" class="st0" width="93.2" height="76.2"/>\r
-       <circle class="st1" cx="54.4" cy="72.7" r="26.1"/>\r
-</g>\r
-<g>\r
-       \r
-               <rect x="51.6" y="41.7" transform="matrix(-0.1524 0.9883 -0.9883 -0.1524 192.0548 -5.1264)" class="st0" width="93.2" height="76.2"/>\r
-       <circle class="st2" cx="98.2" cy="79.8" r="26.1"/>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+       .st1{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+       .st2{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+</style>
+<g>
+       
+               <rect x="7.8" y="34.6" transform="matrix(0.2182 0.9759 -0.9759 0.2182 113.5065 3.8061)" class="st0" width="93.2" height="76.2"/>
+       <circle class="st1" cx="54.4" cy="72.7" r="26.1"/>
+</g>
+<g>
+       
+               <rect x="51.6" y="41.7" transform="matrix(-0.1524 0.9883 -0.9883 -0.1524 192.0548 -5.1264)" class="st0" width="93.2" height="76.2"/>
+       <circle class="st2" cx="98.2" cy="79.8" r="26.1"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
index 639775f..dc660c8 100644 (file)
@@ -1,26 +1,26 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-       .st1{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-</style>\r
-<polygon class="st0" points="6.3,43.3 38.4,43.3 46,34.4 78.4,34.4 85.5,42.6 142.6,42.6 142.6,115 6.3,115 "/>\r
-<g>\r
-       <circle class="st0" cx="63.3" cy="78.8" r="25.8"/>\r
-       <circle class="st1" cx="63.3" cy="78.8" r="16.2"/>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:none;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+       .st1{stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+</style>
+<polygon class="st0" points="6.3,43.3 38.4,43.3 46,34.4 78.4,34.4 85.5,42.6 142.6,42.6 142.6,115 6.3,115 "/>
+<g>
+       <circle class="st0" cx="63.3" cy="78.8" r="25.8"/>
+       <circle class="st1" cx="63.3" cy="78.8" r="16.2"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
index baa982b..2af3f93 100644 (file)
@@ -1,24 +1,24 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}\r
-</style>\r
-<circle class="st0" cx="72.7" cy="62.5" r="47.1"/>\r
-<path d="M76,128.8c34.8,0,63.2-28.3,63.2-63.2c0-18.3-7.9-34.9-20.4-46.4l11.8-11.8L129.1,6l-11.8,11.8c0,0,0,0,0,0l-1.4,1.4\r
-       c13,11.2,21.3,27.8,21.3,46.4c0,33.7-27.4,61.2-61.2,61.2c-18.5,0-35.1-8.3-46.4-21.3l-0.4,0.4l0,0l-13,13l1.4,1.4l11.9-11.9\r
-       c10.9,11.9,26.4,19.6,43.6,20.4v15.7H49.7v2h48.9v-2H75.1v-15.7C75.4,128.8,75.7,128.8,76,128.8z"/>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 150 150" style="enable-background:new 0 0 150 150;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-miterlimit:10;}
+</style>
+<circle class="st0" cx="72.7" cy="62.5" r="47.1"/>
+<path d="M76,128.8c34.8,0,63.2-28.3,63.2-63.2c0-18.3-7.9-34.9-20.4-46.4l11.8-11.8L129.1,6l-11.8,11.8c0,0,0,0,0,0l-1.4,1.4
+       c13,11.2,21.3,27.8,21.3,46.4c0,33.7-27.4,61.2-61.2,61.2c-18.5,0-35.1-8.3-46.4-21.3l-0.4,0.4l0,0l-13,13l1.4,1.4l11.9-11.9
+       c10.9,11.9,26.4,19.6,43.6,20.4v15.7H49.7v2h48.9v-2H75.1v-15.7C75.4,128.8,75.7,128.8,76,128.8z"/>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
index dc43b5d..b110396 100644 (file)
@@ -1,60 +1,60 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 264 162" height="162" width="264" style="enable-background:new 0 0 264 162;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#00AF89;}\r
-       .st1{fill:#FFFFFF;}\r
-       .st2{fill:#FFFFFF;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st3{fill:#E5E5E5;}\r
-       .st4{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st5{fill:#9B9A9A;}\r
-       .st6{fill:none;stroke:#E5E5E5;stroke-linejoin:round;stroke-miterlimit:10;}\r
-</style>\r
-<rect x="62.3" y="28.1" class="st0" width="137.6" height="105"/>\r
-<g>\r
-       <path class="st1" d="M73.1,57v64.4h45.3V78.5h25.3v42.9h45.3V57H73.1z M94.3,107.6H80.9V94.3h13.3V107.6z M94.3,91.9H80.9V78.5\r
-               h13.3V91.9z M109.9,107.6H96.6V94.3h13.3V107.6z M109.9,91.9H96.6V78.5h13.3V91.9z M164.3,107.6h-13.3V94.3h13.3V107.6z\r
-                M164.3,91.9h-13.3V78.5h13.3V91.9z M179.9,107.6h-13.3V94.3h13.3V107.6z M179.9,91.9h-13.3V78.5h13.3V91.9z"/>\r
-       <rect x="73.1" y="48.5" class="st1" width="116" height="5.7"/>\r
-       <rect x="73.1" y="39.9" class="st1" width="116" height="5.7"/>\r
-</g>\r
-<rect x="62.3" y="124" class="st1" width="137.6" height="1.7"/>\r
-<g>\r
-       <g>\r
-               <rect x="6.7" y="7.4" class="st2" width="35.3" height="12.7"/>\r
-               <rect x="42" y="11.1" class="st3" width="2.3" height="5.7"/>\r
-       </g>\r
-       <rect x="8.2" y="9.1" class="st3" width="5.2" height="9.2"/>\r
-       <rect x="14.5" y="9.1" class="st3" width="5.2" height="9.2"/>\r
-       <rect x="21" y="9.1" class="st3" width="5.2" height="9.2"/>\r
-</g>\r
-<circle class="st3" cx="252.7" cy="12.1" r="6.2"/>\r
-<rect x="239.3" y="9.1" class="st3" width="5.3" height="7.7"/>\r
-<rect x="233" y="9.1" class="st3" width="5.3" height="7.7"/>\r
-<rect x="214" y="143.3" class="st3" width="5.3" height="7.7"/>\r
-<rect x="207.6" y="143.3" class="st3" width="5.3" height="7.7"/>\r
-<rect x="50.4" y="143.3" class="st3" width="5.3" height="7.7"/>\r
-<rect x="39" y="143.3" class="st3" width="5.3" height="7.7"/>\r
-<line class="st4" x1="73.1" y1="148" x2="189.1" y2="148"/>\r
-<line class="st4" x1="132" y1="143.1" x2="132" y2="152.9"/>\r
-<line class="st4" x1="189.1" y1="144.3" x2="189.1" y2="151.7"/>\r
-<line class="st4" x1="73.1" y1="144.3" x2="73.1" y2="151.7"/>\r
-<line class="st4" x1="86" y1="145.5" x2="86" y2="150.5"/>\r
-<line class="st4" x1="100.7" y1="145.5" x2="100.7" y2="150.5"/>\r
-<line class="st4" x1="115.3" y1="145.5" x2="115.3" y2="150.5"/>\r
-<line class="st4" x1="145.8" y1="145.5" x2="145.8" y2="150.5"/>\r
-<line class="st4" x1="160.5" y1="145.5" x2="160.5" y2="150.5"/>\r
-<line class="st4" x1="175.2" y1="145.5" x2="175.2" y2="150.5"/>\r
-<g>\r
-       <rect x="113.8" y="154.1" class="st5" width="3.2" height="4.7"/>\r
-       <polygon class="st5" points="113.8,154.1 115.3,152.3 116.9,154.1        "/>\r
-</g>\r
-<g>\r
-       <g>\r
-               <polyline class="st6" points="34.3,147.1 31.5,147.1 34.3,144.3 31.5,147.1 34.3,147.1 31.5,149.9                 "/>\r
-       </g>\r
-       <polygon class="st3" points="33.9,149.5 32.1,151.7 30.3,149.5   "/>\r
-</g>\r
-<line class="st6" x1="48.9" y1="143.2" x2="45.9" y2="151.1"/>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 264 162" height="162" width="264" style="enable-background:new 0 0 264 162;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#00AF89;}
+       .st1{fill:#FFFFFF;}
+       .st2{fill:#FFFFFF;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st3{fill:#E5E5E5;}
+       .st4{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st5{fill:#9B9A9A;}
+       .st6{fill:none;stroke:#E5E5E5;stroke-linejoin:round;stroke-miterlimit:10;}
+</style>
+<rect x="62.3" y="28.1" class="st0" width="137.6" height="105"/>
+<g>
+       <path class="st1" d="M73.1,57v64.4h45.3V78.5h25.3v42.9h45.3V57H73.1z M94.3,107.6H80.9V94.3h13.3V107.6z M94.3,91.9H80.9V78.5
+               h13.3V91.9z M109.9,107.6H96.6V94.3h13.3V107.6z M109.9,91.9H96.6V78.5h13.3V91.9z M164.3,107.6h-13.3V94.3h13.3V107.6z
+                M164.3,91.9h-13.3V78.5h13.3V91.9z M179.9,107.6h-13.3V94.3h13.3V107.6z M179.9,91.9h-13.3V78.5h13.3V91.9z"/>
+       <rect x="73.1" y="48.5" class="st1" width="116" height="5.7"/>
+       <rect x="73.1" y="39.9" class="st1" width="116" height="5.7"/>
+</g>
+<rect x="62.3" y="124" class="st1" width="137.6" height="1.7"/>
+<g>
+       <g>
+               <rect x="6.7" y="7.4" class="st2" width="35.3" height="12.7"/>
+               <rect x="42" y="11.1" class="st3" width="2.3" height="5.7"/>
+       </g>
+       <rect x="8.2" y="9.1" class="st3" width="5.2" height="9.2"/>
+       <rect x="14.5" y="9.1" class="st3" width="5.2" height="9.2"/>
+       <rect x="21" y="9.1" class="st3" width="5.2" height="9.2"/>
+</g>
+<circle class="st3" cx="252.7" cy="12.1" r="6.2"/>
+<rect x="239.3" y="9.1" class="st3" width="5.3" height="7.7"/>
+<rect x="233" y="9.1" class="st3" width="5.3" height="7.7"/>
+<rect x="214" y="143.3" class="st3" width="5.3" height="7.7"/>
+<rect x="207.6" y="143.3" class="st3" width="5.3" height="7.7"/>
+<rect x="50.4" y="143.3" class="st3" width="5.3" height="7.7"/>
+<rect x="39" y="143.3" class="st3" width="5.3" height="7.7"/>
+<line class="st4" x1="73.1" y1="148" x2="189.1" y2="148"/>
+<line class="st4" x1="132" y1="143.1" x2="132" y2="152.9"/>
+<line class="st4" x1="189.1" y1="144.3" x2="189.1" y2="151.7"/>
+<line class="st4" x1="73.1" y1="144.3" x2="73.1" y2="151.7"/>
+<line class="st4" x1="86" y1="145.5" x2="86" y2="150.5"/>
+<line class="st4" x1="100.7" y1="145.5" x2="100.7" y2="150.5"/>
+<line class="st4" x1="115.3" y1="145.5" x2="115.3" y2="150.5"/>
+<line class="st4" x1="145.8" y1="145.5" x2="145.8" y2="150.5"/>
+<line class="st4" x1="160.5" y1="145.5" x2="160.5" y2="150.5"/>
+<line class="st4" x1="175.2" y1="145.5" x2="175.2" y2="150.5"/>
+<g>
+       <rect x="113.8" y="154.1" class="st5" width="3.2" height="4.7"/>
+       <polygon class="st5" points="113.8,154.1 115.3,152.3 116.9,154.1        "/>
+</g>
+<g>
+       <g>
+               <polyline class="st6" points="34.3,147.1 31.5,147.1 34.3,144.3 31.5,147.1 34.3,147.1 31.5,149.9                 "/>
+       </g>
+       <polygon class="st3" points="33.9,149.5 32.1,151.7 30.3,149.5   "/>
+</g>
+<line class="st6" x1="48.9" y1="143.2" x2="45.9" y2="151.1"/>
+</svg>
index ff3d682..c32f79f 100644 (file)
@@ -1,98 +1,98 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st1{fill:none;stroke:#9B9A9A;stroke-miterlimit:10;}\r
-       .st2{fill:#E5E5E5;}\r
-       .st3{fill:#9B9A9A;}\r
-       .st4{fill:#00AF89;}\r
-       .st5{fill:#FFFFFF;}\r
-       .st6{fill:none;stroke:#FFFFFF;stroke-width:5;stroke-miterlimit:10;}\r
-</style>\r
-<line class="st0" x1="2" y1="10.5" x2="262" y2="10.5"/>\r
-<line class="st0" x1="2" y1="24.8" x2="262" y2="24.8"/>\r
-<g>\r
-       <rect x="6.9" y="32.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="32.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="42.1" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="42.1" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="51.4" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="51.4" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="60.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="60.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="70.1" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="70.1" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="79.4" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="79.4" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="88.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="88.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="98.1" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="98.1" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="107.4" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="107.4" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="6.9" y="116.8" class="st1" width="9.3" height="9.3"/>\r
-       <rect x="16.2" y="116.8" class="st1" width="9.3" height="9.3"/>\r
-</g>\r
-<rect x="3.2" y="12.7" class="st2" width="9.3" height="9.3"/>\r
-<rect x="3.2" y="3.4" class="st2" width="4.7" height="4.7"/>\r
-<rect x="10.2" y="3.4" class="st2" width="4.7" height="4.7"/>\r
-<rect x="16.9" y="3.4" class="st2" width="4.7" height="4.7"/>\r
-<rect x="249.2" y="3.4" class="st2" width="4.7" height="4.7"/>\r
-<rect x="255.9" y="3.4" class="st2" width="4.7" height="4.7"/>\r
-<rect x="14.6" y="12.7" class="st2" width="9.3" height="9.3"/>\r
-<rect x="239.9" y="12.7" class="st2" width="9.3" height="9.3"/>\r
-<rect x="228.9" y="12.7" class="st2" width="9.3" height="9.3"/>\r
-<rect x="251.2" y="12.7" class="st2" width="9.3" height="9.3"/>\r
-<g>\r
-       <path class="st2" d="M73.5,13.7V21H26.6v-7.3H73.5 M74.5,12.7H25.6V22h48.9V12.7L74.5,12.7z"/>\r
-</g>\r
-<g>\r
-       <path class="st2" d="M117.8,13.7V21H76.9v-7.3H117.8 M118.8,12.7H75.9V22h42.9V12.7L118.8,12.7z"/>\r
-</g>\r
-<g>\r
-       <path class="st2" d="M162.5,13.7V21h-40.9v-7.3H162.5 M163.5,12.7h-42.9V22h42.9V12.7L163.5,12.7z"/>\r
-</g>\r
-<rect x="209.7" y="30.3" class="st2" width="50.9" height="32.3"/>\r
-<rect x="209.7" y="30.3" class="st3" width="50.9" height="5.7"/>\r
-<g>\r
-       <rect x="212.2" y="38.5" class="st3" width="6.2" height="6.2"/>\r
-       <rect x="239.9" y="38.5" class="st3" width="6.2" height="2.5"/>\r
-       <rect x="239.9" y="42.2" class="st3" width="6.2" height="2.5"/>\r
-       <g>\r
-               <path class="st3" d="M237.2,39.5v4.2H221v-4.2H237.2 M238.2,38.5H220v6.2h18.2V38.5L238.2,38.5z"/>\r
-       </g>\r
-</g>\r
-<g>\r
-       <rect x="212.2" y="46.5" class="st3" width="6.2" height="6.2"/>\r
-       <rect x="239.9" y="46.5" class="st3" width="6.2" height="2.5"/>\r
-       <rect x="239.9" y="50.2" class="st3" width="6.2" height="2.5"/>\r
-       <g>\r
-               <path class="st3" d="M237.2,47.5v4.2H221v-4.2H237.2 M238.2,46.5H220v6.2h18.2V46.5L238.2,46.5z"/>\r
-       </g>\r
-</g>\r
-<rect x="209.7" y="66.6" class="st2" width="50.9" height="32.3"/>\r
-<rect x="209.7" y="66.6" class="st3" width="50.9" height="5.7"/>\r
-<g>\r
-       <path class="st3" d="M257.2,75.8V80h-44.1v-4.2H257.2 M258.2,74.8h-46.1V81h46.1V74.8L258.2,74.8z"/>\r
-</g>\r
-<g>\r
-       <path class="st3" d="M257.2,81v4.2h-44.1V81H257.2 M258.2,80h-46.1v6.2h46.1V80L258.2,80z"/>\r
-</g>\r
-<g>\r
-       <path class="st3" d="M257.2,86.2v4.2h-44.1v-4.2H257.2 M258.2,85.2h-46.1v6.2h46.1V85.2L258.2,85.2z"/>\r
-</g>\r
-<g>\r
-       <path class="st3" d="M257.2,91.3v4.2h-44.1v-4.2H257.2 M258.2,90.3h-46.1v6.2h46.1V90.3L258.2,90.3z"/>\r
-</g>\r
-<ellipse class="st1" cx="218.3" cy="85.7" rx="0" ry="10.2"/>\r
-<rect x="69.5" y="47.5" class="st4" width="108" height="82.4"/>\r
-<circle class="st5" cx="123.5" cy="88.7" r="6.6"/>\r
-<circle class="st5" cx="116.9" cy="106.2" r="6.6"/>\r
-<circle class="st5" cx="154.5" cy="80.3" r="6.6"/>\r
-<circle class="st5" cx="93.1" cy="81.5" r="6.6"/>\r
-<circle class="st5" cx="141.7" cy="114.1" r="6.6"/>\r
-<circle class="st6" cx="123.5" cy="88.7" r="18.2"/>\r
-<circle class="st6" cx="123.5" cy="88.7" r="31.7"/>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st1{fill:none;stroke:#9B9A9A;stroke-miterlimit:10;}
+       .st2{fill:#E5E5E5;}
+       .st3{fill:#9B9A9A;}
+       .st4{fill:#00AF89;}
+       .st5{fill:#FFFFFF;}
+       .st6{fill:none;stroke:#FFFFFF;stroke-width:5;stroke-miterlimit:10;}
+</style>
+<line class="st0" x1="2" y1="10.5" x2="262" y2="10.5"/>
+<line class="st0" x1="2" y1="24.8" x2="262" y2="24.8"/>
+<g>
+       <rect x="6.9" y="32.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="32.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="42.1" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="42.1" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="51.4" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="51.4" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="60.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="60.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="70.1" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="70.1" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="79.4" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="79.4" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="88.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="88.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="98.1" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="98.1" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="107.4" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="107.4" class="st1" width="9.3" height="9.3"/>
+       <rect x="6.9" y="116.8" class="st1" width="9.3" height="9.3"/>
+       <rect x="16.2" y="116.8" class="st1" width="9.3" height="9.3"/>
+</g>
+<rect x="3.2" y="12.7" class="st2" width="9.3" height="9.3"/>
+<rect x="3.2" y="3.4" class="st2" width="4.7" height="4.7"/>
+<rect x="10.2" y="3.4" class="st2" width="4.7" height="4.7"/>
+<rect x="16.9" y="3.4" class="st2" width="4.7" height="4.7"/>
+<rect x="249.2" y="3.4" class="st2" width="4.7" height="4.7"/>
+<rect x="255.9" y="3.4" class="st2" width="4.7" height="4.7"/>
+<rect x="14.6" y="12.7" class="st2" width="9.3" height="9.3"/>
+<rect x="239.9" y="12.7" class="st2" width="9.3" height="9.3"/>
+<rect x="228.9" y="12.7" class="st2" width="9.3" height="9.3"/>
+<rect x="251.2" y="12.7" class="st2" width="9.3" height="9.3"/>
+<g>
+       <path class="st2" d="M73.5,13.7V21H26.6v-7.3H73.5 M74.5,12.7H25.6V22h48.9V12.7L74.5,12.7z"/>
+</g>
+<g>
+       <path class="st2" d="M117.8,13.7V21H76.9v-7.3H117.8 M118.8,12.7H75.9V22h42.9V12.7L118.8,12.7z"/>
+</g>
+<g>
+       <path class="st2" d="M162.5,13.7V21h-40.9v-7.3H162.5 M163.5,12.7h-42.9V22h42.9V12.7L163.5,12.7z"/>
+</g>
+<rect x="209.7" y="30.3" class="st2" width="50.9" height="32.3"/>
+<rect x="209.7" y="30.3" class="st3" width="50.9" height="5.7"/>
+<g>
+       <rect x="212.2" y="38.5" class="st3" width="6.2" height="6.2"/>
+       <rect x="239.9" y="38.5" class="st3" width="6.2" height="2.5"/>
+       <rect x="239.9" y="42.2" class="st3" width="6.2" height="2.5"/>
+       <g>
+               <path class="st3" d="M237.2,39.5v4.2H221v-4.2H237.2 M238.2,38.5H220v6.2h18.2V38.5L238.2,38.5z"/>
+       </g>
+</g>
+<g>
+       <rect x="212.2" y="46.5" class="st3" width="6.2" height="6.2"/>
+       <rect x="239.9" y="46.5" class="st3" width="6.2" height="2.5"/>
+       <rect x="239.9" y="50.2" class="st3" width="6.2" height="2.5"/>
+       <g>
+               <path class="st3" d="M237.2,47.5v4.2H221v-4.2H237.2 M238.2,46.5H220v6.2h18.2V46.5L238.2,46.5z"/>
+       </g>
+</g>
+<rect x="209.7" y="66.6" class="st2" width="50.9" height="32.3"/>
+<rect x="209.7" y="66.6" class="st3" width="50.9" height="5.7"/>
+<g>
+       <path class="st3" d="M257.2,75.8V80h-44.1v-4.2H257.2 M258.2,74.8h-46.1V81h46.1V74.8L258.2,74.8z"/>
+</g>
+<g>
+       <path class="st3" d="M257.2,81v4.2h-44.1V81H257.2 M258.2,80h-46.1v6.2h46.1V80L258.2,80z"/>
+</g>
+<g>
+       <path class="st3" d="M257.2,86.2v4.2h-44.1v-4.2H257.2 M258.2,85.2h-46.1v6.2h46.1V85.2L258.2,85.2z"/>
+</g>
+<g>
+       <path class="st3" d="M257.2,91.3v4.2h-44.1v-4.2H257.2 M258.2,90.3h-46.1v6.2h46.1V90.3L258.2,90.3z"/>
+</g>
+<ellipse class="st1" cx="218.3" cy="85.7" rx="0" ry="10.2"/>
+<rect x="69.5" y="47.5" class="st4" width="108" height="82.4"/>
+<circle class="st5" cx="123.5" cy="88.7" r="6.6"/>
+<circle class="st5" cx="116.9" cy="106.2" r="6.6"/>
+<circle class="st5" cx="154.5" cy="80.3" r="6.6"/>
+<circle class="st5" cx="93.1" cy="81.5" r="6.6"/>
+<circle class="st5" cx="141.7" cy="114.1" r="6.6"/>
+<circle class="st6" cx="123.5" cy="88.7" r="18.2"/>
+<circle class="st6" cx="123.5" cy="88.7" r="31.7"/>
+</svg>
index 7bcf826..67c4ef0 100644 (file)
@@ -1,92 +1,92 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{display:none;}\r
-       .st1{display:inline;}\r
-       .st2{fill:#FFFFFF;}\r
-       .st3{fill:#E5E5E5;}\r
-       .st4{opacity:0.5;fill:#E5E5E5;enable-background:new    ;}\r
-       .st5{fill:#E5E5E5;stroke:#E5E5E5;}\r
-       .st6{fill:#9B9B9B;}\r
-       .st7{fill:#4470B6;}\r
-       .st8{fill:#D0D1D1;}\r
-       .st9{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st10{fill:none;stroke:#D02227;stroke-miterlimit:10;}\r
-       .st11{fill:#D02227;}\r
-</style>\r
-<g id="Layer_1" class="st0">\r
-       <g class="st1">\r
-               <path class="st2" d="M24.8,151.4l-11.5,10l-12.9-9.6V0.5h263v151.7l-9,6.7V42.5h-39v112.6l-4.4-3.7l-7.6,5.3V42.5h-155v110.8\r
-                       l-9.8,8L24.8,151.4z"/>\r
-               <path class="st3" d="M263,1v151l-8,6V42h-40v112l-3.3-2.8l-0.6-0.5l-0.6,0.4l-6.5,4.6V42H48v111.1l-9.3,7.7l-13.3-9.6l-0.6-0.5\r
-                       l-0.6,0.5l-10.9,9.5L1,151.5V1L263,1L263,1z M264,0H0v152l13.4,10l11.5-10l13.9,10l10.2-8.4V43h154v114.7l8.1-5.7l4.9,4.2V43h38\r
-                       v116.9l10-7.4V0L264,0z M203,43H49v110.6l2.1-1.6l12.6,10l13.8-10l14.2,10l13-10l13,10l13.8-10l12.1,10l13.4-10l14.2,10l13-10\r
-                       l13,10l5.8-4.3L203,43L203,43z"/>\r
-               <path class="st4" d="M254,43h-38v113.2l7.6,5.8l13.8-10l13.9,10l2.7-2.1V43z"/>\r
-               <path class="st5" d="M2,14.5h260"/>\r
-               <path class="st3" d="M232.5,5h26v6h-26V5z M208.5,5h22v6h-22V5z M142,6v4H50V6H142z M143,5H49v6h94V5z M184.5,5h22v6h-22V5z\r
-                        M161.5,5h13v6h-13V5z M176.5,5h6v6h-6V5z M153.5,5h6v6h-6V5z M9,5h32v6H9V5z M52,7h2v2h-2V7z M11,36c0-7.7,6.3-14,14-14\r
-                       s14,6.3,14,14s-6.3,14-14,14S11,43.7,11,36z M38,107V72H13v35H38z M38,59v-5H13v5H38z"/>\r
-       </g>\r
-       <g class="st1">\r
-               <path class="st6" d="M123.8,68H85.6v7.8h38.1L123.8,68L123.8,68z M180.5,103v-8.8h-50.8v8.8H180.5z M90.8,80.6H69.9v8.6h20.8V80.6\r
-                       z M189.3,88.4v-7.8h-37.7v7.8H189.3z M100.7,94.1H75v7.8h25.7V94.1z M175.2,68H129v7.8h46.2V68z"/>\r
-               <path class="st7" d="M94.7,80.6v9.2h51.6v-9.2L94.7,80.6z"/>\r
-       </g>\r
-       <g class="st1">\r
-               <path class="st2" d="M94,94h75v44H94V94z"/>\r
-               <path class="st7" d="M169,94h-31v44h31V94z M150.8,118.1l5.9,9.7l3.2-3l4.8,6.1l-21.1,0l6.2-12.8H150.8z"/>\r
-               <g transform="scale(-1 1)">\r
-                       <path class="st8" d="M-102,100h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,100.2-101.4,100-102,100z"/>\r
-                       <path class="st8" d="M-102,104h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,104.2-101.4,104-102,104z"/>\r
-                       <path class="st8" d="M-102,108h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,108.2-101.4,108-102,108z"/>\r
-                       <path class="st8" d="M-102,112h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,112.2-101.4,112-102,112z"/>\r
-                       <path class="st8" d="M-102,116h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,116.2-101.4,116-102,116z"/>\r
-                       <path class="st8" d="M-102,120h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,120.2-101.4,120-102,120z"/>\r
-                       <path class="st8" d="M-102,124h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,124.2-101.4,124-102,124z"/>\r
-                       <path class="st8" d="M-102,128h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0\r
-                               C-101,128.2-101.4,128-102,128z"/>\r
-               </g>\r
-       </g>\r
-</g>\r
-<g id="Layer_2">\r
-       <g>\r
-               <rect x="4.8" y="5.5" class="st2" width="93" height="5"/>\r
-               <path class="st3" d="M97.2,6v4h-92V6H97.2 M98.2,5h-94v6h94V5L98.2,5z"/>\r
-       </g>\r
-       <polygon class="st3" points="177.7,8 176.4,8 109.6,8 108.2,8 106,14 111.8,14 174.2,14 180,14    "/>\r
-       <polygon class="st3" points="256.5,8 255.2,8 188.3,8 187,8 184.7,14 190.6,14 252.9,14 258.8,14  "/>\r
-       <line class="st9" x1="2" y1="14.5" x2="262" y2="14.5"/>\r
-       <line class="st9" x1="2" y1="32.8" x2="262" y2="32.8"/>\r
-       <g>\r
-               <circle class="st10" cx="8.9" cy="22.6" r="3.6"/>\r
-               <line class="st10" x1="11.6" y1="25" x2="15" y2="28.5"/>\r
-       </g>\r
-       <rect x="20.9" y="19" class="st3" width="57.6" height="9.5"/>\r
-       <rect x="4.3" y="37" class="st3" width="69.2" height="32.7"/>\r
-       <rect x="4.3" y="72.5" class="st3" width="90.1" height="56.8"/>\r
-       <rect x="97" y="72.5" class="st11" width="106.5" height="56.8"/>\r
-       <rect x="206.3" y="72.5" class="st3" width="52.4" height="56.8"/>\r
-       <rect x="4.3" y="132.2" class="st3" width="36.5" height="25.3"/>\r
-       <rect x="43.7" y="132.2" class="st3" width="62.3" height="25.3"/>\r
-       <rect x="109.3" y="132.2" class="st3" width="18.1" height="25.3"/>\r
-       <rect x="169.1" y="132.2" class="st3" width="18.1" height="25.3"/>\r
-       <rect x="189.8" y="132.2" class="st3" width="18.1" height="25.3"/>\r
-       <rect x="210.6" y="132.2" class="st3" width="48.1" height="25.3"/>\r
-       <rect x="130" y="132.2" class="st3" width="36" height="25.3"/>\r
-       <rect x="115.2" y="37" class="st3" width="36.5" height="32.7"/>\r
-       <rect x="76.1" y="37" class="st3" width="36.5" height="32.7"/>\r
-       <rect x="154.6" y="37" class="st3" width="56" height="32.7"/>\r
-       <rect x="213.3" y="37" class="st3" width="45.4" height="32.7"/>\r
-       <path class="st2" d="M102.4,115.4l9.5-9.5l12.8,12.8l26.1-26.1l31.8,31.8h-80.3L102.4,115.4z"/>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
+<style type="text/css">
+       .st0{display:none;}
+       .st1{display:inline;}
+       .st2{fill:#FFFFFF;}
+       .st3{fill:#E5E5E5;}
+       .st4{opacity:0.5;fill:#E5E5E5;enable-background:new    ;}
+       .st5{fill:#E5E5E5;stroke:#E5E5E5;}
+       .st6{fill:#9B9B9B;}
+       .st7{fill:#4470B6;}
+       .st8{fill:#D0D1D1;}
+       .st9{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st10{fill:none;stroke:#D02227;stroke-miterlimit:10;}
+       .st11{fill:#D02227;}
+</style>
+<g id="Layer_1" class="st0">
+       <g class="st1">
+               <path class="st2" d="M24.8,151.4l-11.5,10l-12.9-9.6V0.5h263v151.7l-9,6.7V42.5h-39v112.6l-4.4-3.7l-7.6,5.3V42.5h-155v110.8
+                       l-9.8,8L24.8,151.4z"/>
+               <path class="st3" d="M263,1v151l-8,6V42h-40v112l-3.3-2.8l-0.6-0.5l-0.6,0.4l-6.5,4.6V42H48v111.1l-9.3,7.7l-13.3-9.6l-0.6-0.5
+                       l-0.6,0.5l-10.9,9.5L1,151.5V1L263,1L263,1z M264,0H0v152l13.4,10l11.5-10l13.9,10l10.2-8.4V43h154v114.7l8.1-5.7l4.9,4.2V43h38
+                       v116.9l10-7.4V0L264,0z M203,43H49v110.6l2.1-1.6l12.6,10l13.8-10l14.2,10l13-10l13,10l13.8-10l12.1,10l13.4-10l14.2,10l13-10
+                       l13,10l5.8-4.3L203,43L203,43z"/>
+               <path class="st4" d="M254,43h-38v113.2l7.6,5.8l13.8-10l13.9,10l2.7-2.1V43z"/>
+               <path class="st5" d="M2,14.5h260"/>
+               <path class="st3" d="M232.5,5h26v6h-26V5z M208.5,5h22v6h-22V5z M142,6v4H50V6H142z M143,5H49v6h94V5z M184.5,5h22v6h-22V5z
+                        M161.5,5h13v6h-13V5z M176.5,5h6v6h-6V5z M153.5,5h6v6h-6V5z M9,5h32v6H9V5z M52,7h2v2h-2V7z M11,36c0-7.7,6.3-14,14-14
+                       s14,6.3,14,14s-6.3,14-14,14S11,43.7,11,36z M38,107V72H13v35H38z M38,59v-5H13v5H38z"/>
+       </g>
+       <g class="st1">
+               <path class="st6" d="M123.8,68H85.6v7.8h38.1L123.8,68L123.8,68z M180.5,103v-8.8h-50.8v8.8H180.5z M90.8,80.6H69.9v8.6h20.8V80.6
+                       z M189.3,88.4v-7.8h-37.7v7.8H189.3z M100.7,94.1H75v7.8h25.7V94.1z M175.2,68H129v7.8h46.2V68z"/>
+               <path class="st7" d="M94.7,80.6v9.2h51.6v-9.2L94.7,80.6z"/>
+       </g>
+       <g class="st1">
+               <path class="st2" d="M94,94h75v44H94V94z"/>
+               <path class="st7" d="M169,94h-31v44h31V94z M150.8,118.1l5.9,9.7l3.2-3l4.8,6.1l-21.1,0l6.2-12.8H150.8z"/>
+               <g transform="scale(-1 1)">
+                       <path class="st8" d="M-102,100h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,100.2-101.4,100-102,100z"/>
+                       <path class="st8" d="M-102,104h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,104.2-101.4,104-102,104z"/>
+                       <path class="st8" d="M-102,108h-18c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h18c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,108.2-101.4,108-102,108z"/>
+                       <path class="st8" d="M-102,112h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,112.2-101.4,112-102,112z"/>
+                       <path class="st8" d="M-102,116h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,116.2-101.4,116-102,116z"/>
+                       <path class="st8" d="M-102,120h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,120.2-101.4,120-102,120z"/>
+                       <path class="st8" d="M-102,124h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,124.2-101.4,124-102,124z"/>
+                       <path class="st8" d="M-102,128h-26c-0.6,0-1,0.2-1,0.5l0,0c0,0.3,0.4,0.5,1,0.5h26c0.6,0,1-0.2,1-0.5l0,0
+                               C-101,128.2-101.4,128-102,128z"/>
+               </g>
+       </g>
+</g>
+<g id="Layer_2">
+       <g>
+               <rect x="4.8" y="5.5" class="st2" width="93" height="5"/>
+               <path class="st3" d="M97.2,6v4h-92V6H97.2 M98.2,5h-94v6h94V5L98.2,5z"/>
+       </g>
+       <polygon class="st3" points="177.7,8 176.4,8 109.6,8 108.2,8 106,14 111.8,14 174.2,14 180,14    "/>
+       <polygon class="st3" points="256.5,8 255.2,8 188.3,8 187,8 184.7,14 190.6,14 252.9,14 258.8,14  "/>
+       <line class="st9" x1="2" y1="14.5" x2="262" y2="14.5"/>
+       <line class="st9" x1="2" y1="32.8" x2="262" y2="32.8"/>
+       <g>
+               <circle class="st10" cx="8.9" cy="22.6" r="3.6"/>
+               <line class="st10" x1="11.6" y1="25" x2="15" y2="28.5"/>
+       </g>
+       <rect x="20.9" y="19" class="st3" width="57.6" height="9.5"/>
+       <rect x="4.3" y="37" class="st3" width="69.2" height="32.7"/>
+       <rect x="4.3" y="72.5" class="st3" width="90.1" height="56.8"/>
+       <rect x="97" y="72.5" class="st11" width="106.5" height="56.8"/>
+       <rect x="206.3" y="72.5" class="st3" width="52.4" height="56.8"/>
+       <rect x="4.3" y="132.2" class="st3" width="36.5" height="25.3"/>
+       <rect x="43.7" y="132.2" class="st3" width="62.3" height="25.3"/>
+       <rect x="109.3" y="132.2" class="st3" width="18.1" height="25.3"/>
+       <rect x="169.1" y="132.2" class="st3" width="18.1" height="25.3"/>
+       <rect x="189.8" y="132.2" class="st3" width="18.1" height="25.3"/>
+       <rect x="210.6" y="132.2" class="st3" width="48.1" height="25.3"/>
+       <rect x="130" y="132.2" class="st3" width="36" height="25.3"/>
+       <rect x="115.2" y="37" class="st3" width="36.5" height="32.7"/>
+       <rect x="76.1" y="37" class="st3" width="36.5" height="32.7"/>
+       <rect x="154.6" y="37" class="st3" width="56" height="32.7"/>
+       <rect x="213.3" y="37" class="st3" width="45.4" height="32.7"/>
+       <path class="st2" d="M102.4,115.4l9.5-9.5l12.8,12.8l26.1-26.1l31.8,31.8h-80.3L102.4,115.4z"/>
+</g>
+</svg>
index 408f9e6..17c54d2 100644 (file)
@@ -1,53 +1,53 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#FFFFFF;}\r
-       .st1{fill:#E5E5E5;}\r
-       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st3{fill:none;stroke:#D02227;stroke-miterlimit:10;}\r
-       .st4{fill:#D02227;}\r
-</style>\r
-<g>\r
-       <rect x="166.2" y="5.5" class="st0" width="93" height="5"/>\r
-       <path class="st1" d="M258.8,6v4h-92V6H258.8 M259.8,5h-94v6h94V5L259.8,5z"/>\r
-</g>\r
-<polygon class="st1" points="86.3,8 87.6,8 154.4,8 155.8,8 158,14 152.2,14 89.8,14 84,14 "/>\r
-<polygon class="st1" points="7.5,8 8.8,8 75.7,8 77,8 79.3,14 73.4,14 11.1,14 5.2,14 "/>\r
-<line class="st2" x1="262" y1="14.5" x2="2" y2="14.5"/>\r
-<line class="st2" x1="262" y1="32.8" x2="2" y2="32.8"/>\r
-<g>\r
-       <circle class="st3" cx="255.1" cy="22.6" r="3.6"/>\r
-       <line class="st3" x1="252.4" y1="25" x2="249" y2="28.5"/>\r
-</g>\r
-<rect x="185.5" y="19" class="st1" width="57.6" height="9.5"/>\r
-<rect x="190.5" y="37" class="st1" width="69.2" height="32.7"/>\r
-<rect x="169.7" y="72.5" class="st1" width="90.1" height="56.8"/>\r
-<rect x="60.4" y="72.5" class="st4" width="106.5" height="56.8"/>\r
-<rect x="5.2" y="72.5" class="st1" width="52.4" height="56.8"/>\r
-<rect x="223.3" y="132.2" class="st1" width="36.5" height="25.3"/>\r
-<rect x="158" y="132.2" class="st1" width="62.3" height="25.3"/>\r
-<rect x="136.6" y="132.2" class="st1" width="18.1" height="25.3"/>\r
-<rect x="76.8" y="132.2" class="st1" width="18.1" height="25.3"/>\r
-<rect x="56.1" y="132.2" class="st1" width="18.1" height="25.3"/>\r
-<rect x="5.2" y="132.2" class="st1" width="48.1" height="25.3"/>\r
-<rect x="97.9" y="132.2" class="st1" width="36" height="25.3"/>\r
-<rect x="112.3" y="37" class="st1" width="36.5" height="32.7"/>\r
-<rect x="151.4" y="37" class="st1" width="36.5" height="32.7"/>\r
-<rect x="53.4" y="37" class="st1" width="56" height="32.7"/>\r
-<rect x="5.2" y="37" class="st1" width="45.4" height="32.7"/>\r
-<path class="st0" d="M161.7,124.3H81.4l31.8-31.8l26.1,26.1l12.8-12.8l9.5,9.5L161.7,124.3z"/>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+       .st1{fill:#E5E5E5;}
+       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st3{fill:none;stroke:#D02227;stroke-miterlimit:10;}
+       .st4{fill:#D02227;}
+</style>
+<g>
+       <rect x="166.2" y="5.5" class="st0" width="93" height="5"/>
+       <path class="st1" d="M258.8,6v4h-92V6H258.8 M259.8,5h-94v6h94V5L259.8,5z"/>
+</g>
+<polygon class="st1" points="86.3,8 87.6,8 154.4,8 155.8,8 158,14 152.2,14 89.8,14 84,14 "/>
+<polygon class="st1" points="7.5,8 8.8,8 75.7,8 77,8 79.3,14 73.4,14 11.1,14 5.2,14 "/>
+<line class="st2" x1="262" y1="14.5" x2="2" y2="14.5"/>
+<line class="st2" x1="262" y1="32.8" x2="2" y2="32.8"/>
+<g>
+       <circle class="st3" cx="255.1" cy="22.6" r="3.6"/>
+       <line class="st3" x1="252.4" y1="25" x2="249" y2="28.5"/>
+</g>
+<rect x="185.5" y="19" class="st1" width="57.6" height="9.5"/>
+<rect x="190.5" y="37" class="st1" width="69.2" height="32.7"/>
+<rect x="169.7" y="72.5" class="st1" width="90.1" height="56.8"/>
+<rect x="60.4" y="72.5" class="st4" width="106.5" height="56.8"/>
+<rect x="5.2" y="72.5" class="st1" width="52.4" height="56.8"/>
+<rect x="223.3" y="132.2" class="st1" width="36.5" height="25.3"/>
+<rect x="158" y="132.2" class="st1" width="62.3" height="25.3"/>
+<rect x="136.6" y="132.2" class="st1" width="18.1" height="25.3"/>
+<rect x="76.8" y="132.2" class="st1" width="18.1" height="25.3"/>
+<rect x="56.1" y="132.2" class="st1" width="18.1" height="25.3"/>
+<rect x="5.2" y="132.2" class="st1" width="48.1" height="25.3"/>
+<rect x="97.9" y="132.2" class="st1" width="36" height="25.3"/>
+<rect x="112.3" y="37" class="st1" width="36.5" height="32.7"/>
+<rect x="151.4" y="37" class="st1" width="36.5" height="32.7"/>
+<rect x="53.4" y="37" class="st1" width="56" height="32.7"/>
+<rect x="5.2" y="37" class="st1" width="45.4" height="32.7"/>
+<path class="st0" d="M161.7,124.3H81.4l31.8-31.8l26.1,26.1l12.8-12.8l9.5,9.5L161.7,124.3z"/>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
index c6bf6fe..ed07c61 100644 (file)
@@ -1,57 +1,57 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#FFFFFF;}\r
-       .st1{fill:#E5E5E5;}\r
-       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st3{fill:#D02227;}\r
-       .st4{fill:#9B9A9A;}\r
-</style>\r
-<g>\r
-       <rect x="4.8" y="5.5" class="st0" width="93" height="5"/>\r
-       <path class="st1" d="M97.2,6v4h-92V6H97.2 M98.2,5h-94v6h94V5L98.2,5z"/>\r
-</g>\r
-<polygon class="st1" points="177.7,8 176.4,8 109.6,8 108.2,8 106,14 111.8,14 174.2,14 180,14 "/>\r
-<polygon class="st1" points="256.5,8 255.2,8 188.3,8 187,8 184.7,14 190.6,14 252.9,14 258.8,14 "/>\r
-<line class="st2" x1="2" y1="14.5" x2="262" y2="14.5"/>\r
-<rect x="44" y="48.4" class="st3" width="106.5" height="56.8"/>\r
-<path class="st0" d="M49.3,91.2l9.5-9.5l12.8,12.8l26.1-26.1l31.8,31.8H49.3L49.3,91.2z"/>\r
-<circle class="st1" cx="55" cy="32.3" r="11.9"/>\r
-<g>\r
-       <rect x="70.7" y="27.4" class="st4" width="76.2" height="7"/>\r
-       <rect x="70.7" y="35.5" class="st4" width="22.3" height="1.7"/>\r
-       <rect x="95" y="35.5" class="st4" width="28.2" height="1.7"/>\r
-</g>\r
-<rect x="44" y="107.8" class="st1" width="23.3" height="23.3"/>\r
-<rect x="70.7" y="107.8" class="st1" width="79.8" height="1.8"/>\r
-<rect x="70.7" y="114" class="st1" width="79.8" height="1.8"/>\r
-<rect x="70.7" y="120.2" class="st1" width="79.8" height="1.8"/>\r
-<rect x="70.7" y="126.4" class="st1" width="79.8" height="1.8"/>\r
-<rect x="44" y="134.4" class="st1" width="106.5" height="1.8"/>\r
-<rect x="44" y="140.6" class="st1" width="106.5" height="1.8"/>\r
-<rect x="44" y="146.8" class="st1" width="106.5" height="1.8"/>\r
-<rect x="44" y="153" class="st1" width="106.5" height="1.8"/>\r
-<g>\r
-       <rect x="155.7" y="37.2" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="43.4" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="49.6" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="55.8" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="27.4" class="st1" width="57.3" height="7"/>\r
-</g>\r
-<g>\r
-       <rect x="155.7" y="78.2" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="84.4" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="90.6" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="96.8" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="68.4" class="st1" width="57.3" height="7"/>\r
-</g>\r
-<g>\r
-       <rect x="155.7" y="116.7" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="122.9" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="129.1" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="135.3" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="155.7" y="106.9" class="st1" width="57.3" height="7"/>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+       .st1{fill:#E5E5E5;}
+       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st3{fill:#D02227;}
+       .st4{fill:#9B9A9A;}
+</style>
+<g>
+       <rect x="4.8" y="5.5" class="st0" width="93" height="5"/>
+       <path class="st1" d="M97.2,6v4h-92V6H97.2 M98.2,5h-94v6h94V5L98.2,5z"/>
+</g>
+<polygon class="st1" points="177.7,8 176.4,8 109.6,8 108.2,8 106,14 111.8,14 174.2,14 180,14 "/>
+<polygon class="st1" points="256.5,8 255.2,8 188.3,8 187,8 184.7,14 190.6,14 252.9,14 258.8,14 "/>
+<line class="st2" x1="2" y1="14.5" x2="262" y2="14.5"/>
+<rect x="44" y="48.4" class="st3" width="106.5" height="56.8"/>
+<path class="st0" d="M49.3,91.2l9.5-9.5l12.8,12.8l26.1-26.1l31.8,31.8H49.3L49.3,91.2z"/>
+<circle class="st1" cx="55" cy="32.3" r="11.9"/>
+<g>
+       <rect x="70.7" y="27.4" class="st4" width="76.2" height="7"/>
+       <rect x="70.7" y="35.5" class="st4" width="22.3" height="1.7"/>
+       <rect x="95" y="35.5" class="st4" width="28.2" height="1.7"/>
+</g>
+<rect x="44" y="107.8" class="st1" width="23.3" height="23.3"/>
+<rect x="70.7" y="107.8" class="st1" width="79.8" height="1.8"/>
+<rect x="70.7" y="114" class="st1" width="79.8" height="1.8"/>
+<rect x="70.7" y="120.2" class="st1" width="79.8" height="1.8"/>
+<rect x="70.7" y="126.4" class="st1" width="79.8" height="1.8"/>
+<rect x="44" y="134.4" class="st1" width="106.5" height="1.8"/>
+<rect x="44" y="140.6" class="st1" width="106.5" height="1.8"/>
+<rect x="44" y="146.8" class="st1" width="106.5" height="1.8"/>
+<rect x="44" y="153" class="st1" width="106.5" height="1.8"/>
+<g>
+       <rect x="155.7" y="37.2" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="43.4" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="49.6" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="55.8" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="27.4" class="st1" width="57.3" height="7"/>
+</g>
+<g>
+       <rect x="155.7" y="78.2" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="84.4" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="90.6" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="96.8" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="68.4" class="st1" width="57.3" height="7"/>
+</g>
+<g>
+       <rect x="155.7" y="116.7" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="122.9" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="129.1" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="135.3" class="st1" width="57.3" height="1.8"/>
+       <rect x="155.7" y="106.9" class="st1" width="57.3" height="7"/>
+</g>
+</svg>
index 080045a..dd8b0f0 100644 (file)
@@ -1,69 +1,69 @@
-<?xml version="1.0" encoding="utf-8"?>\r
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->\r
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"\r
-        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">\r
-<style type="text/css">\r
-       .st0{fill:#FFFFFF;}\r
-       .st1{fill:#E5E5E5;}\r
-       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}\r
-       .st3{fill:#D02227;}\r
-       .st4{fill:#9B9A9A;}\r
-</style>\r
-<g>\r
-       <rect x="166.2" y="5.5" class="st0" width="93" height="5"/>\r
-       <path class="st1" d="M258.8,6v4h-92V6H258.8 M259.8,5h-94v6h94V5L259.8,5z"/>\r
-</g>\r
-<polygon class="st1" points="86.3,8 87.6,8 154.4,8 155.8,8 158,14 152.2,14 89.8,14 84,14 "/>\r
-<polygon class="st1" points="7.5,8 8.8,8 75.7,8 77,8 79.3,14 73.4,14 11.1,14 5.2,14 "/>\r
-<line class="st2" x1="262" y1="14.5" x2="2" y2="14.5"/>\r
-<rect x="113.5" y="48.4" class="st3" width="106.5" height="56.8"/>\r
-<path class="st0" d="M214.7,100.2h-80.3l31.8-31.8l26.1,26.1l12.8-12.8l9.5,9.5L214.7,100.2z"/>\r
-<circle class="st1" cx="209" cy="32.3" r="11.9"/>\r
-<g>\r
-       <rect x="117" y="27.4" class="st4" width="76.2" height="7"/>\r
-       <rect x="171" y="35.5" class="st4" width="22.3" height="1.7"/>\r
-       <rect x="140.8" y="35.5" class="st4" width="28.2" height="1.7"/>\r
-</g>\r
-<rect x="196.7" y="107.8" class="st1" width="23.3" height="23.3"/>\r
-<rect x="113.5" y="107.8" class="st1" width="79.8" height="1.8"/>\r
-<rect x="113.5" y="114" class="st1" width="79.8" height="1.8"/>\r
-<rect x="113.5" y="120.2" class="st1" width="79.8" height="1.8"/>\r
-<rect x="113.5" y="126.4" class="st1" width="79.8" height="1.8"/>\r
-<rect x="113.5" y="134.4" class="st1" width="106.5" height="1.8"/>\r
-<rect x="113.5" y="140.6" class="st1" width="106.5" height="1.8"/>\r
-<rect x="113.5" y="146.8" class="st1" width="106.5" height="1.8"/>\r
-<rect x="113.5" y="153" class="st1" width="106.5" height="1.8"/>\r
-<g>\r
-       <rect x="51" y="37.2" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="43.4" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="49.6" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="55.8" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="27.4" class="st1" width="57.3" height="7"/>\r
-</g>\r
-<g>\r
-       <rect x="51" y="78.2" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="84.4" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="90.6" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="96.8" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="68.4" class="st1" width="57.3" height="7"/>\r
-</g>\r
-<g>\r
-       <rect x="51" y="116.7" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="122.9" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="129.1" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="135.3" class="st1" width="57.3" height="1.8"/>\r
-       <rect x="51" y="106.9" class="st1" width="57.3" height="7"/>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-<g>\r
-</g>\r
-</svg>\r
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+        viewBox="0 0 264 162" style="enable-background:new 0 0 264 162;" xml:space="preserve">
+<style type="text/css">
+       .st0{fill:#FFFFFF;}
+       .st1{fill:#E5E5E5;}
+       .st2{fill:none;stroke:#E5E5E5;stroke-miterlimit:10;}
+       .st3{fill:#D02227;}
+       .st4{fill:#9B9A9A;}
+</style>
+<g>
+       <rect x="166.2" y="5.5" class="st0" width="93" height="5"/>
+       <path class="st1" d="M258.8,6v4h-92V6H258.8 M259.8,5h-94v6h94V5L259.8,5z"/>
+</g>
+<polygon class="st1" points="86.3,8 87.6,8 154.4,8 155.8,8 158,14 152.2,14 89.8,14 84,14 "/>
+<polygon class="st1" points="7.5,8 8.8,8 75.7,8 77,8 79.3,14 73.4,14 11.1,14 5.2,14 "/>
+<line class="st2" x1="262" y1="14.5" x2="2" y2="14.5"/>
+<rect x="113.5" y="48.4" class="st3" width="106.5" height="56.8"/>
+<path class="st0" d="M214.7,100.2h-80.3l31.8-31.8l26.1,26.1l12.8-12.8l9.5,9.5L214.7,100.2z"/>
+<circle class="st1" cx="209" cy="32.3" r="11.9"/>
+<g>
+       <rect x="117" y="27.4" class="st4" width="76.2" height="7"/>
+       <rect x="171" y="35.5" class="st4" width="22.3" height="1.7"/>
+       <rect x="140.8" y="35.5" class="st4" width="28.2" height="1.7"/>
+</g>
+<rect x="196.7" y="107.8" class="st1" width="23.3" height="23.3"/>
+<rect x="113.5" y="107.8" class="st1" width="79.8" height="1.8"/>
+<rect x="113.5" y="114" class="st1" width="79.8" height="1.8"/>
+<rect x="113.5" y="120.2" class="st1" width="79.8" height="1.8"/>
+<rect x="113.5" y="126.4" class="st1" width="79.8" height="1.8"/>
+<rect x="113.5" y="134.4" class="st1" width="106.5" height="1.8"/>
+<rect x="113.5" y="140.6" class="st1" width="106.5" height="1.8"/>
+<rect x="113.5" y="146.8" class="st1" width="106.5" height="1.8"/>
+<rect x="113.5" y="153" class="st1" width="106.5" height="1.8"/>
+<g>
+       <rect x="51" y="37.2" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="43.4" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="49.6" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="55.8" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="27.4" class="st1" width="57.3" height="7"/>
+</g>
+<g>
+       <rect x="51" y="78.2" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="84.4" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="90.6" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="96.8" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="68.4" class="st1" width="57.3" height="7"/>
+</g>
+<g>
+       <rect x="51" y="116.7" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="122.9" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="129.1" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="135.3" class="st1" width="57.3" height="1.8"/>
+       <rect x="51" y="106.9" class="st1" width="57.3" height="7"/>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+<g>
+</g>
+</svg>
index aa2998b..7331df9 100644 (file)
                mw.ForeignStructuredUpload.BookletLayout.parent.prototype.initialize.call( this )
                        .done( function () {
                                // Point the CategorySelector to the right wiki
-                               this.upload.apiPromise.done( function ( api ) {
+                               this.upload.getApi().done( function ( api ) {
                                        // If this is a ForeignApi, it will have a apiUrl, otherwise we don't need to do anything
                                        if ( api.apiUrl ) {
                                                // Can't reuse the same object, CategorySelector calls #abort on its mw.Api instance
                                                this.categoriesWidget.api = new mw.ForeignApi( api.apiUrl );
                                        }
+                               }.bind( this ) ).always( function () {
                                        deferred.resolve();
-                               }.bind( this ) );
+                               } );
                        }.bind( this ) );
                return deferred.promise();
        };
                        notOwnWorkLocal = mw.message( 'foreign-structured-upload-form-label-not-own-work-local-default' );
                }
 
-               $ownWorkMessage = $( '<p>' ).html( ownWorkMessage.parse() )
+               $ownWorkMessage = $( '<p>' ).append( ownWorkMessage.parseDom() )
                        .addClass( 'mw-foreignStructuredUpload-bookletLayout-license' );
                $notOwnWorkMessage = $( '<div>' ).append(
-                       $( '<p>' ).html( notOwnWorkMessage.parse() ),
-                       $( '<p>' ).html( notOwnWorkLocal.parse() )
+                       $( '<p>' ).append( notOwnWorkMessage.parseDom() ),
+                       $( '<p>' ).append( notOwnWorkLocal.parseDom() )
                );
                $ownWorkMessage.add( $notOwnWorkMessage ).find( 'a' )
                        .attr( 'target', '_blank' )
                this.selectFileWidget.on( 'change', onUploadFormChange.bind( this ) );
                this.ownWorkCheckbox.on( 'change', onUploadFormChange.bind( this ) );
 
+               this.selectFileWidget.on( 'change', function () {
+                       var file = layout.getFile();
+
+                       // Set the date to lastModified once we have the file
+                       if ( layout.getDateFromLastModified( file ) !== undefined ) {
+                               layout.dateWidget.setValue( layout.getDateFromLastModified( file ) );
+                       }
+
+                       // Check if we have EXIF data and set to that where available
+                       layout.getDateFromExif( file ).done( function ( date ) {
+                               layout.dateWidget.setValue( date );
+                       } );
+               } );
+
                return this.uploadForm;
        };
 
                        multiline: true,
                        autosize: true
                } );
-               this.dateWidget = new mw.widgets.DateInputWidget( {
-                       $overlay: this.$overlay,
-                       required: true,
-                       mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
-               } );
                this.categoriesWidget = new mw.widgets.CategorySelector( {
                        // Can't be done here because we don't know the target wiki yet... done in #initialize.
                        // api: new mw.ForeignApi( ... ),
                        $overlay: this.$overlay
                } );
+               this.dateWidget = new mw.widgets.DateInputWidget( {
+                       $overlay: this.$overlay,
+                       required: true,
+                       mustBeBefore: moment().add( 1, 'day' ).locale( 'en' ).format( 'YYYY-MM-DD' ) // Tomorrow
+               } );
 
                fieldset = new OO.ui.FieldsetLayout( {
                        label: mw.msg( 'upload-form-label-infoform-title' )
                fieldset.addItems( [
                        new OO.ui.FieldLayout( this.filenameWidget, {
                                label: mw.msg( 'upload-form-label-infoform-name' ),
-                               align: 'top'
+                               align: 'top',
+                               help: mw.msg( 'upload-form-label-infoform-name-tooltip' )
                        } ),
                        new OO.ui.FieldLayout( this.descriptionWidget, {
                                label: mw.msg( 'upload-form-label-infoform-description' ),
-                               align: 'top'
+                               align: 'top',
+                               help: mw.msg( 'upload-form-label-infoform-description-tooltip' )
                        } ),
                        new OO.ui.FieldLayout( this.categoriesWidget, {
                                label: mw.msg( 'foreign-structured-upload-form-label-infoform-categories' ),
                return this.upload.getText();
        };
 
+       /**
+        * Get original date from EXIF data
+        *
+        * @param {Object} file
+        * @return {jQuery.Promise} Promise resolved with the EXIF date
+        */
+       mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromExif = function ( file ) {
+               var fileReader,
+                       deferred = $.Deferred();
+
+               if ( file && file.type === 'image/jpeg' ) {
+                       fileReader = new FileReader();
+                       fileReader.onload = function () {
+                               var fileStr, arr, i, metadata;
+
+                               if ( typeof fileReader.result === 'string' ) {
+                                       fileStr = fileReader.result;
+                               } else {
+                                       // Array buffer; convert to binary string for the library.
+                                       arr = new Uint8Array( fileReader.result );
+                                       fileStr = '';
+                                       for ( i = 0; i < arr.byteLength; i++ ) {
+                                               fileStr += String.fromCharCode( arr[ i ] );
+                                       }
+                               }
+
+                               try {
+                                       metadata = mw.libs.jpegmeta( this.result, file.name );
+                               } catch ( e ) {
+                                       metadata = null;
+                               }
+
+                               if ( metadata !== null && metadata.exif !== undefined && metadata.exif.DateTimeOriginal ) {
+                                       deferred.resolve( moment( metadata.exif.DateTimeOriginal, 'YYYY:MM:DD' ).format( 'YYYY-MM-DD' ) );
+                               } else {
+                                       deferred.reject();
+                               }
+                       };
+
+                       if ( 'readAsBinaryString' in fileReader ) {
+                               fileReader.readAsBinaryString( file );
+                       } else if ( 'readAsArrayBuffer' in fileReader ) {
+                               fileReader.readAsArrayBuffer( file );
+                       } else {
+                               // We should never get here
+                               deferred.reject();
+                               throw new Error( 'Cannot read thumbnail as binary string or array buffer.' );
+                       }
+               }
+
+               return deferred.promise();
+       };
+
+       /**
+        * Get last modified date from file
+        *
+        * @param {Object} file
+        * @return {Object} Last modified date from file
+        */
+       mw.ForeignStructuredUpload.BookletLayout.prototype.getDateFromLastModified = function ( file ) {
+               if ( file && file.lastModified ) {
+                       return moment( file.lastModified ).format( 'YYYY-MM-DD' );
+               }
+       };
+
        /* Setters */
 
        /**
index 61fb59f..1a0b59a 100644 (file)
                if ( this.target === 'local' ) {
                        // If local uploads were requested, but they are disabled, fail.
                        if ( !mw.config.get( 'wgEnableUploads' ) ) {
-                               throw new Error( 'Local uploads are disabled' );
+                               this.apiPromise = $.Deferred().reject( 'uploaddisabledtext' );
+                       } else {
+                               // We'll ignore the CORS and centralauth stuff if the target is
+                               // the local wiki.
+                               this.apiPromise = $.Deferred().resolve( new mw.Api( apiconfig ) );
                        }
-                       // We'll ignore the CORS and centralauth stuff if the target is
-                       // the local wiki.
-                       this.apiPromise = $.Deferred().resolve( new mw.Api( apiconfig ) );
                } else {
                        api = new mw.Api();
                        this.apiPromise = api.get( {
@@ -76,7 +77,7 @@
                                        }
                                }
 
-                               throw new Error( 'Can not upload to requested foreign repo' );
+                               return $.Deferred().reject( 'upload-foreign-cant-upload' );
                        } );
                }
 
         * or to local uploads if no foreign target is configured.
         */
 
+       /**
+        * @inheritdoc
+        */
+       ForeignUpload.prototype.getApi = function () {
+               return this.apiPromise;
+       };
+
        /**
         * Override from mw.Upload to make sure the API info is found and allowed
         */
index 47250ee..ec2a4b1 100644 (file)
@@ -6,6 +6,7 @@
 ( function ( mw, $ ) {
        /*jshint latedef:false */
 
+       // jscs:disable jsDoc
        /**
         * @class mw.Title
         *
         * logic directly and get null for invalid titles which is easier to work with.
         *
         * @constructor
+        *
+        * Note that in the constructor amd #newFromText method, `namespace` is the **default** namespace
+        * only, and can be overridden by a namespace prefix in `title`. If you do not want this behavior,
+        * use #makeTitle. Compare:
+        *
+        *     new mw.Title( 'Foo', NS_TEMPLATE ).getPrefixedText();                  // => 'Template:Foo'
+        *     mw.Title.newFromText( 'Foo', NS_TEMPLATE ).getPrefixedText();          // => 'Template:Foo'
+        *     mw.Title.makeTitle( NS_TEMPLATE, 'Foo' ).getPrefixedText();            // => 'Template:Foo'
+        *
+        *     new mw.Title( 'Category:Foo', NS_TEMPLATE ).getPrefixedText();         // => 'Category:Foo'
+        *     mw.Title.newFromText( 'Category:Foo', NS_TEMPLATE ).getPrefixedText(); // => 'Category:Foo'
+        *     mw.Title.makeTitle( NS_TEMPLATE, 'Category:Foo' ).getPrefixedText();   // => 'Template:Category:Foo'
+        *
+        *     new mw.Title( 'Template:Foo', NS_TEMPLATE ).getPrefixedText();         // => 'Template:Foo'
+        *     mw.Title.newFromText( 'Template:Foo', NS_TEMPLATE ).getPrefixedText(); // => 'Template:Foo'
+        *     mw.Title.makeTitle( NS_TEMPLATE, 'Template:Foo' ).getPrefixedText();   // => 'Template:Template:Foo'
+        *
         * @param {string} title Title of the page. If no second argument given,
         *  this will be searched for a namespace
         * @param {number} [namespace=NS_MAIN] If given, will used as default namespace for the given title
@@ -32,6 +50,7 @@
 
                return this;
        }
+       // jscs:enable jsDoc
 
        /* Private members */
 
                return id;
        },
 
+       /**
+        * @private
+        * @method getNamespacePrefix_
+        * @param {number} namespace
+        * @return {string}
+        */
+       getNamespacePrefix = function ( namespace ) {
+               return namespace === NS_MAIN ?
+                       '' :
+                       ( mw.config.get( 'wgFormattedNamespaces' )[ namespace ].replace( / /g, '_' ) + ':' );
+       },
+
        rUnderscoreTrim = /^_+|_+$/g,
 
        rSplit = /^(.+?)_*:_*(.*)$/,
        ],
 
        /**
-        * Internal helper for #constructor and #newFromtext.
+        * Internal helper for #constructor and #newFromText.
         *
         * Based on Title.php#secureAndSplit
         *
        /**
         * Constructor for Title objects with a null return instead of an exception for invalid titles.
         *
+        * Note that `namespace` is the **default** namespace only, and can be overridden by a namespace
+        * prefix in `title`. If you do not want this behavior, use #makeTitle. See #constructor for
+        * details.
+        *
         * @static
         * @param {string} title
         * @param {number} [namespace=NS_MAIN] Default namespace
                return t;
        };
 
+       /**
+        * Constructor for Title objects with predefined namespace.
+        *
+        * Unlike #newFromText or #constructor, this function doesn't allow the given `namespace` to be
+        * overridden by a namespace prefix in `title`. See #constructor for details about this behavior.
+        *
+        * The single exception to this is when `namespace` is 0, indicating the main namespace. The
+        * function behaves like #newFromText in that case.
+        *
+        * @static
+        * @param {number} namespace Namespace to use for the title
+        * @param {string} title
+        * @return {mw.Title|null} A valid Title object or null if the title is invalid
+        */
+       Title.makeTitle = function ( namespace, title ) {
+               return mw.Title.newFromText( getNamespacePrefix( namespace ) + title );
+       };
+
        /**
         * Constructor for Title objects from user input altering that input to
         * produce a title that MediaWiki will accept as legal
                 * @return {string}
                 */
                getNamespacePrefix: function () {
-                       return this.namespace === NS_MAIN ?
-                               '' :
-                               ( mw.config.get( 'wgFormattedNamespaces' )[ this.namespace ].replace( / /g, '_' ) + ':' );
+                       return getNamespacePrefix( this.namespace );
                },
 
                /**
                 * @return {string}
                 */
                getUrl: function ( params ) {
-                       return mw.util.getUrl( this.toString(), params );
+                       var fragment = this.getFragment();
+                       if ( fragment ) {
+                               return mw.util.getUrl( this.toString() + '#' + this.getFragment(), params );
+                       } else {
+                               return mw.util.getUrl( this.toString(), params );
+                       }
                },
 
                /**
index 2ef3e87..4038228 100644 (file)
         */
        mw.Upload.BookletLayout.prototype.initialize = function () {
                var
-                       apiPromise,
                        booklet = this,
                        deferred = $.Deferred();
 
                this.upload = this.createUpload();
                this.setPage( 'upload' );
 
-               apiPromise = this.upload.apiPromise || $.Deferred().resolve( this.upload.api );
-               apiPromise.done( function ( api ) {
+               this.upload.getApi().done( function ( api ) {
                        // If the user can't upload anything, don't give them the option to.
                        api.getUserInfo().done( function ( userInfo ) {
                                if ( userInfo.rights.indexOf( 'upload' ) === -1 ) {
                        } ).always( function () {
                                deferred.resolve();
                        } );
+               } ).fail( function ( errorMsg ) {
+                       booklet.getPage( 'upload' ).$element.msg( errorMsg );
+                       deferred.resolve();
                } );
 
                return deferred.promise();
                        layout = this,
                        file = this.getFile();
 
-               this.filenameWidget.setValue( file.name );
+               this.setFilename( file.name );
+
                this.setPage( 'info' );
 
                if ( this.shouldRecordBucket ) {
                }
 
                this.upload.setFile( file );
-               // Explicitly set the filename so that the old filename isn't used in case of retry
-               this.upload.setFilenameFromFile();
+               // The original file name might contain invalid characters, so use our sanitized one
+               this.upload.setFilename( this.getFilename() );
 
                this.uploadPromise = this.upload.uploadToStash();
                this.uploadPromise.then( function () {
                        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(
-                                       $( '<p>' ).html(
-                                               // HACK TitleBlacklist doesn't have a sensible message, this one is from UploadWizard
-                                               mw.message( 'api-error-blacklisted' ).parse()
-                                       ),
+                                       // HACK TitleBlacklist doesn't have a sensible message, this one is from UploadWizard
+                                       $( '<p>' ).msg( 'api-error-blacklisted' ),
                                        { recoverable: false }
                                );
                        }
                                message = mw.message( 'api-error-unknownerror', JSON.stringify( stateDetails ) );
                        }
                        return new OO.ui.Error(
-                               $( '<p>' ).html(
-                                       message.parse()
-                               ),
+                               $( '<p>' ).append( message.parseDom() ),
                                { 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.filenameWidget.setValue( warnings.badfilename );
+                               this.setFilename( warnings.badfilename );
                                return new OO.ui.Error(
                                        $( '<p>' ).msg( 'badfilename', warnings.badfilename )
                                );
                        } else {
                                return new OO.ui.Error(
-                                       $( '<p>' ).html(
-                                               // Let's get all the help we can if we can't pin point the error
-                                               mw.message( 'api-error-unknown-warning', JSON.stringify( stateDetails ) ).parse()
-                                       ),
+                                       // 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 }
                                );
                        }
                fieldset.addItems( [
                        new OO.ui.FieldLayout( this.filenameWidget, {
                                label: mw.msg( 'upload-form-label-infoform-name' ),
-                               align: 'top'
+                               align: 'top',
+                               help: mw.msg( 'upload-form-label-infoform-name-tooltip' )
                        } ),
                        new OO.ui.FieldLayout( this.descriptionWidget, {
                                label: mw.msg( 'upload-form-label-infoform-description' ),
-                               align: 'top'
+                               align: 'top',
+                               help: mw.msg( 'upload-form-label-infoform-description-tooltip' )
                        } )
                ] );
                this.infoForm = new OO.ui.FormLayout( { items: [ fieldset ] } );
         * @return {string}
         */
        mw.Upload.BookletLayout.prototype.getFilename = function () {
-               return this.filenameWidget.getValue();
+               var filename = this.filenameWidget.getValue();
+               if ( this.filenameExtension ) {
+                       filename += '.' + this.filenameExtension;
+               }
+               return filename;
+       };
+
+       /**
+        * Prefills the {@link #infoForm information form} with the given filename.
+        *
+        * @protected
+        * @param {string} filename
+        */
+       mw.Upload.BookletLayout.prototype.setFilename = function ( filename ) {
+               var title = mw.Title.newFromFileName( filename );
+
+               if ( title ) {
+                       this.filenameWidget.setValue( title.getNameText() );
+                       this.filenameExtension = mw.Title.normalizeExtension( title.getExtension() );
+               } else {
+                       // Seems to happen for files with no extension, which should fail some checks anyway...
+                       this.filenameWidget.setValue( filename );
+                       this.filenameExtension = null;
+               }
        };
 
        /**
index d80b4eb..8a74ffc 100644 (file)
 
        UP = Upload.prototype;
 
+       /**
+        * Get the mw.Api instance used by this Upload object.
+        *
+        * @return {jQuery.Promise}
+        * @return {Function} return.done
+        * @return {mw.Api} return.done.api
+        */
+       UP.getApi = function () {
+               return $.Deferred().resolve( this.api ).promise();
+       };
+
        /**
         * Set the text of the file page, to be created on file upload.
         *
diff --git a/resources/src/mediawiki/mediawiki.checkboxtoggle.css b/resources/src/mediawiki/mediawiki.checkboxtoggle.css
new file mode 100644 (file)
index 0000000..3da0d43
--- /dev/null
@@ -0,0 +1,3 @@
+.client-nojs .mw-checkbox-toggle-controls {
+       display: none;
+}
diff --git a/resources/src/mediawiki/mediawiki.checkboxtoggle.js b/resources/src/mediawiki/mediawiki.checkboxtoggle.js
new file mode 100644 (file)
index 0000000..4be4a8d
--- /dev/null
@@ -0,0 +1,37 @@
+/*!
+ * Allows users to perform all / none / invert operations on a list of
+ * checkboxes on the page.
+ *
+ * @licence GNU GPL v2+
+ * @author Luke Faraone <luke at faraone dot cc>
+ *
+ * Based on ext.nuke.js from https://www.mediawiki.org/wiki/Extension:Nuke by
+ * Jeroen De Dauw <jeroendedauw at gmail dot com>
+ */
+
+( function ( mw, $ ) {
+       'use strict';
+
+       var $checkboxes = $( 'li input[type=checkbox]' );
+
+       function selectAll( check ) {
+               $checkboxes.prop( 'checked', check );
+       }
+
+       $( '#checkbox-all' ).click( function ( e ) {
+               selectAll( true );
+               e.preventDefault();
+       } );
+       $( '#checkbox-none' ).click( function ( e ) {
+               selectAll( false );
+               e.preventDefault();
+       } );
+       $( '#checkbox-invert' ).click( function ( e ) {
+               $checkboxes.each( function () {
+                       $( this ).prop( 'checked', !$( this ).is( ':checked' ) );
+               } );
+               e.preventDefault();
+       } );
+
+}( mediaWiki, jQuery ) );
+
index 7afb9d3..97a94b8 100644 (file)
                        classes: [ 'mw-feedbackDialog-welcome-message' ]
                } );
                this.feedbackSubjectInput = new OO.ui.TextInputWidget( {
+                       indicator: 'required',
                        multiline: false
                } );
                this.feedbackMessageInput = new OO.ui.TextInputWidget( {
                                        !this.useragentMandatory ||
                                        this.useragentCheckbox.isSelected()
                                ) &&
-                               (
-                                       !!this.feedbackMessageInput.getValue() ||
-                                       !!this.feedbackSubjectInput.getValue()
-                               )
+                               this.feedbackSubjectInput.getValue()
                        );
 
                this.actions.setAbilities( { submit:  isValid } );
diff --git a/resources/src/mediawiki/mediawiki.hlist.js b/resources/src/mediawiki/mediawiki.hlist.js
deleted file mode 100644 (file)
index 8ba57f6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-/*!
- * .hlist fallbacks for IE 8.
- * @author [[User:Edokter]]
- */
-( function ( mw, $ ) {
-       var profile = $.client.profile();
-
-       if ( profile.name === 'msie' && profile.versionNumber === 8 ) {
-               /* Add pseudo-selector class to last-child list items */
-               mw.hook( 'wikipage.content' ).add( function ( $content ) {
-                       $content.find( '.hlist' ).find( 'dd:last-child, dt:last-child, li:last-child' )
-                               .addClass( 'hlist-last-child' );
-               } );
-       }
-}( mediaWiki, jQuery ) );
index 309eb34..260fd37 100644 (file)
@@ -1,7 +1,6 @@
 /* OOUIHTMLForm styles */
 
 .mw-htmlform-ooui-wrapper {
-       width: 50em;
        margin: 1em 0;
 }
 
@@ -29,3 +28,7 @@
        margin-right: 5%;
        width: 39%;
 }
+
+.oo-ui-fieldLayout .oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
+  margin-bottom: 0;
+}
index 514a3dd..671f38f 100644 (file)
@@ -20,7 +20,7 @@
        function humanSize( bytes ) {
                if ( !$.isNumeric( bytes ) || bytes === 0 ) { return bytes; }
                var i = 0,
-                       units = [ '', ' kB', ' MB', ' GB', ' TB', ' PB' ];
+                       units = [ '', ' KiB', ' MiB', ' GiB', ' TiB', ' PiB' ];
 
                for ( ; bytes >= 1024; bytes /= 1024 ) { i++; }
                // Maintain one decimal for kB and above, but don't
                 */
                auditSelectors: function ( css ) {
                        var selectors = { total: 0, matched: 0 },
-                               style = document.createElement( 'style' ),
-                               sheet, rules;
+                               style = document.createElement( 'style' );
 
                        style.textContent = css;
                        document.body.appendChild( style );
-                       // Standards-compliant browsers use .sheet.cssRules, IE8 uses .styleSheet.rules…
-                       sheet = style.sheet || style.styleSheet;
-                       rules = sheet.cssRules || sheet.rules;
-                       $.each( rules, function ( index, rule ) {
+                       $.each( style.sheet.cssRules, function ( index, rule ) {
                                selectors.total++;
                                // document.querySelector() on prefixed pseudo-elements can throw exceptions
                                // in Firefox and Safari. Ignore these exceptions.
index c25e327..b04e01c 100644 (file)
                                obj[ key ] = val;
                        } : function ( obj, key, val, msg ) {
                                msg = 'Use of "' + key + '" is deprecated.' + ( msg ? ( ' ' + msg ) : '' );
-                               // Support: IE8
-                               // Can throw on Object.defineProperty.
-                               try {
-                                       Object.defineProperty( obj, key, {
-                                               configurable: true,
-                                               enumerable: true,
-                                               get: function () {
-                                                       mw.track( 'mw.deprecate', key );
-                                                       mw.log.warn( msg );
-                                                       return val;
-                                               },
-                                               set: function ( newVal ) {
-                                                       mw.track( 'mw.deprecate', key );
-                                                       mw.log.warn( msg );
-                                                       val = newVal;
-                                               }
-                                       } );
-                               } catch ( err ) {
-                                       // Fallback to creating a copy of the value to the object.
-                                       obj[ key ] = val;
-                               }
+                               Object.defineProperty( obj, key, {
+                                       configurable: true,
+                                       enumerable: true,
+                                       get: function () {
+                                               mw.track( 'mw.deprecate', key );
+                                               mw.log.warn( msg );
+                                               return val;
+                                       },
+                                       set: function ( newVal ) {
+                                               mw.track( 'mw.deprecate', key );
+                                               mw.log.warn( msg );
+                                               val = newVal;
+                                       }
+                               } );
+
                        };
 
                        return log;
                                        // Whether the store is in use on this page.
                                        enabled: null,
 
-                                       // Modules whose string representation exceeds 100 kB are ineligible
-                                       // for storage due to bug T66721.
-                                       MODULE_SIZE_MAX: 100000,
+                                       // Modules whose string representation exceeds 100 kB (30 kB on FF) are
+                                       // ineligible for storage due to bug T66721. The quota is stricter on
+                                       // Firefox due to <https://bugzilla.mozilla.org/show_bug.cgi?id=1064466>.
+                                       MODULE_SIZE_MAX: ( /Firefox/.test( navigator.userAgent ) ? 30 : 100 ) * 1000,
 
                                        // The contents of the store, mapping '[module name]@[version]' keys
                                        // to module implementations.
index 624986a..7f62256 100644 (file)
@@ -4,8 +4,27 @@
        mw.template.registerCompiler( 'mustache', {
                compile: function ( src ) {
                        return {
-                               render: function ( data ) {
-                                       return $( $.parseHTML( Mustache.render( src, data ) ) );
+                               /**
+                                * @ignore
+                                * @return {string} The raw source code of the template
+                                */
+                               getSource: function () {
+                                       return src;
+                               },
+                               /**
+                                * @ignore
+                                * @param {Object} data Data to render
+                                * @param {Object} partialTemplates Map partial names to Mustache template objects
+                                *  returned by mw.template.get()
+                                */
+                               render: function ( data, partialTemplates ) {
+                                       var partials = {};
+                                       if ( partialTemplates ) {
+                                               $.each( partialTemplates, function ( name, template ) {
+                                                       partials[ name ] = template.getSource();
+                                               } );
+                                       }
+                                       return $( $.parseHTML( Mustache.render( src, data, partials ) ) );
                                }
                        };
                }
index 9505bdd..9b3458b 100644 (file)
@@ -36,7 +36,7 @@
 
        // Things outside the wikipage content
        $( function () {
-               var $nodes;
+               var $nodes, $oouiNodes;
 
                if ( !supportsPlaceholder ) {
                        // Exclude content to avoid hitting it twice for the (first) wikipage content
                $nodes.updateTooltipAccessKeys();
 
                // Infuse OOUI widgets, if any are present
-               $nodes = $( '[data-ooui]' );
-               if ( $nodes.length ) {
+               $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' ] ).done( function () {
-                               $nodes.each( function () {
+                               $oouiNodes.each( function () {
                                        OO.ui.infuse( this );
                                } );
                        } );
                }
 
+               $nodes = $( '.catlinks[data-mw="interface"]' );
+               if ( $nodes.length ) {
+                       /**
+                        * Fired when categories are being added to the DOM
+                        *
+                        * It is encouraged to fire it before the main DOM is changed (when $content
+                        * is still detached).  However, this order is not defined either way, so you
+                        * should only rely on $content itself.
+                        *
+                        * This includes the ready event on a page load (including post-edit loads)
+                        * and when content has been previewed with LivePreview.
+                        *
+                        * @event wikipage_categories
+                        * @member mw.hook
+                        * @param {jQuery} $content The most appropriate element containing the content,
+                        *   such as .catlinks
+                        */
+                       mw.hook( 'wikipage.categories' ).fire( $nodes );
+               }
        } );
 
 }( mediaWiki, jQuery ) );
index 1a10f83..0f51a35 100644 (file)
@@ -31,8 +31,8 @@ function isCompatible( ua ) {
 
        // Browsers with outdated or limited JavaScript engines get the no-JS experience
        return !(
-               // Internet Explorer < 8
-               ( ua.indexOf( 'MSIE' ) !== -1 && parseFloat( ua.split( 'MSIE' )[ 1 ] ) < 8 ) ||
+               // Internet Explorer < 9
+               ( ua.indexOf( 'MSIE' ) !== -1 && parseFloat( ua.split( 'MSIE' )[ 1 ] ) < 9 ) ||
                // Firefox < 3
                ( ua.indexOf( 'Firefox/' ) !== -1 && parseFloat( ua.split( 'Firefox/' )[ 1 ] ) < 3 ) ||
                // Opera < 12
index 1c48515..05f454c 100644 (file)
@@ -48,7 +48,9 @@ $wgAutoloadClasses += array(
        '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",
@@ -85,6 +87,9 @@ $wgAutoloadClasses += array(
        # 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",
 
@@ -94,6 +99,10 @@ $wgAutoloadClasses += array(
        '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",
 
@@ -118,6 +127,9 @@ $wgAutoloadClasses += array(
        'MockSvgHandler' => "$testDir/phpunit/mocks/media/MockSvgHandler.php",
        'MockDjVuHandler' => "$testDir/phpunit/mocks/media/MockDjVuHandler.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",
index b91a5bc..6a95205 100644 (file)
@@ -512,6 +512,7 @@ class ParserTest {
                        $ok = true;
 
                        foreach ( $filenames as $filename ) {
+                               echo "Running parser tests from: $filename\n";
                                $tests = new TestFileIterator( $filename, $this );
                                $ok = $this->runTests( $tests ) && $ok;
                        }
@@ -962,7 +963,7 @@ class ParserTest {
                        'protected_titles', 'revision', 'text', 'pagelinks', 'imagelinks',
                        'categorylinks', 'templatelinks', 'externallinks', 'langlinks', 'iwlinks',
                        'site_stats', 'ipblocks', 'image', 'oldimage',
-                       'recentchanges', 'watchlist', 'interwiki', 'logging',
+                       'recentchanges', 'watchlist', 'interwiki', 'logging', 'log_search',
                        'querycache', 'objectcache', 'job', 'l10n_cache', 'redirect', 'querycachetwo',
                        'archive', 'user_groups', 'page_props', 'category'
                );
@@ -1099,6 +1100,19 @@ class ParserTest {
                        'fileExists' => true
                ), $this->db->timestamp( '20010115123500' ), $user );
 
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
+               $image->recordUpload2( '', 'A pretty movie', 'Will it play', array(
+                       'size' => 12345,
+                       'width' => 320,
+                       'height' => 240,
+                       'bits' => 0,
+                       'media_type' => MEDIATYPE_VIDEO,
+                       'mime' => 'application/ogg',
+                       'metadata' => serialize( array() ),
+                       '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', array(
@@ -1212,6 +1226,8 @@ class ParserTest {
                        ' 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" );
 
                return;
        }
@@ -1253,6 +1269,14 @@ class ParserTest {
                                "$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",
                        )
                );
 
@@ -1270,10 +1294,14 @@ class ParserTest {
                                "$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",
index a0e0b3a..438fe31 100644 (file)
@@ -14,6 +14,7 @@
 # Plus any combination of these:
 #
 # cat           add category links
+#               (ignored by Parsoid, since it emits <link>s)
 # ill           add inter-language links
 #               (ignored by Parsoid, since it emits <link>s)
 # subpage       enable subpages (disabled by default)
@@ -1335,6 +1336,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tåg" data-mw='{"name":"tåg","attrs":{},"body":{"extsrc":"tåg"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -2517,7 +2520,6 @@ Barack Obama <President> of the United States
 </p>
 !! end
 
-## PHP parser discards the "<pre " string
 !! test
 Handle broken pre-like tags (bug 64025)
 !! options
@@ -2528,8 +2530,13 @@ parsoid=wt2html
 <table><pre </table>
 !! html/php
 <pre>x</pre>
-<table><pre></pre></table>
+<table>&lt;pre </table>
 
+!! html/php+tidy
+<pre>
+x
+</pre>
+<p>&lt;pre</p>
 !! html/parsoid
 <pre about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"a":{"&lt;pre":null},"sa":{"&lt;pre":""},"stx":"html","pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;pre &lt;pre>x&lt;/pre>"}},"i":0}}]}'>x</pre>
 
@@ -3329,14 +3336,18 @@ parsoid=wt2html,wt2wt
 <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":" [[Category:foo]]"}},"i":0}}]}'> </span><link rel="mw:PageProp/Category" href="./Category:Foo" about="#mwt1"> <!-- No pre&#x2D;wrapping -->
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 7b. Indent-pre and category links
 !! options
-parsoid=wt2html,wt2wt
+parsoid=wt2html
 !! wikitext
  [[Category:foo]] a
  [[Category:foo]] {{echo|b}}
-!! html
+!! html/parsoid
 <pre><link rel="mw:PageProp/Category" href="./Category:Foo"> a
  <link rel="mw:PageProp/Category" href="./Category:Foo"> <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"b"}},"i":0}}]}'>b</span></pre>
 !! end
@@ -7119,6 +7130,17 @@ Piped link with multiple pipe characters in link text
 <p><a rel="mw:WikiLink" href="Main_Page" title="Main Page">|The|Main|Page|</a></p>
 !! end
 
+!! test
+Piped link with no link text
+!! wikitext
+[[Thomas Bek (bishop of St David's)|]]
+!! html/php
+<p>[[Thomas Bek (bishop of St David's)|]]
+</p>
+!! html/parsoid
+<p>[[Thomas Bek (bishop of St David's)|]]</p>
+!! end
+
 !! test
 Broken link
 !! wikitext
@@ -9651,7 +9673,7 @@ Magic Word: {{NUMBEROFFILES}}
 !! wikitext
 {{NUMBEROFFILES}}
 !! html
-<p>5
+<p>6
 </p>
 !! end
 
@@ -10858,10 +10880,18 @@ Un-closed <includeonly>
 !! wikitext
 <includeonly>
 !! html
+<p>&lt;includeonly&gt;
+</p>
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize the include directives to serialize on their own line.
+## Selser will take care of preserving formatting in scenarios where they
+## intermingled with other wikitext.
 !! test
 Includes and comments at SOL
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <!-- comment --><noinclude><!-- comment --></noinclude><!-- comment -->== hu ==
 
@@ -11042,10 +11072,14 @@ parsoid=wt2html,wt2wt
 </tbody></table>
 !!end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize the include directives to serialize on their own line.
+## Selser will take care of preserving formatting in scenarios where they
+## intermingled with other wikitext.
 !!test
 2. Table tag in SOL posn. should get reparsed correctly with valid TSR
 !!options
-parsoid=wt2html,wt2wt
+parsoid=wt2html
 !!wikitext
 <includeonly>a</includeonly>{| {{{b}}}
 |c
@@ -14245,7 +14279,7 @@ cat
 pst
 !! wikitext
 [[Category:MediaWiki User's Guide|]]
-!! html
+!! html/php
 [[Category:MediaWiki User's Guide|MediaWiki User's Guide]]
 !! end
 
@@ -14256,19 +14290,26 @@ cat
 pst
 !! wikitext
 [[Category:Foo (bar)|]]
-!! html
+!! html/php
 [[Category:Foo (bar)|Foo]]
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 Category with link tail
 !! options
 cat
 pst
+parsoid=wt2html
 !! wikitext
 123[[Category:Foo]]456
-!! html
+!! html/php
 123[[Category:Foo]]456
+!! html/parsoid
+<p>123<link rel="mw:PageProp/Category" href="Category:Foo"/>456</p>
 !! end
 
 !! test
@@ -14278,7 +14319,7 @@ cat
 pst
 !! wikitext
 [[Category:{{echo|Foo}}]]
-!! html
+!! html/php
 [[Category:{{echo|Foo}}]]
 !! end
 
@@ -14289,7 +14330,7 @@ cat
 pst
 !! wikitext
 [[Category:Foo|{{echo|Bar}}]]
-!! html
+!! html/php
 [[Category:Foo|{{echo|Bar}}]]
 !! end
 
@@ -14300,12 +14341,18 @@ cat
 pst
 !! wikitext
 [[Category:{{echo|Foo}}|{{echo|Bar}}]]
-!! html
+!! html/php
 [[Category:{{echo|Foo}}|{{echo|Bar}}]]
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 Category / paragraph interactions
+!! options
+parsoid=wt2html
 !! wikitext
 Foo [[Category:Baz]] Bar
 
@@ -14332,7 +14379,7 @@ Bar
 [[Category:Baz]]
  {{echo|[[Category:Baz]]}}
 [[Category:Baz]]
-!! html
+!! html/php
 <p>Foo Bar
 </p><p>Foo
 Bar
@@ -14342,20 +14389,32 @@ Bar
 </p><p>Foo
 Bar
 </p>
+!! html/parsoid
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar</p>
+<p>Foo <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> Bar <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz"/> <link rel="mw:PageProp/Category" href="Category:Baz" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"[[Category:Baz]]"}},"i":0}}]}'/></p>
+<link rel="mw:PageProp/Category" href="Category:Baz"/>
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
+##
 ## The whitespace on the empty line is part of the test. Please do not delete
 !! test
 1. Categories and newlines: All preceding newlines should be suppressed (courtesy bug 87)
 !! options
-parsoid=wt2html,wt2wt
+parsoid=wt2html
 !! wikitext
 This
    
 [[Category:Foo]] and this should be part of same paragraph (not an indent-pre)
    
 {{echo|[[Category:Foo]] and so should this!}}
-!! html
+!! html/php
 <p>This and this should be part of same paragraph (not an indent-pre) and so should this!
 </p>
 !! html/parsoid
@@ -14453,8 +14512,14 @@ parsoid=wt2html
 <link rel="mw:PageProp/Category" href="./Category:Foo" data-parsoid='{"stx":"simple","a":{"href":"./Category:Foo"},"sa":{"href":"Category:Foo"}}'/>
 !! end
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 6. Categories and newlines: migrateTrailingCategories dom pass should not migrate categories not preceded by newlines
+!! options
+parsoid=wt2html
 !! wikitext
 * a [[Category:Foo]]
 !! html/parsoid
@@ -14505,13 +14570,20 @@ parsoid
 </p>
 !! end
 
-# html2wt localizes the "Category" namespace.
-# XXX the <link> element needs an empty data-parsoid attribute, or
-# else the html2html test fails because spaces are inserted.
+# We used to, but no longer wt2wt this test since the default serializer
+# will normalize all categories to serialize on their own line.
+# This wikitext usage is going to be fairly uncommon in production and
+# selser will take care of preventing whitespace insertion if this
+# occurs in an article.
+#
+# html2html disabled for the same reason (whitespace insertion between
+# x and y).
+#
+# html2wt disabled because it localizes the "Category" namespace.
 !! test
 Link prefix/suffixes aren't applied to category links
 !! options
-parsoid=wt2html,wt2wt,html2html
+parsoid=wt2html
 language=is
 !! wikitext
 x[[Category:Foo]]y
@@ -16152,10 +16224,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: empty input using terminated empty elements
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <tag/>
 !! html/php
@@ -16165,6 +16242,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":null}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -16178,6 +16257,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":null}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -16191,11 +16272,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"input"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
-
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: case insensitive
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <TAG>input</TAG>
 !! html/php
@@ -16205,11 +16290,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"input"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
-
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: case insensitive, redux
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <TaG>input</TAg>
 !! html/php
@@ -16219,6 +16308,8 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"input"}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
@@ -16234,11 +16325,35 @@ array (
 )
 </pre>&lt;/tag&gt;
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{},"body":{"extsrc":"&lt;tag>"}}' data-parsoid='{}' about="#mwt2"></pre>&lt;/tag>
 !! end
 
 !! test
 Parser hook: basic arguments
 !! wikitext
+<tag width="200" height="100" depth="50" square=""></tag>
+!! html/php
+<pre>
+''
+array (
+  'width' => '200',
+  'height' => '100',
+  'depth' => '50',
+  'square' => '',
+)
+</pre>
+
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"width":"200","height":"100","depth":"50","square":""},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
+!! end
+
+## Don't expect parsoid to rt this form.
+!! test
+Parser hook: basic arguments, variations
+!! options
+parsoid=wt2html,html2html
+!! wikitext
 <tag width=200 height = "100" depth = '50' square></tag>
 !! html/php
 <pre>
@@ -16251,12 +16366,14 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"width":"200","height":"100","depth":"50","square":""},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
 !! test
 Parser hook: argument containing a forward slash (bug 5344)
 !! wikitext
-<tag filename='/tmp/bla'></tag>
+<tag filename="/tmp/bla"></tag>
 !! html/php
 <pre>
 ''
@@ -16265,10 +16382,15 @@ array (
 )
 </pre>
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"filename":"/tmp/bla"},"body":{"extsrc":""}}' data-parsoid='{}' about="#mwt2"></pre>
 !! end
 
+## Don't expect parsoid to rt this form.
 !! test
 Parser hook: empty input using terminated empty elements (bug 2374)
+!! options
+parsoid=wt2html,html2html
 !! wikitext
 <tag foo=bar/>text
 !! html/php
@@ -16279,6 +16401,8 @@ array (
 )
 </pre>text
 
+!! html/parsoid
+<pre typeof="mw:Extension/tag" data-mw='{"name":"tag","attrs":{"foo":"bar"},"body":null}' data-parsoid='{}' about="#mwt2"></pre>text
 !! end
 
 # </tag> should be output literally since there is no matching tag that begins it
@@ -16311,21 +16435,28 @@ array (
 Parser hook: static parser hook not inside a comment
 !! wikitext
 <statictag>hello, world</statictag>
-<statictag action=flush/>
+
+<statictag action="flush" />
 !! html/php
-<p>hello, world
+<p><br />
+hello, world
 </p>
+!! html/parsoid
+<p><span typeof="mw:Extension/statictag" data-mw='{"name":"statictag","attrs":{},"body":{"extsrc":"hello, world"}}' data-parsoid='{}' about="#mwt2"></span></p>
+<p typeof="mw:Extension/statictag" data-mw='{"name":"statictag","attrs":{"action":"flush"},"body":null}' data-parsoid='{}' about="#mwt4">hello, world</p>
 !! end
 
-
 !! test
 Parser hook: static parser hook inside a comment
 !! wikitext
 <!-- <statictag>hello, world</statictag> -->
-<statictag action=flush/>
+<statictag action="flush" />
 !! html/php
 <p><br />
 </p>
+!! html/parsoid
+<!-- <statictag&#x3E;hello, world</statictag&#x3E; -->
+<p typeof="mw:Extension/statictag" data-mw='{"name":"statictag","attrs":{"action":"flush"},"body":null}' data-parsoid='{}' about="#mwt2"></p>
 !! end
 
 # Nested template calls; this case was broken by Parser.php rev 1.506,
@@ -19211,17 +19342,25 @@ Category:分類
 blah
 !! endarticle
 
+## We used to, but no longer wt2wt this test since the default serializer
+## will normalize all categories to serialize on their own line.
+## This wikitext usage is going to be fairly uncommon in production and
+## selser will take care of preserving formatting in those scenarios.
 !! test
 Don't convert blue categorylinks to another variant (bug 33210)
 !! options
-language=zh cat
+cat
+language=zh
+parsoid=wt2html
 !! wikitext
 [[A]][[Category:分类]]
-!! html
+!! html/php
 <a href="/wiki/Category:%E5%88%86%E7%B1%BB" title="Category:分类">分类</a>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="A" title="A">A</a></p>
+<link rel="mw:PageProp/Category" href="Category:分类"/>
 !! end
 
-
 !! test
 Stripping -{}- tags (language variants)
 !! options
@@ -19756,7 +19895,7 @@ Tildes in comments
 pst
 !! wikitext
 <!-- ~~~~ -->
-!! html
+!! html/php
 <!-- ~~~~ -->
 !! end
 
@@ -20078,7 +20217,7 @@ Edit comment with link
 comment
 !! wikitext
 I like the [[Main Page]] a lot
-!! html
+!! html/php
 I like the <a href="/wiki/Main_Page" title="Main Page">Main Page</a> a lot
 !!end
 
@@ -20088,7 +20227,7 @@ Edit comment with link and link text
 comment
 !! wikitext
 I like the [[Main Page|best pages]] a lot
-!! html
+!! html/php
 I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot
 !!end
 
@@ -20098,7 +20237,7 @@ Edit comment with link and link text with suffix
 comment
 !! wikitext
 I like the [[Main Page|best page]]s a lot
-!! html
+!! html/php
 I like the <a href="/wiki/Main_Page" title="Main Page">best pages</a> a lot
 !!end
 
@@ -20108,7 +20247,7 @@ Edit comment with section link (non-local, eg in history list)
 comment title=[[Main Page]]
 !! wikitext
 /* External links */ removed bogus entries
-!! html
+!! html/php
 <a href="/wiki/Main_Page#External_links" title="Main Page">→</a>‎<span dir="auto"><span class="autocomment">External links: </span> removed bogus entries</span>
 !!end
 
@@ -20118,7 +20257,7 @@ Edit comment with section link and text before it (non-local, eg in history list
 comment title=[[Main Page]]
 !! wikitext
 pre-comment text /* External links */ removed bogus entries
-!! html
+!! html/php
 pre-comment text <a href="/wiki/Main_Page#External_links" title="Main Page">→</a>‎<span dir="auto"><span class="autocomment">External links: </span> removed bogus entries</span>
 !!end
 
@@ -20128,7 +20267,7 @@ Edit comment with section link (local, eg in diff view)
 comment local title=[[Main Page]]
 !! wikitext
 /* External links */ removed bogus entries
-!! html
+!! html/php
 <a href="#External_links">→</a>‎<span dir="auto"><span class="autocomment">External links: </span> removed bogus entries</span>
 !!end
 
@@ -20140,7 +20279,7 @@ subpage
 title=[[Subpage test]]
 !! wikitext
 Poked at a [[/subpage]] here...
-!! html
+!! html/php
 Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">/subpage</a> here...
 !!end
 
@@ -20152,7 +20291,7 @@ subpage
 title=[[Subpage test]]
 !! wikitext
 Poked at a [[/subpage|neat little page]] here...
-!! html
+!! html/php
 Poked at a <a href="/wiki/Subpage_test/subpage" title="Subpage test/subpage">neat little page</a> here...
 !!end
 
@@ -20163,7 +20302,7 @@ comment
 title=[[Subpage test]]
 !! wikitext
 Poked at a [[/subpage]] here...
-!! html
+!! html/php
 Poked at a <a href="/index.php?title=/subpage&amp;action=edit&amp;redlink=1" class="new" title="/subpage (page does not exist)">/subpage</a> here...
 !!end
 
@@ -20175,7 +20314,7 @@ local
 title=[[Main Page]]
 !! wikitext
 [[#section]]
-!! html
+!! html/php
 <a href="#section">#section</a>
 !! end
 
@@ -20186,24 +20325,28 @@ comment
 title=[[Main Page]]
 !! wikitext
 [[#section]]
-!! html
+!! html/php
 <a href="/wiki/Main_Page#section" title="Main Page">#section</a>
 !! end
 
 !! test
 Anchor starting with underscore
+!! options
+title=[[Foo]]
 !! wikitext
 [[#_ref|One]]
-!! html
+!! html/php
 <p><a href="#_ref">One</a>
 </p>
+!! html/parsoid
+<p><a rel="mw:WikiLink" href="./Foo#_ref" data-parsoid='{"stx":"piped","a":{"href":"./Foo#_ref"},"sa":{"href":"#_ref"}}'>One</a></p>
 !! end
 
 !! test
 Id starting with underscore
 !! wikitext
 <div id="_ref"></div>
-!! html
+!! html/*
 <div id="_ref"></div>
 
 !! end
@@ -20215,7 +20358,7 @@ comment
 title=[[Main Page]]
 !! wikitext
 /* __hello__world__ */
-!! html
+!! html/php
 <a href="/wiki/Main_Page#hello_world" title="Main Page">→</a>‎<span dir="auto"><span class="autocomment">__hello__world__</span></span>
 !! end
 
@@ -20534,17 +20677,20 @@ HTML5 data attributes
 !! wikitext
 <span data-foo="bar">Baz</span>
 <p data-abc-def_hij="">Quuz</p>
-!! html
+!! html/php
 <p><span data-foo="bar">Baz</span>
 </p>
 <p data-abc-def_hij="">Quuz</p>
 
+!! html/parsoid
+<p><span data-foo="bar" data-parsoid='{"stx":"html"}'>Baz</span></p>
+<p data-abc-def_hij="" data-parsoid='{"stx":"html"}'>Quuz</p>
 !! end
 
 !! test
 Strip reserved data attributes
 !! wikitext
-<div data-mw="foo" data-parsoid="bar" data-mw-someext="baz" data-ok="fred" data-ooui="xyzzy">d</div>
+<div data-mw="foo" data-parsoid="bar" data-mw-someext="baz" data-ok="fred" data-ooui="xyzzy" data-bad:ns="ns">d</div>
 !! html
 <div data-ok="fred">d</div>
 
@@ -21386,14 +21532,12 @@ parsoid=wt2html,wt2wt
 
 !!test
 Ref: 1. ref-location should be replaced with an index span
-!!options
-parsoid
 !! wikitext
 A <ref>foo</ref>
 B <ref name="x">foo</ref>
 C <ref name="y" />
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-x_2-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-x-2"},"attrs":{"name":"x"}}'><a href="#cite_note-x-2"><span class="mw-reflink-text">[2]</span></a></span>
 C <span about="#mwt6" class="mw-ref" id="cite_ref-y_3-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"y"}}'><a href="#cite_note-y-3"><span class="mw-reflink-text">[3]</span></a></span></p>
@@ -21406,13 +21550,11 @@ C <span about="#mwt6" class="mw-ref" id="cite_ref-y_3-0" rel="dc:references" typ
 
 !!test
 Ref: 2. ref-tags with identical names should all get the same index
-!!options
-parsoid
 !! wikitext
 A <ref name="x">foo</ref>
 B <ref name="x" />
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-x_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-x-1"},"attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-x_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
@@ -21422,14 +21564,12 @@ B <span about="#mwt4" class="mw-ref" id="cite_ref-x_1-1" rel="dc:references" typ
 
 !!test
 Ref: 3. spaces in ref-names should be ignored
-!!options
-parsoid
 !! wikitext
 A <ref name="x">foo</ref>
 B <ref name=" x " />
 C <ref name= x  />
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-x_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-x-1"},"attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-x_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span>
 C <span about="#mwt6" class="mw-ref" id="cite_ref-x_1-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"x"}}'><a href="#cite_note-x-1"><span class="mw-reflink-text">[1]</span></a></span></p>
@@ -21441,12 +21581,10 @@ C <span about="#mwt6" class="mw-ref" id="cite_ref-x_1-2" rel="dc:references" typ
 # NOTE: constructor is a predefined property in JS and constructor as a ref-name can clash with it if not handled properly)
 !!test
 Ref: 4. 'constructor' should be accepted as a valid ref-name
-!!options
-parsoid
 !! wikitext
 A <ref name="constructor">foo</ref>
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-constructor_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-constructor-1"},"attrs":{"name":"constructor"}}'><a href="#cite_note-constructor-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
 <li about="#cite_note-constructor-1" id="cite_note-constructor-1"><a href="#cite_ref-constructor_1-0" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-constructor-1" class="mw-reference-text">foo</span></li>
@@ -21455,15 +21593,13 @@ A <ref name="constructor">foo</ref>
 
 !!test
 Ref: 5. body should accept generic wikitext
-!!options
-parsoid
 !! wikitext
 A <ref>
  This is a '''[[bolded link]]''' and this is a {{echo|transclusion}}
 </ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt5" data-mw='{"name":"references","attrs":{}}'>
@@ -21474,8 +21610,6 @@ A <ref>
 
 !!test
 Ref: 6. indent-pres should not be output in ref-body
-!!options
-parsoid
 !! wikitext
 A <ref>
  foo
@@ -21484,7 +21618,7 @@ A <ref>
 </ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
@@ -21497,8 +21631,6 @@ A <ref>
 
 !!test
 Ref: 7. No p-wrapping in ref-body
-!!options
-parsoid
 !! wikitext
 A <ref>
 foo
@@ -21514,7 +21646,7 @@ booz
 </ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
@@ -21534,27 +21666,23 @@ booz
 
 !!test
 Ref: 8. transclusion wikitext has lower precedence
-!!options
-parsoid
 !! wikitext
 A <ref> foo {{echo|</ref> B C}}
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B C<span typeof="mw:Nowiki">}}</span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
-<li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo <span typeof="mw:Nowiki" data-parsoid='{"src":"{{","dsr":[12,14,0,0]}'>{{</span>echo|</span></li>
+<li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo {{echo|</span></li>
 </ol>
 !!end
 
 !!test
 Ref: 9. unclosed comments should not leak out of ref-body
-!!options
-parsoid
 !! wikitext
 A <ref> foo <!--</ref> B C
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B C</p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
 <li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo <!----></span></li>
@@ -21563,13 +21691,11 @@ A <ref> foo <!--</ref> B C
 
 !!test
 Ref: 10. Unclosed HTML tags should not leak out of ref-body
-!!options
-parsoid
 !! wikitext
 A <ref> <b> foo </ref> B C
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B C</p>
 
 
@@ -21580,13 +21706,11 @@ A <ref> <b> foo </ref> B C
 
 !!test
 Ref: 11. ref-tags acts like an inline element wrt P-wrapping
-!!options
-parsoid
 !! wikitext
 A <ref>foo</ref> B
 C <ref>bar</ref> D
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> B
 C <span about="#mwt4" class="mw-ref" id="cite_ref-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-2"},"attrs":{}}'><a href="#cite_note-2"><span class="mw-reflink-text">[2]</span></a></span> D</p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
 
 !!test
 Ref: 13. ref-tags are not SOL-transparent and block indent-pres
-!!options
-parsoid
 !! wikitext
 <ref>foo</ref> A
 <ref>bar
 </ref> B
 <references />
-!! html
+!! html/parsoid
 <p><span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span> A
 <span about="#mwt4" class="mw-ref" id="cite_ref-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-2"},"attrs":{}}'><a href="#cite_note-2"><span class="mw-reflink-text">[2]</span></a></span> B</p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
@@ -21639,13 +21761,11 @@ parsoid
 
 !!test
 Ref: 14. A nested ref-tag should be emitted as plain text
-!!options
-parsoid
 !! wikitext
 <ref>foo <ref>bar</ref> baz</ref>
 
 <references />
-!! html
+!! html/parsoid
 <p><span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt5" data-mw='{"name":"references","attrs":{}}'>
@@ -21655,14 +21775,12 @@ parsoid
 
 !!test
 Ref: 15. ref-tags with identical names should get identical indexes
-!!options
-parsoid
 !! wikitext
 A1 <ref name="a">foo</ref> A2 <ref name="a" />
 B1 <ref name="b" /> B2 <ref name="b">bar</ref>
 
 <references />
-!! html
+!! html/parsoid
 <p>A1 <span about="#mwt3" class="mw-ref" id="cite_ref-a_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-a-1"},"attrs":{"name":"a"}}'><a href="#cite_note-a-1"><span class="mw-reflink-text">[1]</span></a></span> A2 <span about="#mwt4" class="mw-ref" id="cite_ref-a_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"a"}}'><a href="#cite_note-a-1"><span class="mw-reflink-text">[1]</span></a></span>
 B1 <span about="#mwt7" class="mw-ref" id="cite_ref-b_2-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"b"}}'><a href="#cite_note-b-2"><span class="mw-reflink-text">[2]</span></a></span> B2 <span about="#mwt8" class="mw-ref" id="cite_ref-b_2-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-b-2"},"attrs":{"name":"b"}}'><a href="#cite_note-b-2"><span class="mw-reflink-text">[2]</span></a></span></p>
 
@@ -21679,7 +21797,7 @@ parsoid=wt2html
 A <ref >foo</ref >
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-1"},"attrs":{}}'><a href="#cite_note-1"><span class="mw-reflink-text">[1]</span></a></span></p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt4" data-mw='{"name":"references","attrs":{}}'>
 <li about="#cite_note-1" id="cite_note-1"><a href="#cite_ref-1" rel="mw:referencedBy"><span class="mw-linkback-text">↑ </span></a> <span id="mw-reference-text-cite_note-1" class="mw-reference-text">foo</span></li></ol>
@@ -21687,13 +21805,11 @@ A <ref >foo</ref >
 
 !!test
 Ref: 17. Generate valid HTML5 id/about attributes
-!!options
-parsoid
 !!wikitext
 <ref name="a b">foo</ref>
 
 <references />
-!!html
+!!html/parsoid
 <p><span class="mw-ref" id="cite_ref-a_b_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-a_b-1"},"attrs":{"name":"a b"}}'><a href="#cite_note-a_b-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 
@@ -21704,13 +21820,11 @@ parsoid
 
 !!test
 Ref: 18. T58916: Extension attributes should be parsed as plain text
-!!options
-parsoid
 !!wikitext
 <ref name="{{echo|a}}">foo</ref>
 
 <references />
-!!html
+!!html/parsoid
 <p><span class="mw-ref" id="cite_ref-.7B.7Becho.7Ca.7D.7D_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-.7B.7Becho.7Ca.7D.7D-1"},"attrs":{"name":"{{echo|a}}"}}'><a href="#cite_note-.7B.7Becho.7Ca.7D.7D-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 
@@ -21721,13 +21835,11 @@ parsoid
 
 !!test
 Ref: 19. ref-tags with identical name encodings should get identical indexes
-!!options
-parsoid
 !! wikitext
 1 <ref name="a & b">foo</ref> 2 <ref name="a &amp; b" />
 
 <references />
-!! html
+!! html/parsoid
 <p>1 <span about="#mwt3" class="mw-ref" id="cite_ref-a_.26_b_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-a_.26_b-1"},"attrs":{"name":"a &amp; b"}}'><a href="#cite_note-a_.26_b-1"><span class="mw-reflink-text">[1]</span></a></span> 2 <span about="#mwt4" class="mw-ref" id="cite_ref-a_.26_b_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"a &amp;amp; b"}}'><a href="#cite_note-a_.26_b-1"><span class="mw-reflink-text">[1]</span></a></span>
 </p>
 <ol class="mw-references" typeof="mw:Extension/references" about="#mwt6" data-mw='{"name":"references","attrs":{}}'>
@@ -21737,15 +21849,13 @@ parsoid
 
 !!test
 Ref: 20. ref-tags with identical names but different content should keep it
-!!options
-parsoid
 !! wikitext
 A <ref name="foo">Foo one</ref>
 B <ref name="foo">Foo two</ref>
 C <ref name="foo" />
 
 <references />
-!! html
+!! html/parsoid
 <p>A <span about="#mwt2" class="mw-ref" id="cite_ref-foo_1-0" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"id":"mw-reference-text-cite_note-foo-1"},"attrs":{"name":"foo"}}'><a href="#cite_note-foo-1"><span class="mw-reflink-text">[1]</span></a></span>
 B <span about="#mwt4" class="mw-ref" id="cite_ref-foo_1-1" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","body":{"html":"Foo two"},"attrs":{"name":"foo"}}'><a href="#cite_note-foo-1"><span class="mw-reflink-text">[1]</span></a></span>
 C <span about="#mwt6" class="mw-ref" id="cite_ref-foo_1-2" rel="dc:references" typeof="mw:Extension/ref" data-mw='{"name":"ref","attrs":{"name":"foo"}}'><a href="#cite_note-foo-1"><span class="mw-reflink-text">[1]</span></a></span></p>
@@ -21979,8 +22089,10 @@ foo<ol class="mw-references" typeof="mw:Extension/references" about="#mwt2" data
 #### https://www.mediawiki.org/wiki/Parsoid/HTML_based_LST
 #### ----------------------------------------------------------------
 
-!!test
+!! test
 LST Sections: 1. Simple section start and end
+!! options
+parsoid={ "suppressErrors": true }
 !! wikitext
 <section begin="2011-05-16" />
 <section end="2014-04-10 (MW 1.23wmf22)" />
@@ -23574,7 +23686,8 @@ parsoid=html2wt
 
  __TOC__ foo
 
-__TOC__ bar
+__TOC__
+ bar
 !! end
 
 #### --------------- HTML tags ---------------
@@ -23723,6 +23836,19 @@ HTML tag with broken attribute value quoting
 </p>
 !! end
 
+!! test
+Self-closed tag with broken attribute value quoting
+!! options
+parsoid=wt2html,html2html
+!! wikitext
+<div title="Hello world />Foo
+!! html/php+tidy
+<div title="Hello world"></div>
+<p>Foo</p>
+!! html/parsoid
+<div title="Hello world " data-parsoid='{"stx":"html","selfClose":true}'></div><p>Foo</p>
+!! end
+
 !! test
 Table with broken attribute value quoting
 !! wikitext
@@ -24351,6 +24477,19 @@ Properly encapsulate empty-content transclusions in fosterable positions
 </table>
 !! end
 
+!! test
+Always encapsulate foster box when template range is expanded to table
+!! options
+parsoid=wt2wt
+!! wikitext
+{|
+hello
+{{OpenTable}}
+|}
+!! html/parsoid
+
+!! end
+
 !!test
 Support <object> element with .data attribute
 !!options
@@ -24785,7 +24924,8 @@ parsoid=html2wt
 </div>
 !! wikitext
 foo
-<nowiki> </nowiki><span>bar</span>
+<span>bar</span>
 
 <span>foo2
 <nowiki> </nowiki></span>bar2
@@ -24827,15 +24967,15 @@ parsoid={
 
 <h2><meta property="mw:PageProp/toc" /> ok</h2>
 !! wikitext
-== hello there [[Category:A1]]  ==
+== hello there [[Category:A1]] ==
 
-==  [[Category:A2]] hi pal ==
+== [[Category:A2]] hi pal ==
 
-==  <!--foo-->  [[Category:A3]]    how goes it ==
+== <!--foo-->  [[Category:A3]]    how goes it ==
 
-== it goes well    [[Category:A4]]  <!--bar-->  ==
+== it goes well    [[Category:A4]]  <!--bar--> ==
 
-==howdy [[Category:A5]] ==
+==howdy [[Category:A5]]==
 
 ==  __TOC__  ok ==
 !! end
@@ -25620,7 +25760,7 @@ parsoid={ "modes": ["html2wt"], "suppressErrors": true }
 # shown to sneak through on occasion. See T101768.
 # The original wikitext here is: [http://test.com [[one]] two three]
 !! test
-Strip span tags added to mark as misnested
+Strip span tags added to mark misnested links
 !! options
 parsoid=html2wt
 !! html/parsoid
@@ -25629,10 +25769,112 @@ parsoid=html2wt
 [http://test.com][[one]] two three
 !! end
 
+!! test
+Use data-parsoid.firstWikitextNode to compute newline constraints for template content
+!! options
+parsoid=html2wt
+!! html/parsoid
+<span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"a"}},"i":0}}]}'>a</span><table about="#mwt2" typeof="mw:Transclusion mw:ExpandedAttrs" data-parsoid='{"a":{"{{echo|c\n{{!}}d\n}}":null},"sa":{"{{echo|c\n{{!}}d\n}}":""},"firstWikitextNode":"table","pi":[[{"k":"1","spc":["","","",""]}]]}' data-mw='{"parts":["{|",{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"c\n{{!}}d\n"}},"i":0}},"\n|}"]}'>
+<tbody><tr><td>d
+</td></tr>
+</tbody></table>
+!! wikitext
+{{echo|a}}
+{|{{echo|c
+{{!}}d
+}}
+|}
+!! end
+
+## This test verifies the presence and computation of this attribute indirectly
+## by making an edit and ensuring that the serialization is correct (which it would be
+## only if firstWikitextNode is properly set).
+!! test
+data-parsoid.firstWikitextNode should be computed properly in the presence of fostered content
+!! options
+parsoid= {
+  "modes": ["wt2wt"],
+  "changes": [
+    [ "div#x", "remove" ],
+    [ "div", "before", "<div>new</div>" ]
+  ]
+}
+!! wikitext
+<div id="x">foo</div>
+{|
+{{echo|<div>boo</div>
+{{!}}b}}
+|c
+|}
+!! wikitext/edited
+
+<div>new</div>
+{|
+{{echo|<div>boo</div>
+{{!}}b}}
+|c
+|}
+!! end
+
 # --------------------------------------------
 # Tests spec'ing wikitext serialization norms |
 # --------------------------------------------
 
+!! test
+1. Categories should always be serialized on their own line
+!! options
+parsoid=html2wt
+!! html/parsoid
+foo<link rel="mw:PageProp/Category" href="./Category:Foo">bar
+!! wikitext
+foo
+[[Category:Foo]]
+bar
+!! end
+
+!! test
+2. Categories that are part of templates should not introduce a line break
+!! wikitext
+foo {{echo|<span>bar</span> [[Category:baz]]}} bar
+!! html/parsoid
+<p>foo <span about="#mwt1" typeof="mw:Transclusion" data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;span>bar&lt;/span> [[Category:baz]]"}},"i":0}}]}'>bar</span><span about="#mwt1"> </span><link rel="mw:PageProp/Category" href="./Category:Baz" about="#mwt1" data-parsoid='{"stx":"simple","a":{"href":"./Category:Baz"},"sa":{"href":"Category:baz"}}'/> bar</p>
+!! end
+
+# Careful while editing these next 2 tests. There are \u200f characters
+# before and after the <link> tags in the HTML and following some
+# of the categories in wikitext
+# Do not remove these characters in edits.
+#
+# As part of the serialization, these bidi characters will get stripped.
+!! test
+RTL (\u200f) and LTR (\u200e) markers around category tags should be stripped
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html/parsoid
+<p>‏<link rel="mw:PageProp/Category" href="./קטגוריה:טקסים" />‏
+‏<link rel="mw:PageProp/Category" href="./קטגוריה:_שיטות_משפט" />‏</p>
+!! wikitext
+[[קטגוריה:טקסים]]
+[[קטגוריה: שיטות משפט]]
+!! end
+
+!! test
+RTL (\u200f) and LTR (\u200e) markers should not be stripped if followed by a text node
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html/parsoid
+<p>‏<link rel="mw:PageProp/Category" href="./קטגוריה:טקסים" />‏y</p>
+!! wikitext
+[[קטגוריה:טקסים]]
+‏y
+!! end
+
 !! test
 Lists: Add space after bullets
 !! options
@@ -25720,6 +25962,35 @@ parsoid={
 !! wikitext/edited
 !! end
 
+!! test
+Headings: Replace <br/> with a single whitespace char (when scrubWikitext = true)
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": true
+}
+!! html/parsoid
+<h2>foo<br/>bar</h2>
+<h2>foo <span><br/>bar</span> baz</h2>
+!! wikitext
+== foo bar ==
+
+== foo <span> bar</span> baz ==
+!! end
+
+!! test
+Headings: Replace <br/> with a single whitespace char (when scrubWikitext = false)
+!! options
+parsoid={
+  "modes": ["html2wt"],
+  "scrubWikitext": false
+}
+!! html/parsoid
+<h2>foo<br/>bar</h2>
+!! wikitext
+== foo<br> bar ==
+!! end
+
 !! test
 1. WT Quote Tags: suppress newly created empty style tags
 !! options
@@ -26225,10 +26496,61 @@ parsoid=html2wt
 &lt;nowiki&gt;''foo''&lt;/nowiki&gt;
 !! end
 
+# This is meant to be an interim fix while we go about figuring out
+# how to not introduce these trailing <nowiki/>s in the first place.
+!! test
+T115717: Strip trailing <nowiki/>s (without affecting valid uses)
+!! options
+parsoid=html2wt
+!! html/parsoid
+<p>x<meta typeof="mw:Placeholder" data-parsoid='{"src":"&lt;nowiki/>"}'/><meta typeof="mw:Placeholder" data-parsoid='{"src":"&lt;nowiki/>"}'/>
+y</p>
+<p><span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"dsr":[0,23,null,null],"pi":[[{"k":"1","named":true,"spc":["\n"," "," ",""]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;nowiki/>"}},"i":0}}]}'></span></p>
+<p><span about="#mwt1" typeof="mw:Transclusion" data-parsoid='{"dsr":[0,24,null,null],"pi":[[{"k":"1","named":true,"spc":["\n"," "," ","\n"]}]]}' data-mw='{"parts":[{"template":{"target":{"wt":"echo","href":"./Template:Echo"},"params":{"1":{"wt":"&lt;nowiki/>"}},"i":0}}]}'></span></p>
+!! wikitext
+x
+y
+
+{{echo|
+1 = <nowiki/>}}
+
+{{echo|
+1 = <nowiki/>
+}}
+!! end
+
 # ---------------------------------------------------
 # End of tests spec'ing wikitext serialization norms |
 # ---------------------------------------------------
 
+# T104032
+!! test
+Bare inline nodes not wrapped inside p-tags should be treated as p-wrapped
+!! options
+parsoid=html2wt
+!! html/parsoid
+a<p>b</p>
+<b>c</b><p>d</p>
+<table><tr>
+<td>a<p>b</p></td>
+<td><b>c</b><p>d</p></td>
+</tr></table>
+!! wikitext
+a
+
+b
+
+'''c'''
+
+d
+{|
+|a
+b
+|'''c'''
+d
+|}
+!! end
+
 # -----------------------------------------------------------------
 # End of section for Parsoid-only html2wt tests for serialization
 # of new content
index fc2f743..861e3bd 100644 (file)
@@ -221,6 +221,8 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
        }
 
        protected function tearDown() {
+               global $wgRequest;
+
                $status = ob_get_status();
                if ( isset( $status['name'] ) &&
                        $status['name'] === 'MediaWikiTestCase::wfResetOutputBuffersBarrier'
@@ -252,6 +254,12 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                $this->mwGlobals = array();
                RequestContext::resetMain();
                MediaHandler::resetCache();
+               if ( session_id() !== '' ) {
+                       session_write_close();
+                       session_id( '' );
+               }
+               $wgRequest = new FauxRequest();
+               MediaWiki\Session\SessionManager::resetCache();
 
                $phpErrorLevel = intval( ini_get( 'error_reporting' ) );
 
@@ -509,6 +517,13 @@ abstract class MediaWikiTestCase extends PHPUnit_Framework_TestCase {
                                false,
                                $user
                        );
+
+                       // doEditContent() probably started the session via
+                       // User::loadFromSession(). Close it now.
+                       if ( session_id() !== '' ) {
+                               session_write_close();
+                               session_id( '' );
+                       }
                }
        }
 
diff --git a/tests/phpunit/data/gitinfo/extension/gitinfo.json b/tests/phpunit/data/gitinfo/extension/gitinfo.json
new file mode 100644 (file)
index 0000000..8cf21bd
--- /dev/null
@@ -0,0 +1,7 @@
+{
+    "head": "refs/heads/master",
+    "headSHA1": "0123456789abcdef0123456789abcdef01234567",
+    "headCommitDate": "1070884800",
+    "branch": "master",
+    "remoteURL": "https://gerrit.wikimedia.org/r/mediawiki/core"
+}
diff --git a/tests/phpunit/data/parser/320x240.ogv b/tests/phpunit/data/parser/320x240.ogv
new file mode 100644 (file)
index 0000000..7903820
Binary files /dev/null and b/tests/phpunit/data/parser/320x240.ogv differ
diff --git a/tests/phpunit/data/templates/conds.mustache b/tests/phpunit/data/templates/conds.mustache
new file mode 100644 (file)
index 0000000..5ebd2ea
--- /dev/null
@@ -0,0 +1 @@
+{{#list}}oh no{{/list}}{{#foo}}none of this should render{{/foo}}
\ No newline at end of file
diff --git a/tests/phpunit/includes/ExportTest.php b/tests/phpunit/includes/ExportTest.php
new file mode 100644 (file)
index 0000000..2026030
--- /dev/null
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Test class for Export methods.
+ *
+ * @group Database
+ *
+ * @author Isaac Hutt <mhutti1@gmail.com>
+ */
+class ExportTest extends MediaWikiLangTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( array(
+                       'wgContLang' => Language::factory( 'en' ),
+                       'wgLanguageCode' => 'en',
+                       'wgCapitalLinks' => true,
+               ) );
+       }
+
+       /**
+        * @covers WikiExporter::pageByTitle
+        */
+       public function testPageByTitle() {
+               global $wgContLang;
+               $pageTitle = 'UTPage';
+
+               $exporter = new WikiExporter(
+                       $this->db,
+                       WikiExporter::FULL
+               );
+
+               $title = Title::newFromText( $pageTitle );
+
+               ob_start();
+               $exporter->openStream();
+               $exporter->pageByTitle( $title );
+               $exporter->closeStream();
+               $xmlString = ob_get_clean();
+
+               // This throws error if invalid xml output
+               $xmlObject = simplexml_load_string( $xmlString );
+
+               /**
+                * Check namespaces match xml
+                * FIXME: PHP 5.3 support. When we don't support PHP 5.3,
+                * add ->namespace to object and remove from array
+                */
+               $xmlNamespaces = (array) $xmlObject->siteinfo->namespaces;
+               $xmlNamespaces = str_replace( ' ', '_', $xmlNamespaces['namespace'] );
+               unset ( $xmlNamespaces[ '@attributes' ] );
+               foreach ( $xmlNamespaces as &$namespaceObject ) {
+                       if ( is_object( $namespaceObject ) ) {
+                               $namespaceObject = '';
+                       }
+               }
+
+               $actualNamespaces = (array) $wgContLang->getNamespaces();
+               $actualNamespaces = array_values( $actualNamespaces );
+               $this->assertEquals( $actualNamespaces, $xmlNamespaces );
+
+               // Check xml page title correct
+               $xmlTitle = (array) $xmlObject->page->title;
+               $this->assertEquals( $pageTitle, $xmlTitle[0] );
+
+               // Check xml page text is not empty
+               $text = (array) $xmlObject->page->revision->text;
+               $this->assertNotEquals( '', $text[0] );
+       }
+
+}
index c3539d0..9f4a01c 100644 (file)
@@ -9,18 +9,23 @@ class GitInfoTest extends MediaWikiTestCase {
                $this->setMwGlobals( 'wgGitInfoCacheDirectory', __DIR__ . '/../data/gitinfo' );
        }
 
-       public function testValidJsonData() {
-               $dir = $GLOBALS['IP'] . DIRECTORY_SEPARATOR . 'testValidJsonData';
-               $fixture = new GitInfo( $dir );
-
-               $this->assertTrue( $fixture->cacheIsComplete() );
-               $this->assertEquals( 'refs/heads/master', $fixture->getHead() );
+       protected function assertValidGitInfo( GitInfo $gitInfo ) {
+               $this->assertTrue( $gitInfo->cacheIsComplete() );
+               $this->assertEquals( 'refs/heads/master', $gitInfo->getHead() );
                $this->assertEquals( '0123456789abcdef0123456789abcdef01234567',
-                       $fixture->getHeadSHA1() );
-               $this->assertEquals( '1070884800', $fixture->getHeadCommitDate() );
-               $this->assertEquals( 'master', $fixture->getCurrentBranch() );
+                       $gitInfo->getHeadSHA1() );
+               $this->assertEquals( '1070884800', $gitInfo->getHeadCommitDate() );
+               $this->assertEquals( 'master', $gitInfo->getCurrentBranch() );
                $this->assertContains( '0123456789abcdef0123456789abcdef01234567',
-                       $fixture->getHeadViewUrl() );
+                       $gitInfo->getHeadViewUrl() );
+
+       }
+
+       public function testValidJsonData() {
+               global $IP;
+
+               $this->assertValidGitInfo( new GitInfo( "$IP/testValidJsonData") );
+               $this->assertValidGitInfo( new GitInfo( __DIR__ . "/../data/gitinfo/extension" ) );
        }
 
        public function testMissingJsonData() {
diff --git a/tests/phpunit/includes/ImportLinkCacheIntegrationTest.php b/tests/phpunit/includes/ImportLinkCacheIntegrationTest.php
deleted file mode 100644 (file)
index 1433b89..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-<?php
-/**
- * Integration test that checks import success and
- * LinkCache integration.
- *
- * @group medium
- * @group Database
- *
- * @author mwjames
- */
-class ImportLinkCacheIntegrationTest extends MediaWikiTestCase {
-
-       private $importStreamSource;
-
-       protected function setUp() {
-               parent::setUp();
-
-               $file = dirname( __DIR__ ) . '/data/import/ImportLinkCacheIntegrationTest.xml';
-
-               $this->importStreamSource = ImportStreamSource::newFromFile( $file );
-
-               if ( !$this->importStreamSource->isGood() ) {
-                       throw new Exception( "Import source for {$file} failed" );
-               }
-       }
-
-       public function testImportForImportSource() {
-
-               $this->doImport( $this->importStreamSource );
-
-               // Imported title
-               $loremIpsum = Title::newFromText( 'Lorem ipsum' );
-
-               $this->assertSame(
-                       $loremIpsum->getArticleID(),
-                       $loremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
-               );
-
-               $categoryLoremIpsum = Title::newFromText( 'Category:Lorem ipsum' );
-
-               $this->assertSame(
-                       $categoryLoremIpsum->getArticleID(),
-                       $categoryLoremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
-               );
-
-               $page = new WikiPage( $loremIpsum );
-               $page->doDeleteArticle( 'import test: delete page' );
-
-               $page = new WikiPage( $categoryLoremIpsum );
-               $page->doDeleteArticle( 'import test: delete page' );
-       }
-
-       /**
-        * @depends testImportForImportSource
-        */
-       public function testReImportForImportSource() {
-
-               $this->doImport( $this->importStreamSource );
-
-               // ReImported title
-               $loremIpsum = Title::newFromText( 'Lorem ipsum' );
-
-               $this->assertSame(
-                       $loremIpsum->getArticleID(),
-                       $loremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
-               );
-
-               $categoryLoremIpsum = Title::newFromText( 'Category:Lorem ipsum' );
-
-               $this->assertSame(
-                       $categoryLoremIpsum->getArticleID(),
-                       $categoryLoremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
-               );
-       }
-
-       private function doImport( $importStreamSource ) {
-
-               $importer = new WikiImporter(
-                       $importStreamSource->value,
-                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
-               );
-               $importer->setDebug( true );
-
-               $reporter = new ImportReporter(
-                       $importer,
-                       false,
-                       '',
-                       false
-               );
-
-               $reporter->setContext( new RequestContext() );
-               $reporter->open();
-               $exception = false;
-
-               try {
-                       $importer->doImport();
-               } catch ( Exception $e ) {
-                       $exception = $e;
-               }
-
-               $result = $reporter->close();
-
-               $this->assertFalse(
-                       $exception
-               );
-
-               $this->assertTrue(
-                       $result->isGood()
-               );
-       }
-
-}
diff --git a/tests/phpunit/includes/ImportTest.php b/tests/phpunit/includes/ImportTest.php
deleted file mode 100644 (file)
index 9c22430..0000000
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-
-/**
- * Test class for Import methods.
- *
- * @group Database
- *
- * @author Sebastian Brückner < sebastian.brueckner@student.hpi.uni-potsdam.de >
- */
-class ImportTest extends MediaWikiLangTestCase {
-
-       private function getDataSource( $xml ) {
-               return new ImportStringSource( $xml );
-       }
-
-       /**
-        * @covers WikiImporter::handlePage
-        * @dataProvider getRedirectXML
-        * @param string $xml
-        * @param string|null $redirectTitle
-        */
-       public function testHandlePageContainsRedirect( $xml, $redirectTitle ) {
-               $source = $this->getDataSource( $xml );
-
-               $redirect = null;
-               $callback = function ( Title $title, ForeignTitle $foreignTitle, $revCount,
-                       $sRevCount, $pageInfo ) use ( &$redirect ) {
-                       if ( array_key_exists( 'redirect', $pageInfo ) ) {
-                               $redirect = $pageInfo['redirect'];
-                       }
-               };
-
-               $importer = new WikiImporter(
-                       $source,
-                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
-               );
-               $importer->setPageOutCallback( $callback );
-               $importer->doImport();
-
-               $this->assertEquals( $redirectTitle, $redirect );
-       }
-
-       public function getRedirectXML() {
-               // @codingStandardsIgnoreStart Generic.Files.LineLength
-               return array(
-                       array(
-                               <<< EOF
-<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
-       <page>
-               <title>Test</title>
-               <ns>0</ns>
-               <id>21</id>
-               <redirect title="Test22"/>
-               <revision>
-                       <id>20</id>
-                       <timestamp>2014-05-27T10:00:00Z</timestamp>
-                       <contributor>
-                               <username>Admin</username>
-                               <id>10</id>
-                       </contributor>
-                       <comment>Admin moved page [[Test]] to [[Test22]]</comment>
-                       <model>wikitext</model>
-                       <format>text/x-wiki</format>
-                       <text xml:space="preserve" bytes="20">#REDIRECT [[Test22]]</text>
-                       <sha1>tq456o9x3abm7r9ozi6km8yrbbc56o6</sha1>
-               </revision>
-       </page>
-</mediawiki>
-EOF
-                       ,
-                               'Test22'
-                       ),
-                       array(
-                               <<< EOF
-<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.9/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.9/ http://www.mediawiki.org/xml/export-0.9.xsd" version="0.9" xml:lang="en">
-       <page>
-               <title>Test</title>
-               <ns>0</ns>
-               <id>42</id>
-               <revision>
-                       <id>421</id>
-                       <timestamp>2014-05-27T11:00:00Z</timestamp>
-                       <contributor>
-                               <username>Admin</username>
-                               <id>10</id>
-                       </contributor>
-                       <text xml:space="preserve" bytes="4">Abcd</text>
-                       <sha1>n7uomjq96szt60fy5w3x7ahf7q8m8rh</sha1>
-                       <model>wikitext</model>
-                       <format>text/x-wiki</format>
-               </revision>
-       </page>
-</mediawiki>
-EOF
-                       ,
-                               null
-                       ),
-               );
-               // @codingStandardsIgnoreEnd
-       }
-
-       /**
-        * @covers WikiImporter::handleSiteInfo
-        * @dataProvider getSiteInfoXML
-        * @param string $xml
-        * @param array|null $namespaces
-        */
-       public function testSiteInfoContainsNamespaces( $xml, $namespaces ) {
-               $source = $this->getDataSource( $xml );
-
-               $importNamespaces = null;
-               $callback = function ( array $siteinfo, $innerImporter ) use ( &$importNamespaces ) {
-                       $importNamespaces = $siteinfo['_namespaces'];
-               };
-
-               $importer = new WikiImporter(
-                       $source,
-                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
-               );
-               $importer->setSiteInfoCallback( $callback );
-               $importer->doImport();
-
-               $this->assertEquals( $importNamespaces, $namespaces );
-       }
-
-       public function getSiteInfoXML() {
-               // @codingStandardsIgnoreStart Generic.Files.LineLength
-               return array(
-                       array(
-                               <<< EOF
-<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
-  <siteinfo>
-    <namespaces>
-      <namespace key="-2" case="first-letter">Media</namespace>
-      <namespace key="-1" case="first-letter">Special</namespace>
-      <namespace key="0" case="first-letter" />
-      <namespace key="1" case="first-letter">Talk</namespace>
-      <namespace key="2" case="first-letter">User</namespace>
-      <namespace key="3" case="first-letter">User talk</namespace>
-      <namespace key="100" case="first-letter">Portal</namespace>
-      <namespace key="101" case="first-letter">Portal talk</namespace>
-    </namespaces>
-  </siteinfo>
-</mediawiki>
-EOF
-                       ,
-                               array(
-                                       '-2' => 'Media',
-                                       '-1' => 'Special',
-                                       '0' => '',
-                                       '1' => 'Talk',
-                                       '2' => 'User',
-                                       '3' => 'User talk',
-                                       '100' => 'Portal',
-                                       '101' => 'Portal talk',
-                               )
-                       ),
-               );
-               // @codingStandardsIgnoreEnd
-       }
-
-}
diff --git a/tests/phpunit/includes/PagePropsTest.php b/tests/phpunit/includes/PagePropsTest.php
new file mode 100644 (file)
index 0000000..9a1f597
--- /dev/null
@@ -0,0 +1,276 @@
+<?php
+
+/**
+ * @group Database
+ *     ^--- tell jenkins this test needs the database
+ *
+ * @group medium
+ *     ^--- tell phpunit that these test cases may take longer than 2 seconds.
+ */
+class TestPageProps extends MediaWikiLangTestCase {
+
+       /**
+        * @var Title $title1
+        */
+       private $title1;
+
+       /**
+        * @var Title $title2
+        */
+       private $title2;
+
+       /**
+        * @var array $the_properties
+        */
+       private $the_properties;
+
+       protected function setUp() {
+               global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
+
+               parent::setUp();
+
+               $wgExtraNamespaces[12312] = 'Dummy';
+               $wgExtraNamespaces[12313] = 'Dummy_talk';
+
+               $wgNamespaceContentModels[12312] = 'DUMMY';
+               $wgContentHandlers['DUMMY'] = 'DummyContentHandlerForTesting';
+
+               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
+               $wgContLang->resetNamespaces(); # reset namespace cache
+
+               if ( !$this->the_properties ) {
+                       $this->the_properties = array(
+                               "property1" => "value1",
+                               "property2" => "value2",
+                               "property3" => "value3",
+                               "property4" => "value4"
+                       );
+               }
+
+               if ( !$this->title1 ) {
+                       $page = $this->createPage(
+                               'PagePropsTest_page_1',
+                               "just a dummy page",
+                               CONTENT_MODEL_WIKITEXT
+                       );
+                       $this->title1 = $page->getTitle();
+                       $page1ID = $this->title1->getArticleID();
+                       $this->setProperties( $page1ID, $this->the_properties );
+               }
+
+               if ( !$this->title2 ) {
+                       $page = $this->createPage(
+                               'PagePropsTest_page_2',
+                               "just a dummy page",
+                               CONTENT_MODEL_WIKITEXT
+                       );
+                       $this->title2 = $page->getTitle();
+                       $page2ID = $this->title2->getArticleID();
+                       $this->setProperties( $page2ID, $this->the_properties );
+               }
+       }
+
+       protected function tearDown() {
+               global $wgExtraNamespaces, $wgNamespaceContentModels, $wgContentHandlers, $wgContLang;
+
+               parent::tearDown();
+
+               unset( $wgExtraNamespaces[12312] );
+               unset( $wgExtraNamespaces[12313] );
+
+               unset( $wgNamespaceContentModels[12312] );
+               unset( $wgContentHandlers['DUMMY'] );
+
+               MWNamespace::getCanonicalNamespaces( true ); # reset namespace cache
+               $wgContLang->resetNamespaces(); # reset namespace cache
+       }
+
+       /**
+        * Test getting a single property from a single page. The property was
+        * set in setUp().
+        */
+       public function testGetSingleProperty() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $result = $pageProps->getProperty( $this->title1, "property1" );
+               $this->assertArrayHasKey( $page1ID, $result, "Found property" );
+               $this->assertEquals( $result[$page1ID], "value1", "Get property" );
+       }
+
+       /**
+        * Test getting a single property from multiple pages. The property was
+        * set in setUp().
+        */
+       public function testGetSinglePropertyMultiplePages() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $page2ID = $this->title2->getArticleID();
+               $titles = array(
+                       $this->title1,
+                       $this->title2
+               );
+               $result = $pageProps->getProperty( $titles, "property1" );
+               $this->assertArrayHasKey( $page1ID, $result, "Found page 1 property" );
+               $this->assertArrayHasKey( $page2ID, $result, "Found page 2 property" );
+               $this->assertEquals( $result[$page1ID], "value1", "Get property page 1" );
+               $this->assertEquals( $result[$page2ID], "value1", "Get property page 2" );
+       }
+
+       /**
+        * Test getting all properties from a single page. The properties were
+        * set in setUp(). The properties retrieved from the page may include
+        * additional properties not set in the test case that are added by
+        * other extensions. Therefore, rather than checking to see if the
+        * properties that were set in the test case exactly match the
+        * retrieved properties, we need to check to see if they are a
+        * subset of the retrieved properties. Since this version of PHPUnit
+        * does not yet include assertArraySubset(), we needed to code the
+        * equivalent functionality.
+        */
+       public function testGetAllProperties() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $result = $pageProps->getProperties( $this->title1 );
+               $this->assertArrayHasKey( $page1ID, $result, "Found properties" );
+               $properties = $result[$page1ID];
+               $patched = array_replace_recursive( $properties, $this->the_properties );
+               $this->assertEquals( $patched, $properties, "Get all properties" );
+       }
+
+       /**
+        * Test getting all properties from multiple pages. The properties were
+        * set in setUp(). See getAllProperties() above for more information.
+        */
+       public function testGetAllPropertiesMultiplePages() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $page2ID = $this->title2->getArticleID();
+               $titles = array(
+                       $this->title1,
+                       $this->title2
+               );
+               $result = $pageProps->getProperties( $titles );
+               $this->assertArrayHasKey( $page1ID, $result, "Found page 1 properties" );
+               $this->assertArrayHasKey( $page2ID, $result, "Found page 2 properties" );
+               $properties1 = $result[$page1ID];
+               $patched = array_replace_recursive( $properties1, $this->the_properties );
+               $this->assertEquals( $patched, $properties1, "Get all properties page 1" );
+               $properties2 = $result[$page2ID];
+               $patched = array_replace_recursive( $properties2, $this->the_properties );
+               $this->assertEquals( $patched, $properties2, "Get all properties page 2" );
+       }
+
+       /**
+        * Test caching when retrieving single properties by getting a property,
+        * saving a new value for the property, then getting the property
+        * again. The cached value for the property rather than the new value
+        * of the property should be returned.
+        */
+       public function testSingleCache() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $value1 = $pageProps->getProperty( $this->title1, "property1" );
+               $this->setProperty( $page1ID, "property1", "another value" );
+               $value2 = $pageProps->getProperty( $this->title1, "property1" );
+               $this->assertEquals( $value1, $value2, "Single cache" );
+       }
+
+       /**
+        * Test caching when retrieving all properties by getting all
+        * properties, saving a new value for a property, then getting all
+        * properties again. The cached value for the properties rather than the
+        * new value of the properties should be returned.
+        */
+       public function testMultiCache() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $properties1 = $pageProps->getProperties( $this->title1 );
+               $this->setProperty( $page1ID, "property1", "another value" );
+               $properties2 = $pageProps->getProperties( $this->title1 );
+               $this->assertEquals( $properties1, $properties2, "Multi Cache" );
+       }
+
+       /**
+        * Test that getting all properties clears the single properties
+        * that have been cached by getting a property, saving a new value for
+        * the property, getting all properties (which clears the cached single
+        * properties), then getting the property again. The new value for the
+        * property rather than the cached value of the property should be
+        * returned.
+        */
+       public function testClearCache() {
+               $pageProps = PageProps::getInstance();
+               $page1ID = $this->title1->getArticleID();
+               $pageProps->getProperty( $this->title1, "property1" );
+               $new_value = "another value";
+               $this->setProperty( $page1ID, "property1", $new_value );
+               $pageProps->getProperties( $this->title1 );
+               $result = $pageProps->getProperty( $this->title1, "property1" );
+               $this->assertArrayHasKey( $page1ID, $result, "Found property" );
+               $this->assertEquals( $result[$page1ID], "another value", "Clear cache" );
+       }
+
+       protected function createPage( $page, $text, $model = null ) {
+               if ( is_string( $page ) ) {
+                       if ( !preg_match( '/:/', $page ) &&
+                               ( $model === null || $model === CONTENT_MODEL_WIKITEXT )
+                       ) {
+                               $ns = $this->getDefaultWikitextNS();
+                               $page = MWNamespace::getCanonicalName( $ns ) . ':' . $page;
+                       }
+
+                       $page = Title::newFromText( $page );
+               }
+
+               if ( $page instanceof Title ) {
+                       $page = new WikiPage( $page );
+               }
+
+               if ( $page->exists() ) {
+                       $page->doDeleteArticle( "done" );
+               }
+
+               $content = ContentHandler::makeContent( $text, $page->getTitle(), $model );
+               $page->doEditContent( $content, "testing", EDIT_NEW );
+
+               return $page;
+       }
+
+       protected function setProperties( $pageID, $properties ) {
+
+               $rows = array();
+
+               foreach ( $properties as $propertyName => $propertyValue ) {
+
+                       $row = array(
+                               'pp_page' => $pageID,
+                               'pp_propname' => $propertyName,
+                               'pp_value' => $propertyValue
+                       );
+
+                       $rows[] = $row;
+               }
+
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->replace(
+                       'page_props',
+                       array(
+                               array(
+                                       'pp_page',
+                                       'pp_propname'
+                               )
+                       ),
+                       $rows,
+                       __METHOD__
+               );
+       }
+
+       protected function setProperty( $pageID, $propertyName, $propertyValue ) {
+
+               $properties = array();
+               $properties[$propertyName] = $propertyValue;
+
+               $this->setProperties( $pageID, $properties );
+
+       }
+}
diff --git a/tests/phpunit/includes/TestLogger.php b/tests/phpunit/includes/TestLogger.php
new file mode 100644 (file)
index 0000000..7099c3a
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Testing logger
+ *
+ * Copyright (C) 2015 Brad Jorsch <bjorsch@wikimedia.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
+ * @author Brad Jorsch <bjorsch@wikimedia.org>
+ */
+
+use Psr\Log\LogLevel;
+
+/**
+ * A logger that may be configured to either buffer logs or to print them to
+ * the output where PHPUnit will complain about them.
+ *
+ * @since 1.27
+ */
+class TestLogger extends \Psr\Log\AbstractLogger {
+       private $collect = false;
+       private $buffer = array();
+       private $filter = null;
+
+       /**
+        * @param bool $collect Whether to collect logs
+        * @param callable $filter Filter logs before collecting/printing. Signature is
+        *  string|null function ( string $message, string $level );
+        */
+       public function __construct( $collect = false, $filter = null ) {
+               $this->collect = $collect;
+               $this->filter = $filter;
+       }
+
+       /**
+        * Set the "collect" flag
+        * @param bool $collect
+        */
+       public function setCollect( $collect ) {
+               $this->collect = $collect;
+       }
+
+       /**
+        * Return the collected logs
+        * @return array Array of array( string $level, string $message )
+        */
+       public function getBuffer() {
+               return $this->buffer;
+       }
+
+       /**
+        * Clear the collected log buffer
+        */
+       public function clearBuffer() {
+               $this->buffer = array();
+       }
+
+       public function log( $level, $message, array $context = array() ) {
+               $message = trim( $message );
+
+               if ( $this->filter ) {
+                       $message = call_user_func( $this->filter, $message, $level );
+                       if ( $message === null ) {
+                               return;
+                       }
+               }
+
+               if ( $this->collect ) {
+                       $this->buffer[] = array( $level, $message );
+               } else {
+                       switch ( $level ) {
+                               case LogLevel::DEBUG:
+                               case LogLevel::INFO:
+                               case LogLevel::NOTICE:
+                                       trigger_error( "LOG[$level]: $message", E_USER_NOTICE );
+                                       break;
+
+                               case LogLevel::WARNING:
+                                       trigger_error( "LOG[$level]: $message", E_USER_WARNING );
+                                       break;
+
+                               case LogLevel::ERROR:
+                               case LogLevel::CRITICAL:
+                               case LogLevel::ALERT:
+                               case LogLevel::EMERGENCY:
+                                       trigger_error( "LOG[$level]: $message", E_USER_ERROR );
+                                       break;
+                       }
+               }
+       }
+}
index 7dfd14f..4085925 100644 (file)
@@ -13,9 +13,13 @@ class ApiLoginTest extends ApiTestCase {
         * Test result of attempted login with an empty username
         */
        public function testApiLoginNoName() {
+               $session = array(
+                       'wsLoginToken' => 'foobar'
+               );
                $data = $this->doApiRequest( array( 'action' => 'login',
                        'lgname' => '', 'lgpassword' => self::$users['sysop']->password,
-               ) );
+                       'lgtoken' => 'foobar',
+               ), $session );
                $this->assertEquals( 'NoName', $data[0]['login']['result'] );
        }
 
@@ -179,4 +183,94 @@ class ApiLoginTest extends ApiTestCase {
                $this->assertArrayHasKey( 'lgtoken', $data[0]['login'] );
        }
 
+       public function testBotPassword() {
+               global $wgServer, $wgSessionProviders;
+
+               if ( !isset( $wgServer ) ) {
+                       $this->markTestIncomplete( 'This test needs $wgServer to be set in LocalSettings.php' );
+               }
+
+               $this->setMwGlobals( array(
+                       'wgSessionProviders' => array_merge( $wgSessionProviders, array(
+                               array(
+                                       'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider',
+                                       'args' => array( array( 'priority' => 40 ) ),
+                               )
+                       ) ),
+                       'wgEnableBotPasswords' => true,
+                       'wgBotPasswordsDatabase' => false,
+                       'wgCentralIdLookupProvider' => 'local',
+                       'wgGrantPermissions' => array(
+                               'test' => array( 'read' => true ),
+                       ),
+               ) );
+
+               // Make sure our session provider is present
+               $manager = TestingAccessWrapper::newFromObject( MediaWiki\Session\SessionManager::singleton() );
+               if ( !isset( $manager->sessionProviders['MediaWiki\\Session\\BotPasswordSessionProvider'] ) ) {
+                       $tmp = $manager->sessionProviders;
+                       $manager->sessionProviders = null;
+                       $manager->sessionProviders = $tmp + $manager->getProviders();
+               }
+               $this->assertNotNull(
+                       MediaWiki\Session\SessionManager::singleton()->getProvider(
+                               'MediaWiki\\Session\\BotPasswordSessionProvider'
+                       ),
+                       'sanity check'
+               );
+
+               $user = self::$users['sysop'];
+               $centralId = CentralIdLookup::factory()->centralIdFromLocalUser( $user->getUser() );
+               $this->assertNotEquals( 0, $centralId, 'sanity check' );
+
+               $passwordFactory = new PasswordFactory();
+               $passwordFactory->init( RequestContext::getMain()->getConfig() );
+               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
+               $passwordFactory->setDefaultType( 'A' );
+               $pwhash = $passwordFactory->newFromPlaintext( 'foobaz' );
+
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->insert(
+                       'bot_passwords',
+                       array(
+                               'bp_user' => $centralId,
+                               'bp_app_id' => 'foo',
+                               'bp_password' => $pwhash->toString(),
+                               'bp_token' => '',
+                               'bp_restrictions' => MWRestrictions::newDefault()->toJson(),
+                               'bp_grants' => '["test"]',
+                       ),
+                       __METHOD__
+               );
+
+               $lgName = $user->username . BotPassword::getSeparator() . 'foo';
+
+               $ret = $this->doApiRequest( array(
+                       'action' => 'login',
+                       'lgname' => $lgName,
+                       'lgpassword' => 'foobaz',
+               ) );
+
+               $result = $ret[0];
+               $this->assertNotInternalType( 'bool', $result );
+               $this->assertNotInternalType( 'null', $result['login'] );
+
+               $a = $result['login']['result'];
+               $this->assertEquals( 'NeedToken', $a );
+               $token = $result['login']['token'];
+
+               $ret = $this->doApiRequest( array(
+                       'action' => 'login',
+                       'lgtoken' => $token,
+                       'lgname' => $lgName,
+                       'lgpassword' => 'foobaz',
+               ), $ret[2] );
+
+               $result = $ret[0];
+               $this->assertNotInternalType( 'bool', $result );
+               $a = $result['login']['result'];
+
+               $this->assertEquals( 'Success', $a );
+       }
+
 }
index aef4815..f02f7df 100644 (file)
@@ -97,7 +97,8 @@ class ApiMainTest extends ApiTestCase {
                $request->setHeaders( $headers );
                $request->response()->statusHeader( 200 ); // Why doesn't it default?
 
-               $api = new ApiMain( $request );
+               $context = $this->apiContext->newTestContext( $request, null );
+               $api = new ApiMain( $context );
                $priv = TestingAccessWrapper::newFromObject( $api );
                $priv->mInternalMode = false;
 
index 9dbde3d..292d276 100644 (file)
@@ -218,6 +218,17 @@ class ApiResultTest extends MediaWikiTestCase {
                        0 => "foo\xef\xbf\xbdbar",
                        1 => "\xc3\xa1",
                ), $arr );
+
+               $obj = new stdClass;
+               $obj->{'1'} = 'one';
+               $arr = array();
+               ApiResult::setValue( $arr, 'foo', $obj );
+               $this->assertSame( array(
+                       'foo' => array(
+                               1 => 'one',
+                               ApiResult::META_TYPE => 'assoc',
+                       )
+               ), $arr );
        }
 
        /**
@@ -509,6 +520,19 @@ class ApiResultTest extends MediaWikiTestCase {
                        1 => "\xc3\xa1",
                        ApiResult::META_TYPE => 'assoc',
                ), $result->getResultData() );
+
+               $result = new ApiResult( 8388608 );
+               $obj = new stdClass;
+               $obj->{'1'} = 'one';
+               $arr = array();
+               $result->addValue( $arr, 'foo', $obj );
+               $this->assertSame( array(
+                       'foo' => array(
+                               1 => 'one',
+                               ApiResult::META_TYPE => 'assoc',
+                       ),
+                       ApiResult::META_TYPE => 'assoc',
+               ), $result->getResultData() );
        }
 
        /**
index 01113a6..25ffcb7 100644 (file)
@@ -47,11 +47,7 @@ abstract class ApiTestCase extends MediaWikiLangTestCase {
 
        protected function tearDown() {
                // Avoid leaking session over tests
-               if ( session_id() != '' ) {
-                       global $wgUser;
-                       $wgUser->logout();
-                       session_destroy();
-               }
+               MediaWiki\Session\SessionManager::getGlobalSession()->clear();
 
                parent::tearDown();
        }
index 87f794c..b6ae641 100644 (file)
@@ -15,8 +15,6 @@ abstract class ApiTestCaseUpload extends ApiTestCase {
                        'wgEnableAPI' => true,
                ) );
 
-               wfSetupSession();
-
                $this->clearFakeUploads();
        }
 
index 0b87727..552dacb 100644 (file)
@@ -212,7 +212,7 @@ class RCCacheEntryFactoryTest extends MediaWikiLangTestCase {
                                ),
                                'child' => array(
                                        'tag' => 'a',
-                                       'content' => 'Talk',
+                                       'content' => 'talk',
                                )
                        ),
                        $cacheEntry->usertalklink,
index a9e5be2..25969e6 100644 (file)
@@ -37,6 +37,14 @@ class RequestContextTest extends MediaWikiTestCase {
         * @covers RequestContext::importScopedSession
         */
        public function testImportScopedSession() {
+               // Make sure session handling is started
+               if ( !MediaWiki\Session\PHPSessionHandler::isInstalled() ) {
+                       MediaWiki\Session\PHPSessionHandler::install(
+                               MediaWiki\Session\SessionManager::singleton()
+                       );
+               }
+               $oldSessionId = session_id();
+
                $context = RequestContext::getMain();
 
                $oInfo = $context->exportSession();
@@ -76,7 +84,16 @@ class RequestContextTest extends MediaWikiTestCase {
                        $context->getRequest()->getAllHeaders(),
                        "Correct context headers."
                );
-               $this->assertEquals( $sinfo['sessionId'], session_id(), "Correct context session ID." );
+               $this->assertEquals(
+                       $sinfo['sessionId'],
+                       MediaWiki\Session\SessionManager::getGlobalSession()->getId(),
+                       "Correct context session ID."
+               );
+               if ( \MediaWiki\Session\PhpSessionHandler::isEnabled() ) {
+                       $this->assertEquals( $sinfo['sessionId'], session_id(), "Correct context session ID." );
+               } else {
+                       $this->assertEquals( $oldSessionId, session_id(), "Unchanged PHP session ID." );
+               }
                $this->assertEquals( true, $context->getUser()->isLoggedIn(), "Correct context user." );
                $this->assertEquals( $sinfo['userId'], $context->getUser()->getId(), "Correct context user ID." );
                $this->assertEquals(
index 31e4f5b..6403905 100644 (file)
@@ -256,4 +256,59 @@ class DatabaseMysqlBaseTest extends MediaWikiTestCase {
                $this->assertTrue( $pos2->hasReached( $pos1 ) );
                $this->assertFalse( $pos1->hasReached( $pos2 ) );
        }
+
+       /**
+        * @dataProvider provideLagAmounts
+        */
+       function testPtHeartbeat( $lag ) {
+               $db = $this->getMockBuilder( 'DatabaseMysql' )
+                       ->disableOriginalConstructor()
+                       ->setMethods( array(
+                               'getLagDetectionMethod', 'getHeartbeatData', 'getMasterServerInfo' ) )
+                       ->getMock();
+
+               $db->expects( $this->any() )
+                       ->method( 'getLagDetectionMethod' )
+                       ->will( $this->returnValue( 'pt-heartbeat' ) );
+
+               $db->expects( $this->any() )
+                       ->method( 'getMasterServerInfo' )
+                       ->will( $this->returnValue( array( 'serverId' => 172, 'asOf' => time() ) ) );
+
+               // Fake the current time.
+               list( $nowSecFrac, $nowSec ) = explode( ' ', microtime() );
+               $now = (float)$nowSec + (float)$nowSecFrac;
+               // Fake the heartbeat time.
+               // Work arounds for weak DataTime microseconds support.
+               $ptTime = $now - $lag;
+               $ptSec = (int)$ptTime;
+               $ptSecFrac = ( $ptTime - $ptSec );
+               $ptDateTime = new DateTime( "@$ptSec" );
+               $ptTimeISO = $ptDateTime->format( 'Y-m-d\TH:i:s' );
+               $ptTimeISO .= ltrim( number_format( $ptSecFrac, 6 ), '0' );
+
+               $db->expects( $this->any() )
+                       ->method( 'getHeartbeatData' )
+                       ->with( 172 )
+                       ->will( $this->returnValue( array( $ptTimeISO, $now ) ) );
+
+               $db->setLBInfo( 'clusterMasterHost', 'db1052' );
+               $lagEst = $db->getLag();
+
+               $this->assertGreaterThan( $lag - .010, $lagEst, "Correct heatbeat lag" );
+               $this->assertLessThan( $lag + .010, $lagEst, "Correct heatbeat lag" );
+       }
+
+       function provideLagAmounts() {
+               return array(
+                       array( 0 ),
+                       array( 0.3 ),
+                       array( 6.5 ),
+                       array( 10.1 ),
+                       array( 200.2 ),
+                       array( 400.7 ),
+                       array( 600.22 ),
+                       array( 1000.77 ),
+               );
+       }
 }
index 519d3a0..a647445 100644 (file)
@@ -103,9 +103,13 @@ class LBFactoryTest extends MediaWikiTestCase {
 
                $dbw = $lb->getConnection( DB_MASTER );
                $this->assertTrue( $dbw->getLBInfo( 'master' ), 'master shows as master' );
+               $this->assertEquals(
+                       $wgDBserver, $dbw->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
 
                $dbr = $lb->getConnection( DB_SLAVE );
                $this->assertTrue( $dbr->getLBInfo( 'slave' ), 'slave shows as slave' );
+               $this->assertEquals(
+                       $wgDBserver, $dbr->getLBInfo( 'clusterMasterHost' ), 'cluster master set' );
 
                $factory->shutdown();
                $lb->closeAll();
index 9866ce1..d2b267a 100644 (file)
@@ -167,7 +167,6 @@ class KafkaHandlerTest extends MediaWikiTestCase {
                }
        }
 
-
        public function testBatchHandlesNullFormatterResult() {
                $produce = $this->getMockBuilder( 'Kafka\Produce' )
                        ->disableOriginalConstructor()
index 66fe90c..0aef146 100644 (file)
@@ -60,6 +60,4 @@ class HttpErrorTest extends MediaWikiTestCase {
                        )
                );
        }
-
-
 }
diff --git a/tests/phpunit/includes/import/ImportLinkCacheIntegrationTest.php b/tests/phpunit/includes/import/ImportLinkCacheIntegrationTest.php
new file mode 100644 (file)
index 0000000..5e3c626
--- /dev/null
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Integration test that checks import success and
+ * LinkCache integration.
+ *
+ * @group medium
+ * @group Database
+ *
+ * @author mwjames
+ */
+class ImportLinkCacheIntegrationTest extends MediaWikiTestCase {
+
+       private $importStreamSource;
+
+       protected function setUp() {
+               parent::setUp();
+
+               $file = dirname( __DIR__ ) . '/../data/import/ImportLinkCacheIntegrationTest.xml';
+
+               $this->importStreamSource = ImportStreamSource::newFromFile( $file );
+
+               if ( !$this->importStreamSource->isGood() ) {
+                       throw new Exception( "Import source for {$file} failed" );
+               }
+       }
+
+       public function testImportForImportSource() {
+
+               $this->doImport( $this->importStreamSource );
+
+               // Imported title
+               $loremIpsum = Title::newFromText( 'Lorem ipsum' );
+
+               $this->assertSame(
+                       $loremIpsum->getArticleID(),
+                       $loremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
+               );
+
+               $categoryLoremIpsum = Title::newFromText( 'Category:Lorem ipsum' );
+
+               $this->assertSame(
+                       $categoryLoremIpsum->getArticleID(),
+                       $categoryLoremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
+               );
+
+               $page = new WikiPage( $loremIpsum );
+               $page->doDeleteArticle( 'import test: delete page' );
+
+               $page = new WikiPage( $categoryLoremIpsum );
+               $page->doDeleteArticle( 'import test: delete page' );
+       }
+
+       /**
+        * @depends testImportForImportSource
+        */
+       public function testReImportForImportSource() {
+
+               $this->doImport( $this->importStreamSource );
+
+               // ReImported title
+               $loremIpsum = Title::newFromText( 'Lorem ipsum' );
+
+               $this->assertSame(
+                       $loremIpsum->getArticleID(),
+                       $loremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
+               );
+
+               $categoryLoremIpsum = Title::newFromText( 'Category:Lorem ipsum' );
+
+               $this->assertSame(
+                       $categoryLoremIpsum->getArticleID(),
+                       $categoryLoremIpsum->getArticleID( Title::GAID_FOR_UPDATE )
+               );
+       }
+
+       private function doImport( $importStreamSource ) {
+
+               $importer = new WikiImporter(
+                       $importStreamSource->value,
+                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
+               );
+               $importer->setDebug( true );
+
+               $reporter = new ImportReporter(
+                       $importer,
+                       false,
+                       '',
+                       false
+               );
+
+               $reporter->setContext( new RequestContext() );
+               $reporter->open();
+               $exception = false;
+
+               try {
+                       $importer->doImport();
+               } catch ( Exception $e ) {
+                       $exception = $e;
+               }
+
+               $result = $reporter->close();
+
+               $this->assertFalse(
+                       $exception
+               );
+
+               $this->assertTrue(
+                       $result->isGood()
+               );
+       }
+
+}
diff --git a/tests/phpunit/includes/import/ImportTest.php b/tests/phpunit/includes/import/ImportTest.php
new file mode 100644 (file)
index 0000000..f4aac23
--- /dev/null
@@ -0,0 +1,222 @@
+<?php
+
+/**
+ * Test class for Import methods.
+ *
+ * @group Database
+ *
+ * @author Sebastian Brückner < sebastian.brueckner@student.hpi.uni-potsdam.de >
+ */
+class ImportTest extends MediaWikiLangTestCase {
+
+       private function getDataSource( $xml ) {
+               return new ImportStringSource( $xml );
+       }
+
+       /**
+        * @covers WikiImporter
+        * @dataProvider getUnknownTagsXML
+        * @param string $xml
+        * @param string $text
+        * @param string $title
+        */
+       public function testUnknownXMLTags( $xml, $text, $title ) {
+               $source = $this->getDataSource( $xml );
+
+               $importer = new WikiImporter(
+                       $source,
+                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
+               );
+
+               $importer->doImport();
+               $title = Title::newFromText( $title );
+               $this->assertTrue( $title->exists() );
+
+               $this->assertEquals( WikiPage::factory( $title )->getContent()->getNativeData(), $text );
+       }
+
+       public function getUnknownTagsXML() {
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               return array(
+                       array(
+                               <<< EOF
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
+  <page unknown="123" dontknow="533">
+    <title>TestImportPage</title>
+    <unknowntag>Should be ignored</unknowntag>
+    <ns>0</ns>
+    <id unknown="123" dontknow="533">14</id>
+    <revision>
+      <id unknown="123" dontknow="533">15</id>
+      <unknowntag>Should be ignored</unknowntag>
+      <timestamp>2016-01-03T11:18:43Z</timestamp>
+      <contributor>
+        <unknowntag>Should be ignored</unknowntag>
+        <username unknown="123" dontknow="533">Admin</username>
+        <id>1</id>
+      </contributor>
+      <model>wikitext</model>
+      <format>text/x-wiki</format>
+      <text xml:space="preserve" bytes="0">noitazinagro tseb eht si ikiWaideM</text>
+      <sha1>phoiac9h4m842xq45sp7s6u21eteeq1</sha1>
+      <unknowntag>Should be ignored</unknowntag>
+    </revision>
+  </page>
+  <unknowntag>Should be ignored</unknowntag>
+</mediawiki>
+EOF
+                               ,
+                               'noitazinagro tseb eht si ikiWaideM',
+                               'TestImportPage'
+                       )
+               );
+               // @codingStandardsIgnoreEnd
+       }
+
+       /**
+        * @covers WikiImporter::handlePage
+        * @dataProvider getRedirectXML
+        * @param string $xml
+        * @param string|null $redirectTitle
+        */
+       public function testHandlePageContainsRedirect( $xml, $redirectTitle ) {
+               $source = $this->getDataSource( $xml );
+
+               $redirect = null;
+               $callback = function ( Title $title, ForeignTitle $foreignTitle, $revCount,
+                       $sRevCount, $pageInfo ) use ( &$redirect ) {
+                       if ( array_key_exists( 'redirect', $pageInfo ) ) {
+                               $redirect = $pageInfo['redirect'];
+                       }
+               };
+
+               $importer = new WikiImporter(
+                       $source,
+                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
+               );
+               $importer->setPageOutCallback( $callback );
+               $importer->doImport();
+
+               $this->assertEquals( $redirectTitle, $redirect );
+       }
+
+       public function getRedirectXML() {
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               return array(
+                       array(
+                               <<< EOF
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
+       <page>
+               <title>Test</title>
+               <ns>0</ns>
+               <id>21</id>
+               <redirect title="Test22"/>
+               <revision>
+                       <id>20</id>
+                       <timestamp>2014-05-27T10:00:00Z</timestamp>
+                       <contributor>
+                               <username>Admin</username>
+                               <id>10</id>
+                       </contributor>
+                       <comment>Admin moved page [[Test]] to [[Test22]]</comment>
+                       <model>wikitext</model>
+                       <format>text/x-wiki</format>
+                       <text xml:space="preserve" bytes="20">#REDIRECT [[Test22]]</text>
+                       <sha1>tq456o9x3abm7r9ozi6km8yrbbc56o6</sha1>
+               </revision>
+       </page>
+</mediawiki>
+EOF
+                       ,
+                               'Test22'
+                       ),
+                       array(
+                               <<< EOF
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.9/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.9/ http://www.mediawiki.org/xml/export-0.9.xsd" version="0.9" xml:lang="en">
+       <page>
+               <title>Test</title>
+               <ns>0</ns>
+               <id>42</id>
+               <revision>
+                       <id>421</id>
+                       <timestamp>2014-05-27T11:00:00Z</timestamp>
+                       <contributor>
+                               <username>Admin</username>
+                               <id>10</id>
+                       </contributor>
+                       <text xml:space="preserve" bytes="4">Abcd</text>
+                       <sha1>n7uomjq96szt60fy5w3x7ahf7q8m8rh</sha1>
+                       <model>wikitext</model>
+                       <format>text/x-wiki</format>
+               </revision>
+       </page>
+</mediawiki>
+EOF
+                       ,
+                               null
+                       ),
+               );
+               // @codingStandardsIgnoreEnd
+       }
+
+       /**
+        * @covers WikiImporter::handleSiteInfo
+        * @dataProvider getSiteInfoXML
+        * @param string $xml
+        * @param array|null $namespaces
+        */
+       public function testSiteInfoContainsNamespaces( $xml, $namespaces ) {
+               $source = $this->getDataSource( $xml );
+
+               $importNamespaces = null;
+               $callback = function ( array $siteinfo, $innerImporter ) use ( &$importNamespaces ) {
+                       $importNamespaces = $siteinfo['_namespaces'];
+               };
+
+               $importer = new WikiImporter(
+                       $source,
+                       ConfigFactory::getDefaultInstance()->makeConfig( 'main' )
+               );
+               $importer->setSiteInfoCallback( $callback );
+               $importer->doImport();
+
+               $this->assertEquals( $importNamespaces, $namespaces );
+       }
+
+       public function getSiteInfoXML() {
+               // @codingStandardsIgnoreStart Generic.Files.LineLength
+               return array(
+                       array(
+                               <<< EOF
+<mediawiki xmlns="http://www.mediawiki.org/xml/export-0.10/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mediawiki.org/xml/export-0.10/ http://www.mediawiki.org/xml/export-0.10.xsd" version="0.10" xml:lang="en">
+  <siteinfo>
+    <namespaces>
+      <namespace key="-2" case="first-letter">Media</namespace>
+      <namespace key="-1" case="first-letter">Special</namespace>
+      <namespace key="0" case="first-letter" />
+      <namespace key="1" case="first-letter">Talk</namespace>
+      <namespace key="2" case="first-letter">User</namespace>
+      <namespace key="3" case="first-letter">User talk</namespace>
+      <namespace key="100" case="first-letter">Portal</namespace>
+      <namespace key="101" case="first-letter">Portal talk</namespace>
+    </namespaces>
+  </siteinfo>
+</mediawiki>
+EOF
+                       ,
+                               array(
+                                       '-2' => 'Media',
+                                       '-1' => 'Special',
+                                       '0' => '',
+                                       '1' => 'Talk',
+                                       '2' => 'User',
+                                       '3' => 'User talk',
+                                       '100' => 'Portal',
+                                       '101' => 'Portal talk',
+                               )
+                       ),
+               );
+               // @codingStandardsIgnoreEnd
+       }
+
+}
diff --git a/tests/phpunit/includes/jobqueue/JobQueueMemoryTest.php b/tests/phpunit/includes/jobqueue/JobQueueMemoryTest.php
new file mode 100644 (file)
index 0000000..178a6a6
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @covers JobQueueMemory
+ *
+ * @group JobQueue
+ *
+ * @licence GNU GPL v2+
+ * @author Thiemo Mättig
+ */
+class JobQueueMemoryTest extends PHPUnit_Framework_TestCase {
+
+       /**
+        * @return JobQueueMemory
+        */
+       private function newJobQueue() {
+               return JobQueue::factory( array(
+                       'class' => 'JobQueueMemory',
+                       'wiki' => wfWikiID(),
+                       'type' => 'null',
+               ) );
+       }
+
+       private function newJobSpecification() {
+               return new JobSpecification(
+                       'null',
+                       array( 'customParameter' => null ),
+                       array(),
+                       Title::newFromText( 'Custom title' )
+               );
+       }
+
+       public function testGetAllQueuedJobs() {
+               $queue = $this->newJobQueue();
+               $this->assertCount( 0, $queue->getAllQueuedJobs() );
+
+               $queue->push( $this->newJobSpecification() );
+               $this->assertCount( 1, $queue->getAllQueuedJobs() );
+       }
+
+       public function testGetAllAcquiredJobs() {
+               $queue = $this->newJobQueue();
+               $this->assertCount( 0, $queue->getAllAcquiredJobs() );
+
+               $queue->push( $this->newJobSpecification() );
+               $this->assertCount( 0, $queue->getAllAcquiredJobs() );
+
+               $queue->pop();
+               $this->assertCount( 1, $queue->getAllAcquiredJobs() );
+       }
+
+       public function testJobFromSpecInternal() {
+               $queue = $this->newJobQueue();
+               $job = $queue->jobFromSpecInternal( $this->newJobSpecification() );
+               $this->assertInstanceOf( 'Job', $job );
+               $this->assertSame( 'null', $job->getType() );
+               $this->assertArrayHasKey( 'customParameter', $job->getParams() );
+               $this->assertSame( 'Custom title', $job->getTitle()->getText() );
+       }
+
+}
index 921bba8..6edb3d8 100644 (file)
@@ -20,7 +20,6 @@ class ArrayBackedMemoizedCallable extends MemoizedCallable {
        }
 }
 
-
 /**
  * PHP Unit tests for MemoizedCallable class.
  * @covers MemoizedCallable
index 94b74cb..b9fd6ab 100644 (file)
@@ -183,6 +183,18 @@ class BagOStuffTest extends MediaWikiTestCase {
                $this->assertEquals( $expectedValue, $actualValue, 'Value should be 1 after incrementing' );
        }
 
+       /**
+        * @covers BagOStuff::incrWithInit
+        */
+       public function testIncrWithInit() {
+               $key = wfMemcKey( 'test' );
+               $val = $this->cache->incrWithInit( $key, 0, 1, 3 );
+               $this->assertEquals( 3, $val, "Correct init value" );
+
+               $val = $this->cache->incrWithInit( $key, 0, 1, 3 );
+               $this->assertEquals( 4, $val, "Correct init value" );
+       }
+
        /**
         * @covers BagOStuff::getMulti
         */
index 17decf3..8010b77 100644 (file)
@@ -160,7 +160,6 @@ class ProtectLogFormatterTest extends LogFormatterTestCase {
                );
        }
 
-
        /**
         * @dataProvider provideProtectLogDatabaseRows
         */
@@ -329,7 +328,6 @@ class ProtectLogFormatterTest extends LogFormatterTestCase {
                );
        }
 
-
        /**
         * @dataProvider provideModifyLogDatabaseRows
         */
@@ -362,7 +360,6 @@ class ProtectLogFormatterTest extends LogFormatterTestCase {
                );
        }
 
-
        /**
         * @dataProvider provideUnprotectLogDatabaseRows
         */
index 8f28158..cb10be3 100644 (file)
@@ -11,7 +11,6 @@ abstract class MediaWikiMediaTestCase extends MediaWikiTestCase {
        /** @var string */
        protected $filePath;
 
-
        protected function setUp() {
                parent::setUp();
 
index 5b2de15..536827a 100644 (file)
@@ -13,7 +13,6 @@ class XCFHandlerTest extends MediaWikiMediaTestCase {
                $this->handler = new XCFHandler();
        }
 
-
        /**
         * @param string $filename
         * @param int $expectedWidth Width
index 0a46f8a..7c0dd2e 100644 (file)
@@ -370,24 +370,6 @@ class WikiPageTest extends MediaWikiLangTestCase {
                $this->assertEquals( "some text", $text );
        }
 
-       /**
-        * @covers WikiPage::getRawText
-        */
-       public function testGetRawText() {
-               $this->hideDeprecated( "WikiPage::getRawText" );
-
-               $page = $this->newPage( "WikiPageTest_testGetRawText" );
-
-               $text = $page->getRawText();
-               $this->assertFalse( $text );
-
-               # -----------------
-               $this->createPage( $page, "some text", CONTENT_MODEL_WIKITEXT );
-
-               $text = $page->getRawText();
-               $this->assertEquals( "some text", $text );
-       }
-
        /**
         * @covers WikiPage::getContentModel
         */
@@ -1261,22 +1243,6 @@ more stuff
                );
        }
 
-       /**
-        * @dataProvider providePreSaveTransform
-        * @covers WikiPage::preSaveTransform
-        */
-       public function testPreSaveTransform( $text, $expected ) {
-               $this->hideDeprecated( 'WikiPage::preSaveTransform' );
-               $user = new User();
-               $user->setName( "127.0.0.1" );
-
-               // NOTE: assume Help namespace to contain wikitext
-               $page = $this->newPage( "Help:WikiPageTest_testPreloadTransform" );
-               $text = $page->preSaveTransform( $text, $user );
-
-               $this->assertEquals( $expected, $text );
-       }
-
        /**
         * @covers WikiPage::factory
         */
index 5c6c17d..256ad69 100644 (file)
@@ -304,6 +304,22 @@ class NewParserTest extends MediaWikiTestCase {
                        ), $this->db->timestamp( '20010115123500' ), $user );
                }
 
+               $image = wfLocalFile( Title::makeTitle( NS_FILE, 'Video.ogv' ) );
+               if ( !$this->db->selectField( 'image', '1', array( 'img_name' => $image->getName() ) ) ) {
+                       $image->recordUpload2( '', 'A pretty movie', 'Will it play', array(
+                                       'size'        => 12345,
+                                       'width'       => 240,
+                                       'height'      => 180,
+                                       'bits'        => 0,
+                                       'media_type'  => MEDIATYPE_VIDEO,
+                                       'mime'        => 'application/ogg',
+                                       'metadata'    => serialize( array() ),
+                                       'sha1'        => Wikimedia\base_convert( '', 16, 36, 31 ),
+                                       'fileExists'  => true
+                       ), $this->db->timestamp( '20010115123500' ), $user );
+               }
+
+               # A DjVu file
                # A DjVu file
                $image = wfLocalFile( Title::makeTitle( NS_FILE, 'LoremIpsum.djvu' ) );
                if ( !$this->db->selectField( 'image', '1', array( 'img_name' => $image->getName() ) ) ) {
index 1ebba1a..b940230 100644 (file)
@@ -48,7 +48,7 @@ class PreprocessorTest extends MediaWikiTestCase {
                        array( "<noinclude> Foo bar </noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore> Foo bar <ignore>&lt;/noinclude&gt;</ignore></root>" ),
                        array( "<noinclude>\n{{Foo}}\n</noinclude>", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore></root>" ),
                        array( "<noinclude>\n{{Foo}}\n</noinclude>\n", "<root><ignore>&lt;noinclude&gt;</ignore>\n<template lineStart=\"1\"><title>Foo</title></template>\n<ignore>&lt;/noinclude&gt;</ignore>\n</root>" ),
-                       array( "<gallery>foo bar", "<root><ext><name>gallery</name><attr></attr><inner>foo bar</inner></ext></root>" ),
+                       array( "<gallery>foo bar", "<root>&lt;gallery&gt;foo bar</root>" ),
                        array( "<{{foo}}>", "<root>&lt;<template><title>foo</title></template>&gt;</root>" ),
                        array( "<{{{foo}}}>", "<root>&lt;<tplarg><title>foo</title></tplarg>&gt;</root>" ),
                        array( "<gallery></gallery</gallery>", "<root><ext><name>gallery</name><attr></attr><inner>&lt;/gallery</inner><close>&lt;/gallery&gt;</close></ext></root>" ),
index ddf552e..590644f 100644 (file)
@@ -118,7 +118,8 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                                '_prefix' => 'eg',
                                'Bar' => 'somevalue'
                        ),
-               ) + self::$default;
+                       'name' => 'FooBar2',
+               );
                $processor->extractInfo( $this->dir, $info, 1 );
                $processor->extractInfo( $this->dir, $info2, 1 );
                $extracted = $processor->getExtractedInfo();
@@ -166,7 +167,6 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                }
        }
 
-
        public static function provideExtractMessagesDirs() {
                $dir = __DIR__ . '/FooBar/';
                return array(
@@ -194,6 +194,16 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
                }
        }
 
+       /**
+        * @covers ExtensionProcessor::extractCredits
+        */
+       public function testExtractCredits() {
+               $processor = new ExtensionProcessor();
+               $processor->extractInfo( $this->dir, self::$default, 1 );
+               $this->setExpectedException( 'Exception' );
+               $processor->extractInfo( $this->dir, self::$default, 1 );
+       }
+
        /**
         * @covers ExtensionProcessor::extractResourceLoaderModules
         * @dataProvider provideExtractResourceLoaderModules
@@ -400,7 +410,6 @@ class ExtensionProcessorTest extends MediaWikiTestCase {
        }
 }
 
-
 /**
  * Allow overriding the default value of $this->globals
  * so we can test merging
index 201cbfc..543eb5c 100644 (file)
@@ -25,6 +25,7 @@ class ExtensionRegistryTest extends MediaWikiTestCase {
                        'defines' => array(),
                        'credits' => array(),
                        'attributes' => array(),
+                       'autoloaderPaths' => array()
                );
                $registry = new ExtensionRegistry();
                $class = new ReflectionClass( 'ExtensionRegistry' );
diff --git a/tests/phpunit/includes/session/BotPasswordSessionProviderTest.php b/tests/phpunit/includes/session/BotPasswordSessionProviderTest.php
new file mode 100644 (file)
index 0000000..52872a4
--- /dev/null
@@ -0,0 +1,288 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LogLevel;
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\BotPasswordSessionProvider
+ */
+class BotPasswordSessionProviderTest extends MediaWikiTestCase {
+
+       private $config;
+
+       private function getProvider( $name = null, $prefix = null ) {
+               global $wgSessionProviders;
+
+               $params = array(
+                       'priority' => 40,
+                       'sessionCookieName' => $name,
+                       'sessionCookieOptions' => array(),
+               );
+               if ( $prefix !== null ) {
+                       $params['sessionCookieOptions']['prefix'] = $prefix;
+               }
+
+               if ( !$this->config ) {
+                       $this->config = new \HashConfig( array(
+                               'CookiePrefix' => 'wgCookiePrefix',
+                               'EnableBotPasswords' => true,
+                               'BotPasswordsDatabase' => false,
+                               'SessionProviders' => $wgSessionProviders + array(
+                                       'MediaWiki\\Session\\BotPasswordSessionProvider' => array(
+                                               'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider',
+                                               'args' => array( $params ),
+                                       )
+                               ),
+                       ) );
+               }
+               $manager = new SessionManager( array(
+                       'config' => new \MultiConfig( array( $this->config, \RequestContext::getMain()->getConfig() ) ),
+                       'logger' => new \Psr\Log\NullLogger,
+                       'store' => new TestBagOStuff,
+               ) );
+
+               return $manager->getProvider( 'MediaWiki\\Session\\BotPasswordSessionProvider' );
+       }
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( array(
+                       'wgEnableBotPasswords' => true,
+                       'wgBotPasswordsDatabase' => false,
+                       'wgCentralIdLookupProvider' => 'local',
+                       'wgGrantPermissions' => array(
+                               'test' => array( 'read' => true ),
+                       ),
+               ) );
+       }
+
+       public function addDBData() {
+               $passwordFactory = new \PasswordFactory();
+               $passwordFactory->init( \RequestContext::getMain()->getConfig() );
+               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
+               $passwordFactory->setDefaultType( 'A' );
+               $pwhash = $passwordFactory->newFromPlaintext( 'foobaz' );
+
+               $userId = \CentralIdLookup::factory( 'local' )->centralIdFromName( 'UTSysop' );
+
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete(
+                       'bot_passwords',
+                       array( 'bp_user' => $userId, 'bp_app_id' => 'BotPasswordSessionProvider' ),
+                       __METHOD__
+               );
+               $dbw->insert(
+                       'bot_passwords',
+                       array(
+                               'bp_user' => $userId,
+                               'bp_app_id' => 'BotPasswordSessionProvider',
+                               'bp_password' => $pwhash->toString(),
+                               'bp_token' => 'token!',
+                               'bp_restrictions' => '{"IPAddresses":["127.0.0.0/8"]}',
+                               'bp_grants' => '["test"]',
+                       ),
+                       __METHOD__
+               );
+       }
+
+       public function testConstructor() {
+               try {
+                       $provider = new BotPasswordSessionProvider();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\BotPasswordSessionProvider::__construct: priority must be specified',
+                               $ex->getMessage()
+                       );
+               }
+
+               try {
+                       $provider = new BotPasswordSessionProvider( array(
+                               'priority' => SessionInfo::MIN_PRIORITY - 1
+                       ) );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\BotPasswordSessionProvider::__construct: Invalid priority',
+                               $ex->getMessage()
+                       );
+               }
+
+               try {
+                       $provider = new BotPasswordSessionProvider( array(
+                               'priority' => SessionInfo::MAX_PRIORITY + 1
+                       ) );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\BotPasswordSessionProvider::__construct: Invalid priority',
+                               $ex->getMessage()
+                       );
+               }
+
+               $provider = new BotPasswordSessionProvider( array(
+                       'priority' => 40
+               ) );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+               $this->assertSame( 40, $priv->priority );
+               $this->assertSame( '_BPsession', $priv->sessionCookieName );
+               $this->assertSame( array(), $priv->sessionCookieOptions );
+
+               $provider = new BotPasswordSessionProvider( array(
+                       'priority' => 40,
+                       'sessionCookieName' => null,
+               ) );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+               $this->assertSame( '_BPsession', $priv->sessionCookieName );
+
+               $provider = new BotPasswordSessionProvider( array(
+                       'priority' => 40,
+                       'sessionCookieName' => 'Foo',
+                       'sessionCookieOptions' => array( 'Bar' ),
+               ) );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+               $this->assertSame( 'Foo', $priv->sessionCookieName );
+               $this->assertSame( array( 'Bar' ), $priv->sessionCookieOptions );
+       }
+
+       public function testBasics() {
+               $provider = $this->getProvider();
+
+               $this->assertTrue( $provider->persistsSessionID() );
+               $this->assertFalse( $provider->canChangeUser() );
+
+               $this->assertNull( $provider->newSessionInfo() );
+               $this->assertNull( $provider->newSessionInfo( 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' ) );
+       }
+
+       public function testProvideSessionInfo() {
+               $provider = $this->getProvider();
+               $request = new \FauxRequest;
+               $request->setCookie( '_BPsession', 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'wgCookiePrefix' );
+
+               if ( !defined( 'MW_API' ) ) {
+                       $this->assertNull( $provider->provideSessionInfo( $request ) );
+                       define( 'MW_API', 1 );
+               }
+
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\SessionInfo', $info );
+               $this->assertSame( 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', $info->getId() );
+
+               $this->config->set( 'EnableBotPasswords', false );
+               $this->assertNull( $provider->provideSessionInfo( $request ) );
+               $this->config->set( 'EnableBotPasswords', true );
+
+               $this->assertNull( $provider->provideSessionInfo( new \FauxRequest ) );
+       }
+
+       public function testNewSessionInfoForRequest() {
+               $provider = $this->getProvider();
+               $user = \User::newFromName( 'UTSysop' );
+               $request = $this->getMock( 'FauxRequest', array( 'getIP' ) );
+               $request->expects( $this->any() )->method( 'getIP' )
+                       ->will( $this->returnValue( '127.0.0.1' ) );
+               $bp = \BotPassword::newFromUser( $user, 'BotPasswordSessionProvider' );
+
+               $session = $provider->newSessionForRequest( $user, $bp, $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+
+               $this->assertEquals( $session->getId(), $request->getSession()->getId() );
+               $this->assertEquals( $user->getName(), $session->getUser()->getName() );
+
+               $this->assertEquals( array(
+                       'centralId' => $bp->getUserCentralId(),
+                       'appId' => $bp->getAppId(),
+                       'token' => $bp->getToken(),
+                       'rights' => array( 'read' ),
+               ), $session->getProviderMetadata() );
+
+               $this->assertEquals( array( 'read' ), $session->getAllowedUserRights() );
+       }
+
+       public function testCheckSessionInfo() {
+               $logger = new \TestLogger( true, function ( $m ) {
+                       return preg_replace(
+                               '/^Session \[\d+\][a-zA-Z0-9_\\\\]+<(?:null|anon|[+-]:\d+:\w+)>\w+: /', 'Session X: ', $m
+                       );
+               } );
+               $provider = $this->getProvider();
+               $provider->setLogger( $logger );
+
+               $user = \User::newFromName( 'UTSysop' );
+               $request = $this->getMock( 'FauxRequest', array( 'getIP' ) );
+               $request->expects( $this->any() )->method( 'getIP' )
+                       ->will( $this->returnValue( '127.0.0.1' ) );
+               $bp = \BotPassword::newFromUser( $user, 'BotPasswordSessionProvider' );
+
+               $data = array(
+                       'provider' => $provider,
+                       'id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
+                       'userInfo' => UserInfo::newFromUser( $user, true ),
+                       'persisted' => false,
+                       'metadata' => array(
+                               'centralId' => $bp->getUserCentralId(),
+                               'appId' => $bp->getAppId(),
+                               'token' => $bp->getToken(),
+                       ),
+               );
+               $dataMD = $data['metadata'];
+
+               foreach ( array_keys( $data['metadata'] ) as $key ) {
+                       $data['metadata'] = $dataMD;
+                       unset( $data['metadata'][$key] );
+                       $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
+                       $metadata = $info->getProviderMetadata();
+
+                       $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
+                       $this->assertSame( array(
+                               array( LogLevel::INFO, "Session X: Missing metadata: $key" )
+                       ), $logger->getBuffer() );
+                       $logger->clearBuffer();
+               }
+
+               $data['metadata'] = $dataMD;
+               $data['metadata']['appId'] = 'Foobar';
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
+               $metadata = $info->getProviderMetadata();
+               $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
+               $this->assertSame( array(
+                       array( LogLevel::INFO, "Session X: No BotPassword for {$bp->getUserCentralId()} Foobar" ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $data['metadata'] = $dataMD;
+               $data['metadata']['token'] = 'Foobar';
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
+               $metadata = $info->getProviderMetadata();
+               $this->assertFalse( $provider->refreshSessionInfo( $info, $request, $metadata ) );
+               $this->assertSame( array(
+                       array( LogLevel::INFO, 'Session X: BotPassword token check failed' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $request2 = $this->getMock( 'FauxRequest', array( 'getIP' ) );
+               $request2->expects( $this->any() )->method( 'getIP' )
+                       ->will( $this->returnValue( '10.0.0.1' ) );
+               $data['metadata'] = $dataMD;
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
+               $metadata = $info->getProviderMetadata();
+               $this->assertFalse( $provider->refreshSessionInfo( $info, $request2, $metadata ) );
+               $this->assertSame( array(
+                       array( LogLevel::INFO, 'Session X: Restrictions check failed' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, $data );
+               $metadata = $info->getProviderMetadata();
+               $this->assertTrue( $provider->refreshSessionInfo( $info, $request, $metadata ) );
+               $this->assertSame( array(), $logger->getBuffer() );
+               $this->assertEquals( $dataMD + array( 'rights' => array( 'read' ) ), $metadata );
+       }
+}
diff --git a/tests/phpunit/includes/session/CookieSessionProviderTest.php b/tests/phpunit/includes/session/CookieSessionProviderTest.php
new file mode 100644 (file)
index 0000000..a73bf7c
--- /dev/null
@@ -0,0 +1,726 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\CookieSessionProvider
+ */
+class CookieSessionProviderTest extends MediaWikiTestCase {
+
+       private function getConfig() {
+               global $wgCookieExpiration;
+               return new \HashConfig( array(
+                       'CookiePrefix' => 'CookiePrefix',
+                       'CookiePath' => 'CookiePath',
+                       'CookieDomain' => 'CookieDomain',
+                       'CookieSecure' => true,
+                       'CookieHttpOnly' => true,
+                       'SessionName' => false,
+                       'ExtendedLoginCookies' => array( 'UserID', 'Token' ),
+                       'ExtendedLoginCookieExpiration' => $wgCookieExpiration * 2,
+               ) );
+       }
+
+       public function testConstructor() {
+               try {
+                       new CookieSessionProvider();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\CookieSessionProvider::__construct: priority must be specified',
+                               $ex->getMessage()
+                       );
+               }
+
+               try {
+                       new CookieSessionProvider( array( 'priority' => 'foo' ) );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
+                               $ex->getMessage()
+                       );
+               }
+               try {
+                       new CookieSessionProvider( array( 'priority' => SessionInfo::MIN_PRIORITY - 1 ) );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
+                               $ex->getMessage()
+                       );
+               }
+               try {
+                       new CookieSessionProvider( array( 'priority' => SessionInfo::MAX_PRIORITY + 1 ) );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\CookieSessionProvider::__construct: Invalid priority',
+                               $ex->getMessage()
+                       );
+               }
+
+               try {
+                       new CookieSessionProvider( array( 'priority' => 1, 'cookieOptions' => null ) );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\CookieSessionProvider::__construct: cookieOptions must be an array',
+                               $ex->getMessage()
+                       );
+               }
+
+               $config = $this->getConfig();
+               $p = \TestingAccessWrapper::newFromObject(
+                       new CookieSessionProvider( array( 'priority' => 1 ) )
+               );
+               $p->setLogger( new \TestLogger() );
+               $p->setConfig( $config );
+               $this->assertEquals( 1, $p->priority );
+               $this->assertEquals( array(
+                       'callUserSetCookiesHook' => false,
+                       'sessionName' => 'CookiePrefix_session',
+               ), $p->params );
+               $this->assertEquals( array(
+                       'prefix' => 'CookiePrefix',
+                       'path' => 'CookiePath',
+                       'domain' => 'CookieDomain',
+                       'secure' => true,
+                       'httpOnly' => true,
+               ), $p->cookieOptions );
+
+               $config->set( 'SessionName', 'SessionName' );
+               $p = \TestingAccessWrapper::newFromObject(
+                       new CookieSessionProvider( array( 'priority' => 3 ) )
+               );
+               $p->setLogger( new \TestLogger() );
+               $p->setConfig( $config );
+               $this->assertEquals( 3, $p->priority );
+               $this->assertEquals( array(
+                       'callUserSetCookiesHook' => false,
+                       'sessionName' => 'SessionName',
+               ), $p->params );
+               $this->assertEquals( array(
+                       'prefix' => 'CookiePrefix',
+                       'path' => 'CookiePath',
+                       'domain' => 'CookieDomain',
+                       'secure' => true,
+                       'httpOnly' => true,
+               ), $p->cookieOptions );
+
+               $p = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( array(
+                       'priority' => 10,
+                       'callUserSetCookiesHook' => true,
+                       'cookieOptions' => array(
+                               'prefix' => 'XPrefix',
+                               'path' => 'XPath',
+                               'domain' => 'XDomain',
+                               'secure' => 'XSecure',
+                               'httpOnly' => 'XHttpOnly',
+                       ),
+                       'sessionName' => 'XSession',
+               ) ) );
+               $p->setLogger( new \TestLogger() );
+               $p->setConfig( $config );
+               $this->assertEquals( 10, $p->priority );
+               $this->assertEquals( array(
+                       'callUserSetCookiesHook' => true,
+                       'sessionName' => 'XSession',
+               ), $p->params );
+               $this->assertEquals( array(
+                       'prefix' => 'XPrefix',
+                       'path' => 'XPath',
+                       'domain' => 'XDomain',
+                       'secure' => 'XSecure',
+                       'httpOnly' => 'XHttpOnly',
+               ), $p->cookieOptions );
+       }
+
+       public function testBasics() {
+               $provider = new CookieSessionProvider( array( 'priority' => 10 ) );
+
+               $this->assertTrue( $provider->persistsSessionID() );
+               $this->assertTrue( $provider->canChangeUser() );
+
+               $msg = $provider->whyNoSession();
+               $this->assertInstanceOf( 'Message', $msg );
+               $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
+       }
+
+       public function testProvideSessionInfo() {
+               $params = array(
+                       'priority' => 20,
+                       'sessionName' => 'session',
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               );
+               $provider = new CookieSessionProvider( $params );
+               $provider->setLogger( new \TestLogger() );
+               $provider->setConfig( $this->getConfig() );
+               $provider->setManager( new SessionManager() );
+
+               $user = User::newFromName( 'UTSysop' );
+               $id = $user->getId();
+               $name = $user->getName();
+               $token = $user->getToken( true );
+
+               $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+
+               // No data
+               $request = new \FauxRequest();
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNull( $info );
+
+               // Session key only
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNotNull( $info );
+               $this->assertSame( $params['priority'], $info->getPriority() );
+               $this->assertSame( $sessionId, $info->getId() );
+               $this->assertNull( $info->getUserInfo() );
+               $this->assertFalse( $info->forceHTTPS() );
+
+               // User, no session key
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'xUserID' => $id,
+                       'xToken' => $token,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNotNull( $info );
+               $this->assertSame( $params['priority'], $info->getPriority() );
+               $this->assertNotSame( $sessionId, $info->getId() );
+               $this->assertNotNull( $info->getUserInfo() );
+               $this->assertSame( $id, $info->getUserInfo()->getId() );
+               $this->assertSame( $name, $info->getUserInfo()->getName() );
+               $this->assertFalse( $info->forceHTTPS() );
+
+               // User and session key
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => $id,
+                       'xToken' => $token,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNotNull( $info );
+               $this->assertSame( $params['priority'], $info->getPriority() );
+               $this->assertSame( $sessionId, $info->getId() );
+               $this->assertNotNull( $info->getUserInfo() );
+               $this->assertSame( $id, $info->getUserInfo()->getId() );
+               $this->assertSame( $name, $info->getUserInfo()->getName() );
+               $this->assertFalse( $info->forceHTTPS() );
+
+               // User with bad token
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => $id,
+                       'xToken' => 'BADTOKEN',
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNull( $info );
+
+               // User id with no token
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => $id,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNotNull( $info );
+               $this->assertSame( $params['priority'], $info->getPriority() );
+               $this->assertSame( $sessionId, $info->getId() );
+               $this->assertNotNull( $info->getUserInfo() );
+               $this->assertFalse( $info->getUserInfo()->isVerified() );
+               $this->assertSame( $id, $info->getUserInfo()->getId() );
+               $this->assertSame( $name, $info->getUserInfo()->getName() );
+               $this->assertFalse( $info->forceHTTPS() );
+
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'xUserID' => $id,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNull( $info );
+
+               // User and session key, with forceHTTPS flag
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => $id,
+                       'xToken' => $token,
+                       'forceHTTPS' => true,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNotNull( $info );
+               $this->assertSame( $params['priority'], $info->getPriority() );
+               $this->assertSame( $sessionId, $info->getId() );
+               $this->assertNotNull( $info->getUserInfo() );
+               $this->assertSame( $id, $info->getUserInfo()->getId() );
+               $this->assertSame( $name, $info->getUserInfo()->getName() );
+               $this->assertTrue( $info->forceHTTPS() );
+
+               // Invalid user id
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => '-1',
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNull( $info );
+
+               // User id with matching name
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => $id,
+                       'xUserName' => $name,
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNotNull( $info );
+               $this->assertSame( $params['priority'], $info->getPriority() );
+               $this->assertSame( $sessionId, $info->getId() );
+               $this->assertNotNull( $info->getUserInfo() );
+               $this->assertFalse( $info->getUserInfo()->isVerified() );
+               $this->assertSame( $id, $info->getUserInfo()->getId() );
+               $this->assertSame( $name, $info->getUserInfo()->getName() );
+               $this->assertFalse( $info->forceHTTPS() );
+
+               // User id with wrong name
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'session' => $sessionId,
+                       'xUserID' => $id,
+                       'xUserName' => 'Wrong',
+               ), '' );
+               $info = $provider->provideSessionInfo( $request );
+               $this->assertNull( $info );
+       }
+
+       public function testGetVaryCookies() {
+               $provider = new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'cookieOptions' => array( 'prefix' => 'MyCookiePrefix' ),
+               ) );
+               $this->assertArrayEquals( array(
+                       'MyCookiePrefixToken',
+                       'MyCookiePrefixLoggedOut',
+                       'MySessionName',
+                       'forceHTTPS',
+               ), $provider->getVaryCookies() );
+       }
+
+       public function testSuggestLoginUsername() {
+               $provider = new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               ) );
+
+               $request = new \FauxRequest();
+               $this->assertEquals( null, $provider->suggestLoginUsername( $request ) );
+
+               $request->setCookies( array(
+                       'xUserName' => 'Example',
+               ), '' );
+               $this->assertEquals( 'Example', $provider->suggestLoginUsername( $request ) );
+       }
+
+       public function testPersistSession() {
+               $this->setMwGlobals( array( 'wgCookieExpiration' => 100 ) );
+
+               $provider = new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'callUserSetCookiesHook' => false,
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               ) );
+               $config = $this->getConfig();
+               $provider->setLogger( new \TestLogger() );
+               $provider->setConfig( $config );
+               $provider->setManager( SessionManager::singleton() );
+
+               $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+               $store = new \HashBagOStuff();
+               $user = User::newFromName( 'UTSysop' );
+               $anon = new User;
+
+               $backend = new SessionBackend(
+                       new SessionId( $sessionId ),
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                               'provider' => $provider,
+                               'id' => $sessionId,
+                               'persisted' => true,
+                               'idIsSafe' => true,
+                       ) ),
+                       $store,
+                       new \Psr\Log\NullLogger(),
+                       10
+               );
+               \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+
+               $mock = $this->getMock( 'stdClass', array( 'onUserSetCookies' ) );
+               $mock->expects( $this->never() )->method( 'onUserSetCookies' );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'UserSetCookies' => array( $mock ) ) );
+
+               // Anonymous user
+               $backend->setUser( $anon );
+               $backend->setRememberUser( true );
+               $backend->setForceHTTPS( false );
+               $request = new \FauxRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'forceHTTPS' ) );
+               $this->assertSame( array(), $backend->getData() );
+
+               // Logged-in user, no remember
+               $backend->setUser( $user );
+               $backend->setRememberUser( false );
+               $backend->setForceHTTPS( false );
+               $request = new \FauxRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'forceHTTPS' ) );
+               $this->assertSame( array(), $backend->getData() );
+
+               // Logged-in user, remember
+               $backend->setUser( $user );
+               $backend->setRememberUser( true );
+               $backend->setForceHTTPS( true );
+               $request = new \FauxRequest();
+               $time = time();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
+               $this->assertSame( array(), $backend->getData() );
+       }
+
+       /**
+        * @dataProvider provideCookieData
+        * @param bool $secure
+        * @param bool $remember
+        */
+       public function testCookieData( $secure, $remember ) {
+               $this->setMwGlobals( array(
+                       'wgCookieExpiration' => 100,
+                       'wgSecureLogin' => false,
+               ) );
+
+               $provider = new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'callUserSetCookiesHook' => false,
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               ) );
+               $config = $this->getConfig();
+               $config->set( 'CookieSecure', false );
+               $provider->setLogger( new \TestLogger() );
+               $provider->setConfig( $config );
+               $provider->setManager( SessionManager::singleton() );
+
+               $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+               $user = User::newFromName( 'UTSysop' );
+               $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
+
+               $backend = new SessionBackend(
+                       new SessionId( $sessionId ),
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                               'provider' => $provider,
+                               'id' => $sessionId,
+                               'persisted' => true,
+                               'idIsSafe' => true,
+                       ) ),
+                       new \EmptyBagOStuff(),
+                       new \Psr\Log\NullLogger(),
+                       10
+               );
+               \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+               $backend->setUser( $user );
+               $backend->setRememberUser( $remember );
+               $backend->setForceHTTPS( $secure );
+               $request = new \FauxRequest();
+               $time = time();
+               $provider->persistSession( $backend, $request );
+
+               $defaults = array(
+                       'expire' => (int)100,
+                       'path' => $config->get( 'CookiePath' ),
+                       'domain' => $config->get( 'CookieDomain' ),
+                       'secure' => $secure,
+                       'httpOnly' => $config->get( 'CookieHttpOnly' ),
+                       'raw' => false,
+               );
+               $extendedExpiry = $config->get( 'ExtendedLoginCookieExpiration' );
+               $extendedExpiry = (int)( $extendedExpiry === null ? 0 : $extendedExpiry );
+               $this->assertEquals( array( 'UserID', 'Token' ), $config->get( 'ExtendedLoginCookies' ),
+                       'sanity check' );
+               $expect = array(
+                       'MySessionName' => array(
+                               'value' => (string)$sessionId,
+                               'expire' => 0,
+                       ) + $defaults,
+                       'xUserID' => array(
+                               'value' => (string)$user->getId(),
+                               'expire' => $extendedExpiry,
+                       ) + $defaults,
+                       'xUserName' => array(
+                               'value' => $user->getName(),
+                       ) + $defaults,
+                       'xToken' => array(
+                               'value' => $remember ? $user->getToken() : '',
+                               'expire' => $remember ? $extendedExpiry : -31536000,
+                       ) + $defaults,
+                       'forceHTTPS' => !$secure ? null : array(
+                               'value' => 'true',
+                               'secure' => false,
+                               'expire' => $remember ? $defaults['expire'] : null,
+                       ) + $defaults,
+               );
+               foreach ( $expect as $key => $value ) {
+                       $actual = $request->response()->getCookieData( $key );
+                       if ( $actual && $actual['expire'] > 0 ) {
+                               // Round expiry so we don't randomly fail if the seconds ticked during the test.
+                               $actual['expire'] = round( $actual['expire'] - $time, -2 );
+                       }
+                       $this->assertEquals( $value, $actual, "Cookie $key" );
+               }
+       }
+
+       public static function provideCookieData() {
+               return array(
+                       array( false, false ),
+                       array( false, true ),
+                       array( true, false ),
+                       array( true, true ),
+               );
+       }
+
+       protected function getSentRequest() {
+               $sentResponse = $this->getMock( 'FauxResponse', array( 'headersSent', 'setCookie', 'header' ) );
+               $sentResponse->expects( $this->any() )->method( 'headersSent' )
+                       ->will( $this->returnValue( true ) );
+               $sentResponse->expects( $this->never() )->method( 'setCookie' );
+               $sentResponse->expects( $this->never() )->method( 'header' );
+
+               $sentRequest = $this->getMock( 'FauxRequest', array( 'response' ) );
+               $sentRequest->expects( $this->any() )->method( 'response' )
+                       ->will( $this->returnValue( $sentResponse ) );
+               return $sentRequest;
+       }
+
+       public function testPersistSessionWithHook() {
+               $that = $this;
+
+               $provider = new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'callUserSetCookiesHook' => true,
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               ) );
+               $provider->setLogger( new \Psr\Log\NullLogger() );
+               $provider->setConfig( $this->getConfig() );
+               $provider->setManager( SessionManager::singleton() );
+
+               $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+               $store = new \HashBagOStuff();
+               $user = User::newFromName( 'UTSysop' );
+               $anon = new User;
+
+               $backend = new SessionBackend(
+                       new SessionId( $sessionId ),
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                               'provider' => $provider,
+                               'id' => $sessionId,
+                               'persisted' => true,
+                               'idIsSafe' => true,
+                       ) ),
+                       $store,
+                       new \Psr\Log\NullLogger(),
+                       10
+               );
+               \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+
+               // Anonymous user
+               $mock = $this->getMock( 'stdClass', array( 'onUserSetCookies' ) );
+               $mock->expects( $this->never() )->method( 'onUserSetCookies' );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'UserSetCookies' => array( $mock ) ) );
+               $backend->setUser( $anon );
+               $backend->setRememberUser( true );
+               $backend->setForceHTTPS( false );
+               $request = new \FauxRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'forceHTTPS' ) );
+               $this->assertSame( array(), $backend->getData() );
+
+               $provider->persistSession( $backend, $this->getSentRequest() );
+
+               // Logged-in user, no remember
+               $mock = $this->getMock( __CLASS__, array( 'onUserSetCookies' ) );
+               $mock->expects( $this->once() )->method( 'onUserSetCookies' )
+                       ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $that, $user ) {
+                               $that->assertSame( $user, $u );
+                               $that->assertEquals( array(
+                                       'wsUserID' => $user->getId(),
+                                       'wsUserName' => $user->getName(),
+                                       'wsToken' => $user->getToken(),
+                               ), $sessionData );
+                               $that->assertEquals( array(
+                                       'UserID' => $user->getId(),
+                                       'UserName' => $user->getName(),
+                                       'Token' => false,
+                               ), $cookies );
+
+                               $sessionData['foo'] = 'foo!';
+                               $cookies['bar'] = 'bar!';
+                               return true;
+                       } ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'UserSetCookies' => array( $mock ) ) );
+               $backend->setUser( $user );
+               $backend->setRememberUser( false );
+               $backend->setForceHTTPS( false );
+               $backend->setLoggedOutTimestamp( $loggedOut = time() );
+               $request = new \FauxRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'forceHTTPS' ) );
+               $this->assertSame( 'bar!', $request->response()->getCookie( 'xbar' ) );
+               $this->assertSame( (string)$loggedOut, $request->response()->getCookie( 'xLoggedOut' ) );
+               $this->assertEquals( array(
+                       'wsUserID' => $user->getId(),
+                       'wsUserName' => $user->getName(),
+                       'wsToken' => $user->getToken(),
+                       'foo' => 'foo!',
+               ), $backend->getData() );
+
+               $provider->persistSession( $backend, $this->getSentRequest() );
+
+               // Logged-in user, remember
+               $mock = $this->getMock( __CLASS__, array( 'onUserSetCookies' ) );
+               $mock->expects( $this->once() )->method( 'onUserSetCookies' )
+                       ->will( $this->returnCallback( function ( $u, &$sessionData, &$cookies ) use ( $that, $user ) {
+                               $that->assertSame( $user, $u );
+                               $that->assertEquals( array(
+                                       'wsUserID' => $user->getId(),
+                                       'wsUserName' => $user->getName(),
+                                       'wsToken' => $user->getToken(),
+                               ), $sessionData );
+                               $that->assertEquals( array(
+                                       'UserID' => $user->getId(),
+                                       'UserName' => $user->getName(),
+                                       'Token' => $user->getToken(),
+                               ), $cookies );
+
+                               $sessionData['foo'] = 'foo 2!';
+                               $cookies['bar'] = 'bar 2!';
+                               return true;
+                       } ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'UserSetCookies' => array( $mock ) ) );
+               $backend->setUser( $user );
+               $backend->setRememberUser( true );
+               $backend->setForceHTTPS( true );
+               $backend->setLoggedOutTimestamp( 0 );
+               $request = new \FauxRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( $sessionId, $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( (string)$user->getId(), $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( $user->getName(), $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( $user->getToken(), $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( 'true', $request->response()->getCookie( 'forceHTTPS' ) );
+               $this->assertSame( 'bar 2!', $request->response()->getCookie( 'xbar' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
+               $this->assertEquals( array(
+                       'wsUserID' => $user->getId(),
+                       'wsUserName' => $user->getName(),
+                       'wsToken' => $user->getToken(),
+                       'foo' => 'foo 2!',
+               ), $backend->getData() );
+
+               $provider->persistSession( $backend, $this->getSentRequest() );
+       }
+
+       public function testUnpersistSession() {
+               $provider = new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               ) );
+               $provider->setLogger( new \Psr\Log\NullLogger() );
+               $provider->setConfig( $this->getConfig() );
+               $provider->setManager( SessionManager::singleton() );
+
+               $request = new \FauxRequest();
+               $provider->unpersistSession( $request );
+               $this->assertSame( '', $request->response()->getCookie( 'MySessionName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xUserID' ) );
+               $this->assertSame( null, $request->response()->getCookie( 'xUserName' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'xToken' ) );
+               $this->assertSame( '', $request->response()->getCookie( 'forceHTTPS' ) );
+
+               $provider->unpersistSession( $this->getSentRequest() );
+       }
+
+       public function testSetLoggedOutCookie() {
+               $provider = \TestingAccessWrapper::newFromObject( new CookieSessionProvider( array(
+                       'priority' => 1,
+                       'sessionName' => 'MySessionName',
+                       'cookieOptions' => array( 'prefix' => 'x' ),
+               ) ) );
+               $provider->setLogger( new \Psr\Log\NullLogger() );
+               $provider->setConfig( $this->getConfig() );
+               $provider->setManager( SessionManager::singleton() );
+
+               $t1 = time();
+               $t2 = time() - 86400 * 2;
+
+               // Set it
+               $request = new \FauxRequest();
+               $provider->setLoggedOutCookie( $t1, $request );
+               $this->assertSame( (string)$t1, $request->response()->getCookie( 'xLoggedOut' ) );
+
+               // Too old
+               $request = new \FauxRequest();
+               $provider->setLoggedOutCookie( $t2, $request );
+               $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
+
+               // Don't reset if it's already set
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       'xLoggedOut' => $t1,
+               ), '' );
+               $provider->setLoggedOutCookie( $t1, $request );
+               $this->assertSame( null, $request->response()->getCookie( 'xLoggedOut' ) );
+       }
+
+       /**
+        * To be mocked for hooks, since PHPUnit can't otherwise mock methods that
+        * take references.
+        */
+       public function onUserSetCookies( $user, &$sessionData, &$cookies ) {
+       }
+
+}
diff --git a/tests/phpunit/includes/session/ImmutableSessionProviderWithCookieTest.php b/tests/phpunit/includes/session/ImmutableSessionProviderWithCookieTest.php
new file mode 100644 (file)
index 0000000..e06dfd5
--- /dev/null
@@ -0,0 +1,301 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\ImmutableSessionProviderWithCookie
+ */
+class ImmutableSessionProviderWithCookieTest extends MediaWikiTestCase {
+
+       private function getProvider( $name, $prefix = null ) {
+               $config = new \HashConfig();
+               $config->set( 'CookiePrefix', 'wgCookiePrefix' );
+
+               $params = array(
+                       'sessionCookieName' => $name,
+                       'sessionCookieOptions' => array(),
+               );
+               if ( $prefix !== null ) {
+                       $params['sessionCookieOptions']['prefix'] = $prefix;
+               }
+
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' )
+                       ->setConstructorArgs( array( $params ) )
+                       ->getMockForAbstractClass();
+               $provider->setLogger( new \TestLogger() );
+               $provider->setConfig( $config );
+               $provider->setManager( new SessionManager() );
+
+               return $provider;
+       }
+
+       public function testConstructor() {
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' )
+                       ->getMockForAbstractClass();
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+               $this->assertNull( $priv->sessionCookieName );
+               $this->assertSame( array(), $priv->sessionCookieOptions );
+
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' )
+                       ->setConstructorArgs( array( array(
+                               'sessionCookieName' => 'Foo',
+                               'sessionCookieOptions' => array( 'Bar' ),
+                       ) ) )
+                       ->getMockForAbstractClass();
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+               $this->assertSame( 'Foo', $priv->sessionCookieName );
+               $this->assertSame( array( 'Bar' ), $priv->sessionCookieOptions );
+
+               try {
+                       $provider = $this->getMockBuilder( 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' )
+                               ->setConstructorArgs( array( array(
+                                       'sessionCookieName' => false,
+                               ) ) )
+                               ->getMockForAbstractClass();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'sessionCookieName must be a string',
+                               $ex->getMessage()
+                       );
+               }
+
+               try {
+                       $provider = $this->getMockBuilder( 'MediaWiki\\Session\\ImmutableSessionProviderWithCookie' )
+                               ->setConstructorArgs( array( array(
+                                       'sessionCookieOptions' => 'x',
+                               ) ) )
+                               ->getMockForAbstractClass();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'sessionCookieOptions must be an array',
+                               $ex->getMessage()
+                       );
+               }
+       }
+
+       public function testBasics() {
+               $provider = $this->getProvider( null );
+               $this->assertFalse( $provider->persistsSessionID() );
+               $this->assertFalse( $provider->canChangeUser() );
+
+               $provider = $this->getProvider( 'Foo' );
+               $this->assertTrue( $provider->persistsSessionID() );
+               $this->assertFalse( $provider->canChangeUser() );
+
+               $msg = $provider->whyNoSession();
+               $this->assertInstanceOf( 'Message', $msg );
+               $this->assertSame( 'sessionprovider-nocookies', $msg->getKey() );
+       }
+
+       public function testGetVaryCookies() {
+               $provider = $this->getProvider( null );
+               $this->assertSame( array(), $provider->getVaryCookies() );
+
+               $provider = $this->getProvider( 'Foo' );
+               $this->assertSame( array( 'wgCookiePrefixFoo' ), $provider->getVaryCookies() );
+
+               $provider = $this->getProvider( 'Foo', 'Bar' );
+               $this->assertSame( array( 'BarFoo' ), $provider->getVaryCookies() );
+
+               $provider = $this->getProvider( 'Foo', '' );
+               $this->assertSame( array( 'Foo' ), $provider->getVaryCookies() );
+       }
+
+       public function testGetSessionIdFromCookie() {
+               $this->setMwGlobals( 'wgCookiePrefix', 'wgCookiePrefix' );
+               $request = new \FauxRequest();
+               $request->setCookies( array(
+                       '' => 'empty---------------------------',
+                       'Foo' => 'foo-----------------------------',
+                       'wgCookiePrefixFoo' => 'wgfoo---------------------------',
+                       'BarFoo' => 'foobar--------------------------',
+                       'bad' => 'bad',
+               ), '' );
+
+               $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( null ) );
+               try {
+                       $provider->getSessionIdFromCookie( $request );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \BadMethodCallException $ex ) {
+                       $this->assertSame(
+                               'MediaWiki\\Session\\ImmutableSessionProviderWithCookie::getSessionIdFromCookie ' .
+                                       'may not be called when $this->sessionCookieName === null',
+                               $ex->getMessage()
+                       );
+               }
+
+               $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo' ) );
+               $this->assertSame(
+                       'wgfoo---------------------------',
+                       $provider->getSessionIdFromCookie( $request )
+               );
+
+               $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', 'Bar' ) );
+               $this->assertSame(
+                       'foobar--------------------------',
+                       $provider->getSessionIdFromCookie( $request )
+               );
+
+               $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'Foo', '' ) );
+               $this->assertSame(
+                       'foo-----------------------------',
+                       $provider->getSessionIdFromCookie( $request )
+               );
+
+               $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'bad', '' ) );
+               $this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
+
+               $provider = \TestingAccessWrapper::newFromObject( $this->getProvider( 'none', '' ) );
+               $this->assertSame( null, $provider->getSessionIdFromCookie( $request ) );
+       }
+
+       protected function getSentRequest() {
+               $sentResponse = $this->getMock( 'FauxResponse', array( 'headersSent', 'setCookie', 'header' ) );
+               $sentResponse->expects( $this->any() )->method( 'headersSent' )
+                       ->will( $this->returnValue( true ) );
+               $sentResponse->expects( $this->never() )->method( 'setCookie' );
+               $sentResponse->expects( $this->never() )->method( 'header' );
+
+               $sentRequest = $this->getMock( 'FauxRequest', array( 'response' ) );
+               $sentRequest->expects( $this->any() )->method( 'response' )
+                       ->will( $this->returnValue( $sentResponse ) );
+               return $sentRequest;
+       }
+
+       /**
+        * @dataProvider providePersistSession
+        * @param bool $secure
+        * @param bool $remember
+        */
+       public function testPersistSession( $secure, $remember ) {
+               $this->setMwGlobals( array(
+                       'wgCookieExpiration' => 100,
+                       'wgSecureLogin' => false,
+               ) );
+
+               $provider = $this->getProvider( 'session' );
+               $provider->setLogger( new \Psr\Log\NullLogger() );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+               $priv->sessionCookieOptions = array(
+                       'prefix' => 'x',
+                       'path' => 'CookiePath',
+                       'domain' => 'CookieDomain',
+                       'secure' => false,
+                       'httpOnly' => true,
+               );
+
+               $sessionId = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+               $user = User::newFromName( 'UTSysop' );
+               $this->assertFalse( $user->requiresHTTPS(), 'sanity check' );
+
+               $backend = new SessionBackend(
+                       new SessionId( $sessionId ),
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                               'provider' => $provider,
+                               'id' => $sessionId,
+                               'persisted' => true,
+                               'userInfo' => UserInfo::newFromUser( $user, true ),
+                               'idIsSafe' => true,
+                       ) ),
+                       new \EmptyBagOStuff(),
+                       new \Psr\Log\NullLogger(),
+                       10
+               );
+               \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = false;
+               $backend->setRememberUser( $remember );
+               $backend->setForceHTTPS( $secure );
+
+               // No cookie
+               $priv->sessionCookieName = null;
+               $request = new \FauxRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( array(), $request->response()->getCookies() );
+
+               // Cookie
+               $priv->sessionCookieName = 'session';
+               $request = new \FauxRequest();
+               $time = time();
+               $provider->persistSession( $backend, $request );
+
+               $cookie = $request->response()->getCookieData( 'xsession' );
+               $this->assertInternalType( 'array', $cookie );
+               if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
+                       // Round expiry so we don't randomly fail if the seconds ticked during the test.
+                       $cookie['expire'] = round( $cookie['expire'] - $time, -2 );
+               }
+               $this->assertEquals( array(
+                       'value' => $sessionId,
+                       'expire' => null,
+                       'path' => 'CookiePath',
+                       'domain' => 'CookieDomain',
+                       'secure' => $secure,
+                       'httpOnly' => true,
+                       'raw' => false,
+               ), $cookie );
+
+               $cookie = $request->response()->getCookieData( 'forceHTTPS' );
+               if ( $secure ) {
+                       $this->assertInternalType( 'array', $cookie );
+                       if ( isset( $cookie['expire'] ) && $cookie['expire'] > 0 ) {
+                               // Round expiry so we don't randomly fail if the seconds ticked during the test.
+                               $cookie['expire'] = round( $cookie['expire'] - $time, -2 );
+                       }
+                       $this->assertEquals( array(
+                               'value' => 'true',
+                               'expire' => $remember ? 100 : null,
+                               'path' => 'CookiePath',
+                               'domain' => 'CookieDomain',
+                               'secure' => false,
+                               'httpOnly' => true,
+                               'raw' => false,
+                       ), $cookie );
+               } else {
+                       $this->assertNull( $cookie );
+               }
+
+               // Headers sent
+               $request = $this->getSentRequest();
+               $provider->persistSession( $backend, $request );
+               $this->assertSame( array(), $request->response()->getCookies() );
+       }
+
+       public static function providePersistSession() {
+               return array(
+                       array( false, false ),
+                       array( false, true ),
+                       array( true, false ),
+                       array( true, true ),
+               );
+       }
+
+       public function testUnpersistSession() {
+               $provider = $this->getProvider( 'session', '' );
+               $provider->setLogger( new \Psr\Log\NullLogger() );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+
+               // No cookie
+               $priv->sessionCookieName = null;
+               $request = new \FauxRequest();
+               $provider->unpersistSession( $request );
+               $this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
+
+               // Cookie
+               $priv->sessionCookieName = 'session';
+               $request = new \FauxRequest();
+               $provider->unpersistSession( $request );
+               $this->assertSame( '', $request->response()->getCookie( 'session', '' ) );
+
+               // Headers sent
+               $request = $this->getSentRequest();
+               $provider->unpersistSession( $request );
+               $this->assertSame( null, $request->response()->getCookie( 'session', '' ) );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/PHPSessionHandlerTest.php b/tests/phpunit/includes/session/PHPSessionHandlerTest.php
new file mode 100644 (file)
index 0000000..5a5df6f
--- /dev/null
@@ -0,0 +1,371 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LogLevel;
+use MediaWikiTestCase;
+
+/**
+ * @group Session
+ * @covers MediaWiki\Session\PHPSessionHandler
+ */
+class PHPSessionHandlerTest extends MediaWikiTestCase {
+
+       private function getResetter( &$rProp = null ) {
+               $reset = array();
+
+               // Ignore "headers already sent" warnings during this test
+               set_error_handler( function ( $errno, $errstr ) use ( &$warnings ) {
+                       if ( preg_match( '/headers already sent/', $errstr ) ) {
+                               return true;
+                       }
+                       return false;
+               } );
+               $reset[] = new \ScopedCallback( 'restore_error_handler' );
+
+               $rProp = new \ReflectionProperty( 'MediaWiki\\Session\\PHPSessionHandler', 'instance' );
+               $rProp->setAccessible( true );
+               if ( $rProp->getValue() ) {
+                       $old = \TestingAccessWrapper::newFromObject( $rProp->getValue() );
+                       $oldManager = $old->manager;
+                       $oldStore = $old->store;
+                       $oldLogger = $old->logger;
+                       $reset[] = new \ScopedCallback(
+                               array( 'MediaWiki\\Session\\PHPSessionHandler', 'install' ),
+                               array( $oldManager, $oldStore, $oldLogger )
+                       );
+               }
+
+               return $reset;
+       }
+
+       public function testEnableFlags() {
+               $handler = \TestingAccessWrapper::newFromObject(
+                       $this->getMockBuilder( 'MediaWiki\\Session\\PHPSessionHandler' )
+                               ->setMethods( null )
+                               ->disableOriginalConstructor()
+                               ->getMock()
+               );
+
+               $rProp = new \ReflectionProperty( 'MediaWiki\\Session\\PHPSessionHandler', 'instance' );
+               $rProp->setAccessible( true );
+               $reset = new \ScopedCallback( array( $rProp, 'setValue' ), array( $rProp->getValue() ) );
+               $rProp->setValue( $handler );
+
+               $handler->setEnableFlags( 'enable' );
+               $this->assertTrue( $handler->enable );
+               $this->assertFalse( $handler->warn );
+               $this->assertTrue( PHPSessionHandler::isEnabled() );
+
+               $handler->setEnableFlags( 'warn' );
+               $this->assertTrue( $handler->enable );
+               $this->assertTrue( $handler->warn );
+               $this->assertTrue( PHPSessionHandler::isEnabled() );
+
+               $handler->setEnableFlags( 'disable' );
+               $this->assertFalse( $handler->enable );
+               $this->assertFalse( PHPSessionHandler::isEnabled() );
+
+               $rProp->setValue( null );
+               $this->assertFalse( PHPSessionHandler::isEnabled() );
+       }
+
+       public function testInstall() {
+               $reset = $this->getResetter( $rProp );
+               $rProp->setValue( null );
+
+               session_write_close();
+               ini_set( 'session.use_cookies', 1 );
+               ini_set( 'session.use_trans_sid', 1 );
+
+               $store = new \HashBagOStuff();
+               $logger = new \TestLogger();
+               $manager = new SessionManager( array(
+                       'store' => $store,
+                       'logger' => $logger,
+               ) );
+
+               $this->assertFalse( PHPSessionHandler::isInstalled() );
+               PHPSessionHandler::install( $manager );
+               $this->assertTrue( PHPSessionHandler::isInstalled() );
+
+               $this->assertFalse( wfIniGetBool( 'session.use_cookies' ) );
+               $this->assertFalse( wfIniGetBool( 'session.use_trans_sid' ) );
+
+               $this->assertNotNull( $rProp->getValue() );
+               $priv = \TestingAccessWrapper::newFromObject( $rProp->getValue() );
+               $this->assertSame( $manager, $priv->manager );
+               $this->assertSame( $store, $priv->store );
+               $this->assertSame( $logger, $priv->logger );
+       }
+
+       /**
+        * @dataProvider provideHandlers
+        * @param string $handler php serialize_handler to use
+        */
+       public function testSessionHandling( $handler ) {
+               $this->hideDeprecated( '$_SESSION' );
+               $reset[] = $this->getResetter( $rProp );
+
+               $this->setMwGlobals( array(
+                       'wgSessionProviders' => array( array( 'class' => 'DummySessionProvider' ) ),
+                       'wgObjectCacheSessionExpiry' => 2,
+               ) );
+
+               $store = new \HashBagOStuff();
+               $logger = new \TestLogger( true, function ( $m ) {
+                       return preg_match( '/^SessionBackend a{32} /', $m ) ? null : $m;
+               } );
+               $manager = new SessionManager( array(
+                       'store' => $store,
+                       'logger' => $logger,
+               ) );
+               PHPSessionHandler::install( $manager );
+               $wrap = \TestingAccessWrapper::newFromObject( $rProp->getValue() );
+               $reset[] = new \ScopedCallback(
+                       array( $wrap, 'setEnableFlags' ),
+                       array( $wrap->enable ? $wrap->warn ? 'warn' : 'enable' : 'disable' )
+               );
+               $wrap->setEnableFlags( 'warn' );
+
+               \MediaWiki\suppressWarnings();
+               ini_set( 'session.serialize_handler', $handler );
+               \MediaWiki\restoreWarnings();
+               if ( ini_get( 'session.serialize_handler' ) !== $handler ) {
+                       $this->markTestSkipped( "Cannot set session.serialize_handler to \"$handler\"" );
+               }
+
+               // Session IDs for testing
+               $sessionA = str_repeat( 'a', 32 );
+               $sessionB = str_repeat( 'b', 32 );
+               $sessionC = str_repeat( 'c', 32 );
+
+               // Set up garbage data in the session
+               $_SESSION['AuthenticationSessionTest'] = 'bogus';
+
+               session_id( $sessionA );
+               session_start();
+               $this->assertSame( array(), $_SESSION );
+               $this->assertSame( $sessionA, session_id() );
+
+               // Set some data in the session so we can see if it works.
+               $rand = mt_rand();
+               $_SESSION['AuthenticationSessionTest'] = $rand;
+               $expect = array( 'AuthenticationSessionTest' => $rand );
+               session_write_close();
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Something wrote to $_SESSION!' ),
+               ), $logger->getBuffer() );
+
+               // Screw up $_SESSION so we can tell the difference between "this
+               // worked" and "this did nothing"
+               $_SESSION['AuthenticationSessionTest'] = 'bogus';
+
+               // Re-open the session and see that data was actually reloaded
+               session_start();
+               $this->assertSame( $expect, $_SESSION );
+
+               // Make sure session_reset() works too.
+               if ( function_exists( 'session_reset' ) ) {
+                       $_SESSION['AuthenticationSessionTest'] = 'bogus';
+                       session_reset();
+                       $this->assertSame( $expect, $_SESSION );
+               }
+
+               // Test expiry
+               session_write_close();
+               ini_set( 'session.gc_divisor', 1 );
+               ini_set( 'session.gc_probability', 1 );
+               sleep( 3 );
+               session_start();
+               $this->assertSame( array(), $_SESSION );
+
+               // Re-fill the session, then test that session_destroy() works.
+               $_SESSION['AuthenticationSessionTest'] = $rand;
+               session_write_close();
+               session_start();
+               $this->assertSame( $expect, $_SESSION );
+               session_destroy();
+               session_id( $sessionA );
+               session_start();
+               $this->assertSame( array(), $_SESSION );
+               session_write_close();
+
+               // Test that our session handler won't clone someone else's session
+               session_id( $sessionB );
+               session_start();
+               $this->assertSame( $sessionB, session_id() );
+               $_SESSION['id'] = 'B';
+               session_write_close();
+
+               session_id( $sessionC );
+               session_start();
+               $this->assertSame( array(), $_SESSION );
+               $_SESSION['id'] = 'C';
+               session_write_close();
+
+               session_id( $sessionB );
+               session_start();
+               $this->assertSame( array( 'id' => 'B' ), $_SESSION );
+               session_write_close();
+
+               session_id( $sessionC );
+               session_start();
+               $this->assertSame( array( 'id' => 'C' ), $_SESSION );
+               session_destroy();
+
+               session_id( $sessionB );
+               session_start();
+               $this->assertSame( array( 'id' => 'B' ), $_SESSION );
+
+               // Test merging between Session and $_SESSION
+               session_write_close();
+
+               $session = $manager->getEmptySession();
+               $session->set( 'Unchanged', 'setup' );
+               $session->set( 'Changed in $_SESSION', 'setup' );
+               $session->set( 'Changed in Session', 'setup' );
+               $session->set( 'Changed in both', 'setup' );
+               $session->set( 'Deleted in Session', 'setup' );
+               $session->set( 'Deleted in $_SESSION', 'setup' );
+               $session->set( 'Deleted in both', 'setup' );
+               $session->set( 'Deleted in Session, changed in $_SESSION', 'setup' );
+               $session->set( 'Deleted in $_SESSION, changed in Session', 'setup' );
+               $session->persist();
+               $session->save();
+
+               session_id( $session->getId() );
+               session_start();
+               $session->set( 'Added in Session', 'Session' );
+               $session->set( 'Added in both', 'Session' );
+               $session->set( 'Changed in Session', 'Session' );
+               $session->set( 'Changed in both', 'Session' );
+               $session->set( 'Deleted in $_SESSION, changed in Session', 'Session' );
+               $session->remove( 'Deleted in Session' );
+               $session->remove( 'Deleted in both' );
+               $session->remove( 'Deleted in Session, changed in $_SESSION' );
+               $session->save();
+               $_SESSION['Added in $_SESSION'] = '$_SESSION';
+               $_SESSION['Added in both'] = '$_SESSION';
+               $_SESSION['Changed in $_SESSION'] = '$_SESSION';
+               $_SESSION['Changed in both'] = '$_SESSION';
+               $_SESSION['Deleted in Session, changed in $_SESSION'] = '$_SESSION';
+               unset( $_SESSION['Deleted in $_SESSION'] );
+               unset( $_SESSION['Deleted in both'] );
+               unset( $_SESSION['Deleted in $_SESSION, changed in Session'] );
+               session_write_close();
+
+               $this->assertEquals( array(
+                       'Added in Session' => 'Session',
+                       'Added in $_SESSION' => '$_SESSION',
+                       'Added in both' => 'Session',
+                       'Unchanged' => 'setup',
+                       'Changed in Session' => 'Session',
+                       'Changed in $_SESSION' => '$_SESSION',
+                       'Changed in both' => 'Session',
+                       'Deleted in Session, changed in $_SESSION' => '$_SESSION',
+                       'Deleted in $_SESSION, changed in Session' => 'Session',
+               ), iterator_to_array( $session ) );
+
+               $session->clear();
+               $session->set( 42, 'forty-two' );
+               $session->set( 'forty-two', 42 );
+               $session->set( 'wrong', 43 );
+               $session->persist();
+               $session->save();
+
+               session_start();
+               $this->assertArrayHasKey( 'forty-two', $_SESSION );
+               $this->assertSame( 42, $_SESSION['forty-two'] );
+               $this->assertArrayHasKey( 'wrong', $_SESSION );
+               unset( $_SESSION['wrong'] );
+               session_write_close();
+
+               $this->assertEquals( array(
+                       42 => 'forty-two',
+                       'forty-two' => 42,
+               ), iterator_to_array( $session ) );
+
+               // Test that write doesn't break if the session is invalid
+               $session = $manager->getEmptySession();
+               $session->persist();
+               session_id( $session->getId() );
+               session_start();
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array(
+                       'SessionCheckInfo' => array( function ( &$reason ) {
+                               $reason = 'Testing';
+                               return false;
+                       } ),
+               ) );
+               $this->assertNull( $manager->getSessionById( $session->getId(), true ), 'sanity check' );
+               session_write_close();
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array(
+                       'SessionCheckInfo' => array(),
+               ) );
+               $this->assertNotNull( $manager->getSessionById( $session->getId(), true ), 'sanity check' );
+       }
+
+       public static function provideHandlers() {
+               return array(
+                       array( 'php' ),
+                       array( 'php_binary' ),
+                       array( 'php_serialize' ),
+               );
+       }
+
+       /**
+        * @dataProvider provideDisabled
+        * @expectedException BadMethodCallException
+        * @expectedExceptionMessage Attempt to use PHP session management
+        */
+       public function testDisabled( $method, $args ) {
+               $rProp = new \ReflectionProperty( 'MediaWiki\\Session\\PHPSessionHandler', 'instance' );
+               $rProp->setAccessible( true );
+               $handler = $this->getMockBuilder( 'MediaWiki\\Session\\PHPSessionHandler' )
+                       ->setMethods( null )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               \TestingAccessWrapper::newFromObject( $handler )->setEnableFlags( 'disable' );
+               $oldValue = $rProp->getValue();
+               $rProp->setValue( $handler );
+               $reset = new \ScopedCallback( array( $rProp, 'setValue' ), array( $oldValue ) );
+
+               call_user_func_array( array( $handler, $method ), $args );
+       }
+
+       public static function provideDisabled() {
+               return array(
+                       array( 'open', array( '', '' ) ),
+                       array( 'read', array( '' ) ),
+                       array( 'write', array( '', '' ) ),
+                       array( 'destroy', array( '' ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideWrongInstance
+        * @expectedException UnexpectedValueException
+        * @expectedExceptionMessageRegExp /: Wrong instance called!$/
+        */
+       public function testWrongInstance( $method, $args ) {
+               $handler = $this->getMockBuilder( 'MediaWiki\\Session\\PHPSessionHandler' )
+                       ->setMethods( null )
+                       ->disableOriginalConstructor()
+                       ->getMock();
+               \TestingAccessWrapper::newFromObject( $handler )->setEnableFlags( 'enable' );
+
+               call_user_func_array( array( $handler, $method ), $args );
+       }
+
+       public static function provideWrongInstance() {
+               return array(
+                       array( 'open', array( '', '' ) ),
+                       array( 'close', array() ),
+                       array( 'read', array( '' ) ),
+                       array( 'write', array( '', '' ) ),
+                       array( 'destroy', array( '' ) ),
+                       array( 'gc', array( 0 ) ),
+               );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/SessionBackendTest.php b/tests/phpunit/includes/session/SessionBackendTest.php
new file mode 100644 (file)
index 0000000..d06706b
--- /dev/null
@@ -0,0 +1,757 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\SessionBackend
+ */
+class SessionBackendTest extends MediaWikiTestCase {
+       const SESSIONID = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+
+       protected $manager;
+       protected $config;
+       protected $provider;
+       protected $store;
+
+       protected $onSessionMetadataCalled = false;
+
+       /**
+        * Returns a non-persistent backend that thinks it has at least one session active
+        * @param User|null $user
+        */
+       protected function getBackend( User $user = null ) {
+               if ( !$this->config ) {
+                       $this->config = new \HashConfig();
+                       $this->manager = null;
+               }
+               if ( !$this->store ) {
+                       $this->store = new TestBagOStuff();
+                       $this->manager = null;
+               }
+
+               $logger = new \Psr\Log\NullLogger();
+               if ( !$this->manager ) {
+                       $this->manager = new SessionManager( array(
+                               'store' => $this->store,
+                               'logger' => $logger,
+                               'config' => $this->config,
+                       ) );
+               }
+
+               if ( !$this->provider ) {
+                       $this->provider = new \DummySessionProvider();
+               }
+               $this->provider->setLogger( $logger );
+               $this->provider->setConfig( $this->config );
+               $this->provider->setManager( $this->manager );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $this->provider,
+                       'id' => self::SESSIONID,
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newFromUser( $user ?: new User, true ),
+                       'idIsSafe' => true,
+               ) );
+               $id = new SessionId( $info->getId() );
+
+               $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
+               $priv = \TestingAccessWrapper::newFromObject( $backend );
+               $priv->persist = false;
+               $priv->requests = array( 100 => new \FauxRequest() );
+               $priv->usePhpSessionHandling = false;
+
+               $manager = \TestingAccessWrapper::newFromObject( $this->manager );
+               $manager->allSessionBackends = array( $backend->getId() => $backend );
+               $manager->allSessionIds = array( $backend->getId() => $id );
+               $manager->sessionProviders = array( (string)$this->provider => $this->provider );
+
+               return $backend;
+       }
+
+       public function testConstructor() {
+               // Set variables
+               $this->getBackend();
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $this->provider,
+                       'id' => self::SESSIONID,
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', false ),
+                       'idIsSafe' => true,
+               ) );
+               $id = new SessionId( $info->getId() );
+               $logger = new \Psr\Log\NullLogger();
+               try {
+                       new SessionBackend( $id, $info, $this->store, $logger, 10 );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               "Refusing to create session for unverified user {$info->getUserInfo()}",
+                               $ex->getMessage()
+                       );
+               }
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => self::SESSIONID,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
+                       'idIsSafe' => true,
+               ) );
+               $id = new SessionId( $info->getId() );
+               try {
+                       new SessionBackend( $id, $info, $this->store, $logger, 10 );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Cannot create session without a provider', $ex->getMessage() );
+               }
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $this->provider,
+                       'id' => self::SESSIONID,
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
+                       'idIsSafe' => true,
+               ) );
+               $id = new SessionId( '!' . $info->getId() );
+               try {
+                       new SessionBackend( $id, $info, $this->store, $logger, 10 );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'SessionId and SessionInfo don\'t match',
+                               $ex->getMessage()
+                       );
+               }
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $this->provider,
+                       'id' => self::SESSIONID,
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
+                       'idIsSafe' => true,
+               ) );
+               $id = new SessionId( $info->getId() );
+               $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
+               $this->assertSame( self::SESSIONID, $backend->getId() );
+               $this->assertSame( $id, $backend->getSessionId() );
+               $this->assertSame( $this->provider, $backend->getProvider() );
+               $this->assertInstanceOf( 'User', $backend->getUser() );
+               $this->assertSame( 'UTSysop', $backend->getUser()->getName() );
+               $this->assertSame( $info->wasPersisted(), $backend->isPersistent() );
+               $this->assertSame( $info->wasRemembered(), $backend->shouldRememberUser() );
+               $this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
+
+               $expire = time() + 100;
+               $this->store->setSessionMeta( self::SESSIONID, array( 'expires' => $expire ), 2 );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $this->provider,
+                       'id' => self::SESSIONID,
+                       'persisted' => true,
+                       'forceHTTPS' => true,
+                       'metadata' => array( 'foo' ),
+                       'idIsSafe' => true,
+               ) );
+               $id = new SessionId( $info->getId() );
+               $backend = new SessionBackend( $id, $info, $this->store, $logger, 10 );
+               $this->assertSame( self::SESSIONID, $backend->getId() );
+               $this->assertSame( $id, $backend->getSessionId() );
+               $this->assertSame( $this->provider, $backend->getProvider() );
+               $this->assertInstanceOf( 'User', $backend->getUser() );
+               $this->assertTrue( $backend->getUser()->isAnon() );
+               $this->assertSame( $info->wasPersisted(), $backend->isPersistent() );
+               $this->assertSame( $info->wasRemembered(), $backend->shouldRememberUser() );
+               $this->assertSame( $info->forceHTTPS(), $backend->shouldForceHTTPS() );
+               $this->assertSame( $expire, \TestingAccessWrapper::newFromObject( $backend )->expires );
+               $this->assertSame( array( 'foo' ), $backend->getProviderMetadata() );
+       }
+
+       public function testSessionStuff() {
+               $backend = $this->getBackend();
+               $priv = \TestingAccessWrapper::newFromObject( $backend );
+               $priv->requests = array(); // Remove dummy session
+
+               $manager = \TestingAccessWrapper::newFromObject( $this->manager );
+
+               $request1 = new \FauxRequest();
+               $session1 = $backend->getSession( $request1 );
+               $request2 = new \FauxRequest();
+               $session2 = $backend->getSession( $request2 );
+
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session1 );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session2 );
+               $this->assertSame( 2, count( $priv->requests ) );
+
+               $index = \TestingAccessWrapper::newFromObject( $session1 )->index;
+
+               $this->assertSame( $request1, $backend->getRequest( $index ) );
+               $this->assertSame( null, $backend->suggestLoginUsername( $index ) );
+               $request1->setCookie( 'UserName', 'Example' );
+               $this->assertSame( 'Example', $backend->suggestLoginUsername( $index ) );
+
+               $session1 = null;
+               $this->assertSame( 1, count( $priv->requests ) );
+               $this->assertArrayHasKey( $backend->getId(), $manager->allSessionBackends );
+               $this->assertSame( $backend, $manager->allSessionBackends[$backend->getId()] );
+               try {
+                       $backend->getRequest( $index );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid session index', $ex->getMessage() );
+               }
+               try {
+                       $backend->suggestLoginUsername( $index );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid session index', $ex->getMessage() );
+               }
+
+               $session2 = null;
+               $this->assertSame( 0, count( $priv->requests ) );
+               $this->assertArrayNotHasKey( $backend->getId(), $manager->allSessionBackends );
+               $this->assertArrayHasKey( $backend->getId(), $manager->allSessionIds );
+       }
+
+       public function testResetId() {
+               $id = session_id();
+
+               $builder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( array( 'persistsSessionId', 'sessionIdWasReset' ) );
+
+               $this->provider = $builder->getMock();
+               $this->provider->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( false ) );
+               $this->provider->expects( $this->never() )->method( 'sessionIdWasReset' );
+               $backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
+               $manager = \TestingAccessWrapper::newFromObject( $this->manager );
+               $sessionId = $backend->getSessionId();
+               $backend->resetId();
+               $this->assertSame( self::SESSIONID, $backend->getId() );
+               $this->assertSame( $backend->getId(), $sessionId->getId() );
+               $this->assertSame( $id, session_id() );
+               $this->assertSame( $backend, $manager->allSessionBackends[self::SESSIONID] );
+
+               $this->provider = $builder->getMock();
+               $this->provider->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( true ) );
+               $backend = $this->getBackend();
+               $this->provider->expects( $this->once() )->method( 'sessionIdWasReset' )
+                       ->with( $this->identicalTo( $backend ), $this->identicalTo( self::SESSIONID ) );
+               $manager = \TestingAccessWrapper::newFromObject( $this->manager );
+               $sessionId = $backend->getSessionId();
+               $backend->resetId();
+               $this->assertNotEquals( self::SESSIONID, $backend->getId() );
+               $this->assertSame( $backend->getId(), $sessionId->getId() );
+               $this->assertInternalType( 'array', $this->store->getSession( $backend->getId() ) );
+               $this->assertFalse( $this->store->getSession( self::SESSIONID ) );
+               $this->assertSame( $id, session_id() );
+               $this->assertArrayNotHasKey( self::SESSIONID, $manager->allSessionBackends );
+               $this->assertArrayHasKey( $backend->getId(), $manager->allSessionBackends );
+               $this->assertSame( $backend, $manager->allSessionBackends[$backend->getId()] );
+       }
+
+       public function testPersist() {
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->once() )->method( 'persistSession' );
+               $backend = $this->getBackend();
+               $this->assertFalse( $backend->isPersistent(), 'sanity check' );
+               $backend->save(); // This one shouldn't call $provider->persistSession()
+
+               $backend->persist();
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+
+               $this->provider = null;
+               $backend = $this->getBackend();
+               $wrap = \TestingAccessWrapper::newFromObject( $backend );
+               $wrap->persist = true;
+               $wrap->expires = 0;
+               $backend->persist();
+               $this->assertNotEquals( 0, $wrap->expires );
+       }
+
+       public function testRememberUser() {
+               $backend = $this->getBackend();
+
+               $remembered = $backend->shouldRememberUser();
+               $backend->setRememberUser( !$remembered );
+               $this->assertNotEquals( $remembered, $backend->shouldRememberUser() );
+               $backend->setRememberUser( $remembered );
+               $this->assertEquals( $remembered, $backend->shouldRememberUser() );
+       }
+
+       public function testForceHTTPS() {
+               $backend = $this->getBackend();
+
+               $force = $backend->shouldForceHTTPS();
+               $backend->setForceHTTPS( !$force );
+               $this->assertNotEquals( $force, $backend->shouldForceHTTPS() );
+               $backend->setForceHTTPS( $force );
+               $this->assertEquals( $force, $backend->shouldForceHTTPS() );
+       }
+
+       public function testLoggedOutTimestamp() {
+               $backend = $this->getBackend();
+
+               $backend->setLoggedOutTimestamp( 42 );
+               $this->assertSame( 42, $backend->getLoggedOutTimestamp() );
+               $backend->setLoggedOutTimestamp( '123' );
+               $this->assertSame( 123, $backend->getLoggedOutTimestamp() );
+       }
+
+       public function testSetUser() {
+               $user = User::newFromName( 'UTSysop' );
+
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'canChangeUser' ) );
+               $this->provider->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( false ) );
+               $backend = $this->getBackend();
+               $this->assertFalse( $backend->canSetUser() );
+               try {
+                       $backend->setUser( $user );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \BadMethodCallException $ex ) {
+                       $this->assertSame(
+                               'Cannot set user on this session; check $session->canSetUser() first',
+                               $ex->getMessage()
+                       );
+               }
+               $this->assertNotSame( $user, $backend->getUser() );
+
+               $this->provider = null;
+               $backend = $this->getBackend();
+               $this->assertTrue( $backend->canSetUser() );
+               $this->assertNotSame( $user, $backend->getUser(), 'sanity check' );
+               $backend->setUser( $user );
+               $this->assertSame( $user, $backend->getUser() );
+       }
+
+       public function testDirty() {
+               $backend = $this->getBackend();
+               $priv = \TestingAccessWrapper::newFromObject( $backend );
+               $priv->dataDirty = false;
+               $backend->dirty();
+               $this->assertTrue( $priv->dataDirty );
+       }
+
+       public function testGetData() {
+               $backend = $this->getBackend();
+               $data = $backend->getData();
+               $this->assertSame( array(), $data );
+               $this->assertTrue( \TestingAccessWrapper::newFromObject( $backend )->dataDirty );
+               $data['???'] = '!!!';
+               $this->assertSame( array( '???' => '!!!' ), $data );
+
+               $testData = array( 'foo' => 'foo!', 'bar', array( 'baz', null ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend();
+               $this->assertSame( $testData, $backend->getData() );
+               $this->assertFalse( \TestingAccessWrapper::newFromObject( $backend )->dataDirty );
+       }
+
+       public function testAddData() {
+               $backend = $this->getBackend();
+               $priv = \TestingAccessWrapper::newFromObject( $backend );
+
+               $priv->data = array( 'foo' => 1 );
+               $priv->dataDirty = false;
+               $backend->addData( array( 'foo' => 1 ) );
+               $this->assertSame( array( 'foo' => 1 ), $priv->data );
+               $this->assertFalse( $priv->dataDirty );
+
+               $priv->data = array( 'foo' => 1 );
+               $priv->dataDirty = false;
+               $backend->addData( array( 'foo' => '1' ) );
+               $this->assertSame( array( 'foo' => '1' ), $priv->data );
+               $this->assertTrue( $priv->dataDirty );
+
+               $priv->data = array( 'foo' => 1 );
+               $priv->dataDirty = false;
+               $backend->addData( array( 'bar' => 2 ) );
+               $this->assertSame( array( 'foo' => 1, 'bar' => 2 ), $priv->data );
+               $this->assertTrue( $priv->dataDirty );
+       }
+
+       public function testDelaySave() {
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $backend = $this->getBackend();
+               $priv = \TestingAccessWrapper::newFromObject( $backend );
+               $priv->persist = true;
+
+               // Saves happen normally when no delay is in effect
+               $this->onSessionMetadataCalled = false;
+               $priv->metaDirty = true;
+               $backend->save();
+               $this->assertTrue( $this->onSessionMetadataCalled, 'sanity check' );
+
+               $this->onSessionMetadataCalled = false;
+               $priv->metaDirty = true;
+               $priv->autosave();
+               $this->assertTrue( $this->onSessionMetadataCalled, 'sanity check' );
+
+               $delay = $backend->delaySave();
+
+               // Autosave doesn't happen when no delay is in effect
+               $this->onSessionMetadataCalled = false;
+               $priv->metaDirty = true;
+               $priv->autosave();
+               $this->assertFalse( $this->onSessionMetadataCalled );
+
+               // Save still does happen when no delay is in effect
+               $priv->save();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+
+               // Save happens when delay is consumed
+               $this->onSessionMetadataCalled = false;
+               $priv->metaDirty = true;
+               \ScopedCallback::consume( $delay );
+               $this->assertTrue( $this->onSessionMetadataCalled );
+
+               // Test multiple delays
+               $delay1 = $backend->delaySave();
+               $delay2 = $backend->delaySave();
+               $delay3 = $backend->delaySave();
+               $this->onSessionMetadataCalled = false;
+               $priv->metaDirty = true;
+               $priv->autosave();
+               $this->assertFalse( $this->onSessionMetadataCalled );
+               \ScopedCallback::consume( $delay3 );
+               $this->assertFalse( $this->onSessionMetadataCalled );
+               \ScopedCallback::consume( $delay1 );
+               $this->assertFalse( $this->onSessionMetadataCalled );
+               \ScopedCallback::consume( $delay2 );
+               $this->assertTrue( $this->onSessionMetadataCalled );
+       }
+
+       public function testSave() {
+               $user = User::newFromName( 'UTSysop' );
+               $this->store = new TestBagOStuff();
+               $testData = array( 'foo' => 'foo!', 'bar', array( 'baz', null ) );
+
+               $neverHook = $this->getMock( __CLASS__, array( 'onSessionMetadata' ) );
+               $neverHook->expects( $this->never() )->method( 'onSessionMetadata' );
+
+               $neverProvider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $neverProvider->expects( $this->never() )->method( 'persistSession' );
+
+               // Not persistent or dirty
+               $this->provider = $neverProvider;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $neverHook ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               $this->assertFalse( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
+               $backend->save();
+               $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
+
+               // Not persistent, but dirty
+               $this->provider = $neverProvider;
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               $this->assertFalse( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
+               $backend->save();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+
+               // Persistent, not dirty
+               $this->provider = $neverProvider;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $neverHook ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
+               $backend->save();
+               $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
+
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $neverHook ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               \TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
+               $backend->save();
+               $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
+
+               // Persistent and dirty
+               $this->provider = $neverProvider;
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
+               $backend->save();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               \TestingAccessWrapper::newFromObject( $backend )->forcePersist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
+               $backend->save();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = true;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
+               $backend->save();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+
+               // Not marked dirty, but dirty data
+               $this->provider = $neverProvider;
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = false;
+               \TestingAccessWrapper::newFromObject( $backend )->dataHash = 'Doesn\'t match';
+               $backend->save();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+
+               // Bad hook
+               $this->provider = null;
+               $mockHook = $this->getMock( __CLASS__, array( 'onSessionMetadata' ) );
+               $mockHook->expects( $this->any() )->method( 'onSessionMetadata' )
+                       ->will( $this->returnCallback(
+                               function ( SessionBackend $backend, array &$metadata, array $requests ) {
+                                       $metadata['userId']++;
+                               }
+                       ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $mockHook ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $backend->dirty();
+               try {
+                       $backend->save();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'SessionMetadata hook changed metadata key "userId"',
+                               $ex->getMessage()
+                       );
+               }
+
+               // SessionManager::preventSessionsForUser
+               \TestingAccessWrapper::newFromObject( $this->manager )->preventUsers = array(
+                       $user->getName() => true,
+               );
+               $this->provider = $neverProvider;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $neverHook ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               \TestingAccessWrapper::newFromObject( $backend )->persist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               \TestingAccessWrapper::newFromObject( $backend )->metaDirty = true;
+               \TestingAccessWrapper::newFromObject( $backend )->dataDirty = true;
+               $backend->save();
+               $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
+       }
+
+       public function testRenew() {
+               $user = User::newFromName( 'UTSysop' );
+               $this->store = new TestBagOStuff();
+               $testData = array( 'foo' => 'foo!', 'bar', array( 'baz', null ) );
+
+               // Not persistent
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->never() )->method( 'persistSession' );
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               $wrap = \TestingAccessWrapper::newFromObject( $backend );
+               $this->assertFalse( $backend->isPersistent(), 'sanity check' );
+               $wrap->metaDirty = false;
+               $wrap->dataDirty = false;
+               $wrap->forcePersist = false;
+               $wrap->expires = 0;
+               $backend->renew();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertNotEquals( 0, $wrap->expires );
+
+               // Persistent
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->atLeastOnce() )->method( 'persistSession' );
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               $wrap = \TestingAccessWrapper::newFromObject( $backend );
+               $wrap->persist = true;
+               $this->assertTrue( $backend->isPersistent(), 'sanity check' );
+               $wrap->metaDirty = false;
+               $wrap->dataDirty = false;
+               $wrap->forcePersist = false;
+               $wrap->expires = 0;
+               $backend->renew();
+               $this->assertTrue( $this->onSessionMetadataCalled );
+               $blob = $this->store->getSession( self::SESSIONID );
+               $this->assertInternalType( 'array', $blob );
+               $this->assertArrayHasKey( 'metadata', $blob );
+               $metadata = $blob['metadata'];
+               $this->assertInternalType( 'array', $metadata );
+               $this->assertArrayHasKey( '???', $metadata );
+               $this->assertSame( '!!!', $metadata['???'] );
+               $this->assertNotEquals( 0, $wrap->expires );
+
+               // Not persistent, not expiring
+               $this->provider = $this->getMock( 'DummySessionProvider', array( 'persistSession' ) );
+               $this->provider->expects( $this->never() )->method( 'persistSession' );
+               $this->onSessionMetadataCalled = false;
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'SessionMetadata' => array( $this ) ) );
+               $this->store->setSessionData( self::SESSIONID, $testData );
+               $backend = $this->getBackend( $user );
+               $this->store->deleteSession( self::SESSIONID );
+               $wrap = \TestingAccessWrapper::newFromObject( $backend );
+               $this->assertFalse( $backend->isPersistent(), 'sanity check' );
+               $wrap->metaDirty = false;
+               $wrap->dataDirty = false;
+               $wrap->forcePersist = false;
+               $expires = time() + $wrap->lifetime + 100;
+               $wrap->expires = $expires;
+               $backend->renew();
+               $this->assertFalse( $this->onSessionMetadataCalled );
+               $this->assertFalse( $this->store->getSession( self::SESSIONID ), 'making sure it didn\'t save' );
+               $this->assertEquals( $expires, $wrap->expires );
+       }
+
+       public function onSessionMetadata( SessionBackend $backend, array &$metadata, array $requests ) {
+               $this->onSessionMetadataCalled = true;
+               $metadata['???'] = '!!!';
+       }
+
+       public function testResetIdOfGlobalSession() {
+               if ( !PHPSessionHandler::isInstalled() ) {
+                       PHPSessionHandler::install( SessionManager::singleton() );
+               }
+               if ( !PHPSessionHandler::isEnabled() ) {
+                       $rProp = new \ReflectionProperty( 'MediaWiki\\Session\\PHPSessionHandler', 'instance' );
+                       $rProp->setAccessible( true );
+                       $handler = \TestingAccessWrapper::newFromObject( $rProp->getValue() );
+                       $resetHandler = new \ScopedCallback( function () use ( $handler ) {
+                               session_write_close();
+                               $handler->enable = false;
+                       } );
+                       $handler->enable = true;
+               }
+
+               $backend = $this->getBackend( User::newFromName( 'UTSysop' ) );
+               \TestingAccessWrapper::newFromObject( $backend )->usePhpSessionHandling = true;
+
+               TestUtils::setSessionManagerSingleton( $this->manager );
+
+               $manager = \TestingAccessWrapper::newFromObject( $this->manager );
+               $request = \RequestContext::getMain()->getRequest();
+               $manager->globalSession = $backend->getSession( $request );
+               $manager->globalSessionRequest = $request;
+
+               session_id( self::SESSIONID );
+               \MediaWiki\quietCall( 'session_start' );
+               $backend->resetId();
+               $this->assertNotEquals( self::SESSIONID, $backend->getId() );
+               $this->assertSame( $backend->getId(), session_id() );
+               session_write_close();
+
+               session_id( '' );
+               $this->assertNotSame( $backend->getId(), session_id(), 'sanity check' );
+               $backend->persist();
+               $this->assertSame( $backend->getId(), session_id() );
+               session_write_close();
+       }
+
+       public function testGetAllowedUserRights() {
+               $this->provider = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( array( 'getAllowedUserRights' ) )
+                       ->getMock();
+               $this->provider->expects( $this->any() )->method( 'getAllowedUserRights' )
+                       ->will( $this->returnValue( array( 'foo', 'bar' ) ) );
+
+               $backend = $this->getBackend();
+               $this->assertSame( array( 'foo', 'bar' ), $backend->getAllowedUserRights() );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/SessionIdTest.php b/tests/phpunit/includes/session/SessionIdTest.php
new file mode 100644 (file)
index 0000000..2b06d97
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+
+/**
+ * @group Session
+ * @covers MediaWiki\Session\SessionId
+ */
+class SessionIdTest extends MediaWikiTestCase {
+
+       public function testEverything() {
+               $id = new SessionId( 'foo' );
+               $this->assertSame( 'foo', $id->getId() );
+               $this->assertSame( 'foo', (string)$id );
+               $id->setId( 'bar' );
+               $this->assertSame( 'bar', $id->getId() );
+               $this->assertSame( 'bar', (string)$id );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/SessionInfoTest.php b/tests/phpunit/includes/session/SessionInfoTest.php
new file mode 100644 (file)
index 0000000..b411f3c
--- /dev/null
@@ -0,0 +1,328 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LogLevel;
+use MediaWikiTestCase;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\SessionInfo
+ */
+class SessionInfoTest extends MediaWikiTestCase {
+
+       public function testBasics() {
+               $anonInfo = UserInfo::newAnonymous();
+               $userInfo = UserInfo::newFromName( 'UTSysop', true );
+               $unverifiedUserInfo = UserInfo::newFromName( 'UTSysop', false );
+
+               try {
+                       new SessionInfo( SessionInfo::MIN_PRIORITY - 1, array() );
+                       $this->fail( 'Expected exception not thrown', 'priority < min' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid priority', $ex->getMessage(), 'priority < min' );
+               }
+
+               try {
+                       new SessionInfo( SessionInfo::MAX_PRIORITY + 1, array() );
+                       $this->fail( 'Expected exception not thrown', 'priority > max' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid priority', $ex->getMessage(), 'priority > max' );
+               }
+
+               try {
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array( 'id' => 'ABC?' ) );
+                       $this->fail( 'Expected exception not thrown', 'bad session ID' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid session ID', $ex->getMessage(), 'bad session ID' );
+               }
+
+               try {
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array( 'userInfo' => new \stdClass ) );
+                       $this->fail( 'Expected exception not thrown', 'bad userInfo' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid userInfo', $ex->getMessage(), 'bad userInfo' );
+               }
+
+               try {
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array() );
+                       $this->fail( 'Expected exception not thrown', 'no provider, no id' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Must supply an ID when no provider is given', $ex->getMessage(),
+                               'no provider, no id' );
+               }
+
+               try {
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array( 'copyFrom' => new \stdClass ) );
+                       $this->fail( 'Expected exception not thrown', 'bad copyFrom' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid copyFrom', $ex->getMessage(),
+                               'bad copyFrom' );
+               }
+
+               $manager = new SessionManager();
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->setMethods( array( 'persistsSessionId', 'canChangeUser', '__toString' ) )
+                       ->getMockForAbstractClass();
+               $provider->setManager( $manager );
+               $provider->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( true ) );
+               $provider->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( true ) );
+               $provider->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Mock' ) );
+
+               $provider2 = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->setMethods( array( 'persistsSessionId', 'canChangeUser', '__toString' ) )
+                       ->getMockForAbstractClass();
+               $provider2->setManager( $manager );
+               $provider2->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( true ) );
+               $provider2->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( true ) );
+               $provider2->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Mock2' ) );
+
+               try {
+                       new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                               'provider' => $provider,
+                               'userInfo' => $anonInfo,
+                               'metadata' => 'foo',
+                       ) );
+                       $this->fail( 'Expected exception not thrown', 'bad metadata' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid metadata', $ex->getMessage(), 'bad metadata' );
+               }
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'userInfo' => $anonInfo
+               ) );
+               $this->assertSame( $provider, $info->getProvider() );
+               $this->assertNotNull( $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertSame( $anonInfo, $info->getUserInfo() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertFalse( $info->wasPersisted() );
+               $this->assertFalse( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertNull( $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'userInfo' => $unverifiedUserInfo,
+                       'metadata' => array( 'Foo' ),
+               ) );
+               $this->assertSame( $provider, $info->getProvider() );
+               $this->assertNotNull( $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertFalse( $info->wasPersisted() );
+               $this->assertFalse( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array( 'Foo' ), $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertSame( $provider, $info->getProvider() );
+               $this->assertNotNull( $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertSame( $userInfo, $info->getUserInfo() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertFalse( $info->wasPersisted() );
+               $this->assertTrue( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertNull( $info->getProviderMetadata() );
+
+               $id = $manager->generateSessionId();
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'persisted' => true,
+                       'userInfo' => $anonInfo
+               ) );
+               $this->assertSame( $provider, $info->getProvider() );
+               $this->assertSame( $id, $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertSame( $anonInfo, $info->getUserInfo() );
+               $this->assertFalse( $info->isIdSafe() );
+               $this->assertTrue( $info->wasPersisted() );
+               $this->assertFalse( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertNull( $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertSame( $provider, $info->getProvider() );
+               $this->assertSame( $id, $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertSame( $userInfo, $info->getUserInfo() );
+               $this->assertFalse( $info->isIdSafe() );
+               $this->assertFalse( $info->wasPersisted() );
+               $this->assertTrue( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertNull( $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'id' => $id,
+                       'persisted' => true,
+                       'userInfo' => $userInfo,
+                       'metadata' => array( 'Foo' ),
+               ) );
+               $this->assertSame( $id, $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertSame( $userInfo, $info->getUserInfo() );
+               $this->assertFalse( $info->isIdSafe() );
+               $this->assertTrue( $info->wasPersisted() );
+               $this->assertFalse( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertNull( $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'id' => $id,
+                       'remembered' => true,
+                       'userInfo' => $userInfo,
+               ) );
+               $this->assertFalse( $info->wasRemembered(), 'no provider' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'remembered' => true,
+               ) );
+               $this->assertFalse( $info->wasRemembered(), 'no user' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'remembered' => true,
+                       'userInfo' => $anonInfo,
+               ) );
+               $this->assertFalse( $info->wasRemembered(), 'anonymous user' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'remembered' => true,
+                       'userInfo' => $unverifiedUserInfo,
+               ) );
+               $this->assertFalse( $info->wasRemembered(), 'unverified user' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'remembered' => false,
+                       'userInfo' => $userInfo,
+               ) );
+               $this->assertFalse( $info->wasRemembered(), 'specific override' );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 5, array(
+                       'id' => $id,
+                       'idIsSafe' => true,
+               ) );
+               $this->assertSame( $id, $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 5, $info->getPriority() );
+               $this->assertTrue( $info->isIdSafe() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id,
+                       'forceHTTPS' => 1,
+               ) );
+               $this->assertTrue( $info->forceHTTPS() );
+
+               $fromInfo = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id . 'A',
+                       'provider' => $provider,
+                       'userInfo' => $userInfo,
+                       'idIsSafe' => true,
+                       'persisted' => true,
+                       'remembered' => true,
+                       'forceHTTPS' => true,
+                       'metadata' => array( 'foo!' ),
+               ) );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 4, array(
+                       'copyFrom' => $fromInfo,
+               ) );
+               $this->assertSame( $id . 'A', $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 4, $info->getPriority() );
+               $this->assertSame( $provider, $info->getProvider() );
+               $this->assertSame( $userInfo, $info->getUserInfo() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertTrue( $info->wasPersisted() );
+               $this->assertTrue( $info->wasRemembered() );
+               $this->assertTrue( $info->forceHTTPS() );
+               $this->assertSame( array( 'foo!' ), $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY + 4, array(
+                       'id' => $id . 'X',
+                       'provider' => $provider2,
+                       'userInfo' => $unverifiedUserInfo,
+                       'idIsSafe' => false,
+                       'persisted' => false,
+                       'remembered' => false,
+                       'forceHTTPS' => false,
+                       'metadata' => null,
+                       'copyFrom' => $fromInfo,
+               ) );
+               $this->assertSame( $id . 'X', $info->getId() );
+               $this->assertSame( SessionInfo::MIN_PRIORITY + 4, $info->getPriority() );
+               $this->assertSame( $provider2, $info->getProvider() );
+               $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
+               $this->assertFalse( $info->isIdSafe() );
+               $this->assertFalse( $info->wasPersisted() );
+               $this->assertFalse( $info->wasRemembered() );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertNull( $info->getProviderMetadata() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id,
+               ) );
+               $this->assertSame(
+                       '[' . SessionInfo::MIN_PRIORITY . "]null<null>$id",
+                       (string)$info,
+                       'toString'
+               );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'persisted' => true,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertSame(
+                       '[' . SessionInfo::MIN_PRIORITY . "]Mock<+:{$userInfo->getId()}:UTSysop>$id",
+                       (string)$info,
+                       'toString'
+               );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'persisted' => true,
+                       'userInfo' => $unverifiedUserInfo
+               ) );
+               $this->assertSame(
+                       '[' . SessionInfo::MIN_PRIORITY . "]Mock<-:{$userInfo->getId()}:UTSysop>$id",
+                       (string)$info,
+                       'toString'
+               );
+       }
+
+       public function testCompare() {
+               $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, array( 'id' => $id ) );
+               $info2 = new SessionInfo( SessionInfo::MIN_PRIORITY + 2, array( 'id' => $id ) );
+
+               $this->assertTrue( SessionInfo::compare( $info1, $info2 ) < 0, '<' );
+               $this->assertTrue( SessionInfo::compare( $info2, $info1 ) > 0, '>' );
+               $this->assertTrue( SessionInfo::compare( $info1, $info1 ) === 0, '==' );
+       }
+}
diff --git a/tests/phpunit/includes/session/SessionManagerTest.php b/tests/phpunit/includes/session/SessionManagerTest.php
new file mode 100644 (file)
index 0000000..b4687ba
--- /dev/null
@@ -0,0 +1,1672 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use Psr\Log\LogLevel;
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\SessionManager
+ */
+class SessionManagerTest extends MediaWikiTestCase {
+
+       protected $config, $logger, $store;
+
+       protected function getManager() {
+               \ObjectCache::$instances['testSessionStore'] = new TestBagOStuff();
+               $this->config = new \HashConfig( array(
+                       'LanguageCode' => 'en',
+                       'SessionCacheType' => 'testSessionStore',
+                       'ObjectCacheSessionExpiry' => 100,
+                       'SessionProviders' => array(
+                               array( 'class' => 'DummySessionProvider' ),
+                       )
+               ) );
+               $this->logger = new \TestLogger( false, function ( $m ) {
+                       return substr( $m, 0, 15 ) === 'SessionBackend ' ? null : $m;
+               } );
+               $this->store = new TestBagOStuff();
+
+               return new SessionManager( array(
+                       'config' => $this->config,
+                       'logger' => $this->logger,
+                       'store' => $this->store,
+               ) );
+       }
+
+       protected function objectCacheDef( $object ) {
+               return array( 'factory' => function () use ( $object ) {
+                       return $object;
+               } );
+       }
+
+       public function testSingleton() {
+               $reset = TestUtils::setSessionManagerSingleton( null );
+
+               $singleton = SessionManager::singleton();
+               $this->assertInstanceOf( 'MediaWiki\\Session\\SessionManager', $singleton );
+               $this->assertSame( $singleton, SessionManager::singleton() );
+       }
+
+       public function testGetGlobalSession() {
+               $context = \RequestContext::getMain();
+
+               if ( !PHPSessionHandler::isInstalled() ) {
+                       PHPSessionHandler::install( SessionManager::singleton() );
+               }
+               $rProp = new \ReflectionProperty( 'MediaWiki\\Session\\PHPSessionHandler', 'instance' );
+               $rProp->setAccessible( true );
+               $handler = \TestingAccessWrapper::newFromObject( $rProp->getValue() );
+               $oldEnable = $handler->enable;
+               $reset[] = new \ScopedCallback( function () use ( $handler, $oldEnable ) {
+                       if ( $handler->enable ) {
+                               session_write_close();
+                       }
+                       $handler->enable = $oldEnable;
+               } );
+               $reset[] = TestUtils::setSessionManagerSingleton( $this->getManager() );
+
+               $handler->enable = true;
+               $request = new \FauxRequest();
+               $context->setRequest( $request );
+               $id = $request->getSession()->getId();
+
+               session_id( '' );
+               $session = SessionManager::getGlobalSession();
+               $this->assertSame( $id, $session->getId() );
+
+               session_id( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
+               $session = SessionManager::getGlobalSession();
+               $this->assertSame( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $session->getId() );
+               $this->assertSame( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', $request->getSession()->getId() );
+
+               session_write_close();
+               $handler->enable = false;
+               $request = new \FauxRequest();
+               $context->setRequest( $request );
+               $id = $request->getSession()->getId();
+
+               session_id( '' );
+               $session = SessionManager::getGlobalSession();
+               $this->assertSame( $id, $session->getId() );
+
+               session_id( 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' );
+               $session = SessionManager::getGlobalSession();
+               $this->assertSame( $id, $session->getId() );
+               $this->assertSame( $id, $request->getSession()->getId() );
+       }
+
+       public function testConstructor() {
+               $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
+               $this->assertSame( $this->config, $manager->config );
+               $this->assertSame( $this->logger, $manager->logger );
+               $this->assertSame( $this->store, $manager->store );
+
+               $manager = \TestingAccessWrapper::newFromObject( new SessionManager() );
+               $this->assertSame( \RequestContext::getMain()->getConfig(), $manager->config );
+
+               $manager = \TestingAccessWrapper::newFromObject( new SessionManager( array(
+                       'config' => $this->config,
+               ) ) );
+               $this->assertSame( \ObjectCache::$instances['testSessionStore'], $manager->store );
+
+               foreach ( array(
+                       'config' => '$options[\'config\'] must be an instance of Config',
+                       'logger' => '$options[\'logger\'] must be an instance of LoggerInterface',
+                       'store' => '$options[\'store\'] must be an instance of BagOStuff',
+               ) as $key => $error ) {
+                       try {
+                               new SessionManager( array( $key => new \stdClass ) );
+                               $this->fail( 'Expected exception not thrown' );
+                       } catch ( \InvalidArgumentException $ex ) {
+                               $this->assertSame( $error, $ex->getMessage() );
+                       }
+               }
+       }
+
+       public function testGetSessionForRequest() {
+               $manager = $this->getManager();
+               $request = new \FauxRequest();
+
+               $id1 = '';
+               $id2 = '';
+               $idEmpty = 'empty-session-------------------';
+
+               $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods(
+                               array( 'provideSessionInfo', 'newSessionInfo', '__toString', 'describe' )
+                       );
+
+               $provider1 = $providerBuilder->getMock();
+               $provider1->expects( $this->any() )->method( 'provideSessionInfo' )
+                       ->with( $this->identicalTo( $request ) )
+                       ->will( $this->returnCallback( function ( $request ) {
+                               return $request->info1;
+                       } ) );
+               $provider1->expects( $this->any() )->method( 'newSessionInfo' )
+                       ->will( $this->returnCallback( function () use ( $idEmpty, $provider1 ) {
+                               return new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                                       'provider' => $provider1,
+                                       'id' => $idEmpty,
+                                       'persisted' => true,
+                                       'idIsSafe' => true,
+                               ) );
+                       } ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Provider1' ) );
+               $provider1->expects( $this->any() )->method( 'describe' )
+                       ->will( $this->returnValue( '#1 sessions' ) );
+
+               $provider2 = $providerBuilder->getMock();
+               $provider2->expects( $this->any() )->method( 'provideSessionInfo' )
+                       ->with( $this->identicalTo( $request ) )
+                       ->will( $this->returnCallback( function ( $request ) {
+                               return $request->info2;
+                       } ) );
+               $provider2->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Provider2' ) );
+               $provider2->expects( $this->any() )->method( 'describe' )
+                       ->will( $this->returnValue( '#2 sessions' ) );
+
+               $this->config->set( 'SessionProviders', array(
+                       $this->objectCacheDef( $provider1 ),
+                       $this->objectCacheDef( $provider2 ),
+               ) );
+
+               // No provider returns info
+               $request->info1 = null;
+               $request->info2 = null;
+               $session = $manager->getSessionForRequest( $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $idEmpty, $session->getId() );
+               $this->assertNull( $manager->getPersistedSessionId( $request ) );
+
+               // Both providers return info, picks best one
+               $request->info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, array(
+                       'provider' => $provider1,
+                       'id' => ( $id1 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $request->info2 = new SessionInfo( SessionInfo::MIN_PRIORITY + 2, array(
+                       'provider' => $provider2,
+                       'id' => ( $id2 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $session = $manager->getSessionForRequest( $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id2, $session->getId() );
+               $this->assertSame( $id2, $manager->getPersistedSessionId( $request ) );
+
+               $request->info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 2, array(
+                       'provider' => $provider1,
+                       'id' => ( $id1 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $request->info2 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, array(
+                       'provider' => $provider2,
+                       'id' => ( $id2 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $session = $manager->getSessionForRequest( $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id1, $session->getId() );
+               $this->assertSame( $id1, $manager->getPersistedSessionId( $request ) );
+
+               // Tied priorities
+               $request->info1 = new SessionInfo( SessionInfo::MAX_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => ( $id1 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newAnonymous(),
+                       'idIsSafe' => true,
+               ) );
+               $request->info2 = new SessionInfo( SessionInfo::MAX_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => ( $id2 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newAnonymous(),
+                       'idIsSafe' => true,
+               ) );
+               try {
+                       $manager->getSessionForRequest( $request );
+                       $this->fail( 'Expcected exception not thrown' );
+               } catch ( \OverFlowException $ex ) {
+                       $this->assertStringStartsWith(
+                               'Multiple sessions for this request tied for top priority: ',
+                               $ex->getMessage()
+                       );
+                       $this->assertCount( 2, $ex->sessionInfos );
+                       $this->assertContains( $request->info1, $ex->sessionInfos );
+                       $this->assertContains( $request->info2, $ex->sessionInfos );
+               }
+               try {
+                       $manager->getPersistedSessionId( $request );
+                       $this->fail( 'Expcected exception not thrown' );
+               } catch ( \OverFlowException $ex ) {
+                       $this->assertStringStartsWith(
+                               'Multiple sessions for this request tied for top priority: ',
+                               $ex->getMessage()
+                       );
+                       $this->assertCount( 2, $ex->sessionInfos );
+                       $this->assertContains( $request->info1, $ex->sessionInfos );
+                       $this->assertContains( $request->info2, $ex->sessionInfos );
+               }
+
+               // Bad provider
+               $request->info1 = new SessionInfo( SessionInfo::MAX_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => ( $id1 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $request->info2 = null;
+               try {
+                       $manager->getSessionForRequest( $request );
+                       $this->fail( 'Expcected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'Provider1 returned session info for a different provider: ' . $request->info1,
+                               $ex->getMessage()
+                       );
+               }
+               try {
+                       $manager->getPersistedSessionId( $request );
+                       $this->fail( 'Expcected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'Provider1 returned session info for a different provider: ' . $request->info1,
+                               $ex->getMessage()
+                       );
+               }
+
+               // Unusable session info
+               $this->logger->setCollect( true );
+               $request->info1 = new SessionInfo( SessionInfo::MAX_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => ( $id1 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', false ),
+                       'idIsSafe' => true,
+               ) );
+               $request->info2 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => ( $id2 = $manager->generateSessionId() ),
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $session = $manager->getSessionForRequest( $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id2, $session->getId() );
+               $this->assertSame( $id2, $manager->getPersistedSessionId( $request ) );
+               $this->logger->setCollect( false );
+
+               // Unpersisted session ID
+               $request->info1 = new SessionInfo( SessionInfo::MAX_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => ( $id1 = $manager->generateSessionId() ),
+                       'persisted' => false,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
+                       'idIsSafe' => true,
+               ) );
+               $request->info2 = null;
+               $session = $manager->getSessionForRequest( $request );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id1, $session->getId() );
+               $session->persist();
+               $this->assertTrue( $session->isPersistent(), 'sanity check' );
+               $this->assertNull( $manager->getPersistedSessionId( $request ) );
+       }
+
+       public function testGetSessionById() {
+               $manager = $this->getManager();
+
+               try {
+                       $manager->getSessionById( 'bad' );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid session ID', $ex->getMessage() );
+               }
+
+               // Unknown session ID
+               $id = $manager->generateSessionId();
+               $session = $manager->getSessionById( $id, true );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id, $session->getId() );
+
+               $id = $manager->generateSessionId();
+               $this->assertNull( $manager->getSessionById( $id, false ) );
+
+               // Known but unloadable session ID
+               $this->logger->setCollect( true );
+               $id = $manager->generateSessionId();
+               $this->store->setSession( $id, array( 'metadata' => array(
+                       'userId' => User::idFromName( 'UTSysop' ),
+                       'userToken' => 'bad',
+               ) ) );
+
+               $this->assertNull( $manager->getSessionById( $id, true ) );
+               $this->assertNull( $manager->getSessionById( $id, false ) );
+               $this->logger->setCollect( false );
+
+               // Known session ID
+               $this->store->setSession( $id, array() );
+               $session = $manager->getSessionById( $id, false );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id, $session->getId() );
+       }
+
+       public function testGetEmptySession() {
+               $manager = $this->getManager();
+               $pmanager = \TestingAccessWrapper::newFromObject( $manager );
+               $request = new \FauxRequest();
+
+               $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( array( 'provideSessionInfo', 'newSessionInfo', '__toString' ) );
+
+               $expectId = null;
+               $info1 = null;
+               $info2 = null;
+
+               $provider1 = $providerBuilder->getMock();
+               $provider1->expects( $this->any() )->method( 'provideSessionInfo' )
+                       ->will( $this->returnValue( null ) );
+               $provider1->expects( $this->any() )->method( 'newSessionInfo' )
+                       ->with( $this->callback( function ( $id ) use ( &$expectId ) {
+                               return $id === $expectId;
+                       } ) )
+                       ->will( $this->returnCallback( function () use ( &$info1 ) {
+                               return $info1;
+                       } ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider1' ) );
+
+               $provider2 = $providerBuilder->getMock();
+               $provider2->expects( $this->any() )->method( 'provideSessionInfo' )
+                       ->will( $this->returnValue( null ) );
+               $provider2->expects( $this->any() )->method( 'newSessionInfo' )
+                       ->with( $this->callback( function ( $id ) use ( &$expectId ) {
+                               return $id === $expectId;
+                       } ) )
+                       ->will( $this->returnCallback( function () use ( &$info2 ) {
+                               return $info2;
+                       } ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider2' ) );
+
+               $this->config->set( 'SessionProviders', array(
+                       $this->objectCacheDef( $provider1 ),
+                       $this->objectCacheDef( $provider2 ),
+               ) );
+
+               // No info
+               $expectId = null;
+               $info1 = null;
+               $info2 = null;
+               try {
+                       $manager->getEmptySession();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'No provider could provide an empty session!',
+                               $ex->getMessage()
+                       );
+               }
+
+               // Info
+               $expectId = null;
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => 'empty---------------------------',
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $info2 = null;
+               $session = $manager->getEmptySession();
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( 'empty---------------------------', $session->getId() );
+
+               // Info, explicitly
+               $expectId = 'expected------------------------';
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => $expectId,
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $info2 = null;
+               $session = $pmanager->getEmptySessionInternal( null, $expectId );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $expectId, $session->getId() );
+
+               // Wrong ID
+               $expectId = 'expected-----------------------2';
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => "un$expectId",
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $info2 = null;
+               try {
+                       $pmanager->getEmptySessionInternal( null, $expectId );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'MockProvider1 returned empty session info with a wrong id: ' .
+                                       "un$expectId != $expectId",
+                               $ex->getMessage()
+                       );
+               }
+
+               // Unsafe ID
+               $expectId = 'expected-----------------------2';
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => $expectId,
+                       'persisted' => true,
+               ) );
+               $info2 = null;
+               try {
+                       $pmanager->getEmptySessionInternal( null, $expectId );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'MockProvider1 returned empty session info with id flagged unsafe',
+                               $ex->getMessage()
+                       );
+               }
+
+               // Wrong provider
+               $expectId = null;
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => 'empty---------------------------',
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $info2 = null;
+               try {
+                       $manager->getEmptySession();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'MockProvider1 returned an empty session info for a different provider: ' . $info1,
+                               $ex->getMessage()
+                       );
+               }
+
+               // Highest priority wins
+               $expectId = null;
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, array(
+                       'provider' => $provider1,
+                       'id' => 'empty1--------------------------',
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $info2 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => 'empty2--------------------------',
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $session = $manager->getEmptySession();
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( 'empty1--------------------------', $session->getId() );
+
+               $expectId = null;
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY + 1, array(
+                       'provider' => $provider1,
+                       'id' => 'empty1--------------------------',
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $info2 = new SessionInfo( SessionInfo::MIN_PRIORITY + 2, array(
+                       'provider' => $provider2,
+                       'id' => 'empty2--------------------------',
+                       'persisted' => true,
+                       'idIsSafe' => true,
+               ) );
+               $session = $manager->getEmptySession();
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( 'empty2--------------------------', $session->getId() );
+
+               // Tied priorities throw an exception
+               $expectId = null;
+               $info1 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider1,
+                       'id' => 'empty1--------------------------',
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newAnonymous(),
+                       'idIsSafe' => true,
+               ) );
+               $info2 = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => 'empty2--------------------------',
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newAnonymous(),
+                       'idIsSafe' => true,
+               ) );
+               try {
+                       $manager->getEmptySession();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertStringStartsWith(
+                               'Multiple empty sessions tied for top priority: ',
+                               $ex->getMessage()
+                       );
+               }
+
+               // Bad id
+               try {
+                       $pmanager->getEmptySessionInternal( null, 'bad' );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid session ID', $ex->getMessage() );
+               }
+
+               // Session already exists
+               $expectId = 'expected-----------------------3';
+               $this->store->setSessionMeta( $expectId, array(
+                       'provider' => 'MockProvider2',
+                       'userId' => 0,
+                       'userName' => null,
+                       'userToken' => null,
+               ) );
+               try {
+                       $pmanager->getEmptySessionInternal( null, $expectId );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Session ID already exists', $ex->getMessage() );
+               }
+       }
+
+       public function testGetVaryHeaders() {
+               $manager = $this->getManager();
+
+               $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( array( 'getVaryHeaders', '__toString' ) );
+
+               $provider1 = $providerBuilder->getMock();
+               $provider1->expects( $this->once() )->method( 'getVaryHeaders' )
+                       ->will( $this->returnValue( array(
+                               'Foo' => null,
+                               'Bar' => array( 'X', 'Bar1' ),
+                               'Quux' => null,
+                       ) ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider1' ) );
+
+               $provider2 = $providerBuilder->getMock();
+               $provider2->expects( $this->once() )->method( 'getVaryHeaders' )
+                       ->will( $this->returnValue( array(
+                               'Baz' => null,
+                               'Bar' => array( 'X', 'Bar2' ),
+                               'Quux' => array( 'Quux' ),
+                       ) ) );
+               $provider2->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider2' ) );
+
+               $this->config->set( 'SessionProviders', array(
+                       $this->objectCacheDef( $provider1 ),
+                       $this->objectCacheDef( $provider2 ),
+               ) );
+
+               $expect = array(
+                       'Foo' => array(),
+                       'Bar' => array( 'X', 'Bar1', 3 => 'Bar2' ),
+                       'Quux' => array( 'Quux' ),
+                       'Baz' => array(),
+                       'Quux' => array( 'Quux' ),
+               );
+
+               $this->assertEquals( $expect, $manager->getVaryHeaders() );
+
+               // Again, to ensure it's cached
+               $this->assertEquals( $expect, $manager->getVaryHeaders() );
+       }
+
+       public function testGetVaryCookies() {
+               $manager = $this->getManager();
+
+               $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( array( 'getVaryCookies', '__toString' ) );
+
+               $provider1 = $providerBuilder->getMock();
+               $provider1->expects( $this->once() )->method( 'getVaryCookies' )
+                       ->will( $this->returnValue( array( 'Foo', 'Bar' ) ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider1' ) );
+
+               $provider2 = $providerBuilder->getMock();
+               $provider2->expects( $this->once() )->method( 'getVaryCookies' )
+                       ->will( $this->returnValue( array( 'Foo', 'Baz' ) ) );
+               $provider2->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider2' ) );
+
+               $this->config->set( 'SessionProviders', array(
+                       $this->objectCacheDef( $provider1 ),
+                       $this->objectCacheDef( $provider2 ),
+               ) );
+
+               $expect = array( 'Foo', 'Bar', 'Baz' );
+
+               $this->assertEquals( $expect, $manager->getVaryCookies() );
+
+               // Again, to ensure it's cached
+               $this->assertEquals( $expect, $manager->getVaryCookies() );
+       }
+
+       public function testGetProviders() {
+               $realManager = $this->getManager();
+               $manager = \TestingAccessWrapper::newFromObject( $realManager );
+
+               $this->config->set( 'SessionProviders', array(
+                       array( 'class' => 'DummySessionProvider' ),
+               ) );
+               $providers = $manager->getProviders();
+               $this->assertArrayHasKey( 'DummySessionProvider', $providers );
+               $provider = \TestingAccessWrapper::newFromObject( $providers['DummySessionProvider'] );
+               $this->assertSame( $manager->logger, $provider->logger );
+               $this->assertSame( $manager->config, $provider->config );
+               $this->assertSame( $realManager, $provider->getManager() );
+
+               $this->config->set( 'SessionProviders', array(
+                       array( 'class' => 'DummySessionProvider' ),
+                       array( 'class' => 'DummySessionProvider' ),
+               ) );
+               $manager->sessionProviders = null;
+               try {
+                       $manager->getProviders();
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'Duplicate provider name "DummySessionProvider"',
+                               $ex->getMessage()
+                       );
+               }
+       }
+
+       public function testShutdown() {
+               $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
+               $manager->setLogger( new \Psr\Log\NullLogger() );
+
+               $mock = $this->getMock( 'stdClass', array( 'save' ) );
+               $mock->expects( $this->once() )->method( 'save' );
+
+               $manager->allSessionBackends = array( $mock );
+               $manager->shutdown();
+       }
+
+       public function testGetSessionFromInfo() {
+               $manager = \TestingAccessWrapper::newFromObject( $this->getManager() );
+               $request = new \FauxRequest();
+
+               $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $manager->getProvider( 'DummySessionProvider' ),
+                       'id' => $id,
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newFromName( 'UTSysop', true ),
+                       'idIsSafe' => true,
+               ) );
+               \TestingAccessWrapper::newFromObject( $info )->idIsSafe = true;
+               $session1 = \TestingAccessWrapper::newFromObject(
+                       $manager->getSessionFromInfo( $info, $request )
+               );
+               $session2 = \TestingAccessWrapper::newFromObject(
+                       $manager->getSessionFromInfo( $info, $request )
+               );
+
+               $this->assertSame( $session1->backend, $session2->backend );
+               $this->assertNotEquals( $session1->index, $session2->index );
+               $this->assertSame( $session1->getSessionId(), $session2->getSessionId() );
+               $this->assertSame( $id, $session1->getId() );
+
+               \TestingAccessWrapper::newFromObject( $info )->idIsSafe = false;
+               $session3 = $manager->getSessionFromInfo( $info, $request );
+               $this->assertNotSame( $id, $session3->getId() );
+       }
+
+       public function testBackendRegistration() {
+               $manager = $this->getManager();
+
+               $session = $manager->getSessionForRequest( new \FauxRequest );
+               $backend = \TestingAccessWrapper::newFromObject( $session )->backend;
+               $sessionId = $session->getSessionId();
+               $id = (string)$sessionId;
+
+               $this->assertSame( $sessionId, $manager->getSessionById( $id, true )->getSessionId() );
+
+               $manager->changeBackendId( $backend );
+               $this->assertSame( $sessionId, $session->getSessionId() );
+               $this->assertNotEquals( $id, (string)$sessionId );
+               $id = (string)$sessionId;
+
+               $this->assertSame( $sessionId, $manager->getSessionById( $id, true )->getSessionId() );
+
+               // Destruction of the session here causes the backend to be deregistered
+               $session = null;
+
+               try {
+                       $manager->changeBackendId( $backend );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'Backend was not registered with this SessionManager', $ex->getMessage()
+                       );
+               }
+
+               try {
+                       $manager->deregisterSessionBackend( $backend );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'Backend was not registered with this SessionManager', $ex->getMessage()
+                       );
+               }
+
+               $session = $manager->getSessionById( $id, true );
+               $this->assertSame( $sessionId, $session->getSessionId() );
+       }
+
+       public function testGenerateSessionId() {
+               $manager = $this->getManager();
+
+               $id = $manager->generateSessionId();
+               $this->assertTrue( SessionManager::validateSessionId( $id ), "Generated ID: $id" );
+       }
+
+       public function testAutoCreateUser() {
+               global $wgGroupPermissions;
+
+               $that = $this;
+
+               \ObjectCache::$instances[__METHOD__] = new \HashBagOStuff();
+               $this->setMwGlobals( array( 'wgMainCacheType' => __METHOD__ ) );
+
+               $this->stashMwGlobals( array( '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 );
+                       $m = preg_replace( '/ - from: .*$/', ' - from: XXX', $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( array(), $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( array(
+                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate1) - from: XXX' ),
+               ), $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( array(
+                       array( 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( array(
+                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate2) - from: XXX' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Test account-creation block
+               $anon = new User;
+               $block = new \Block( array(
+                       'address' => $anon->getName(),
+                       'user' => $id,
+                       'reason' => __METHOD__,
+                       'expiry' => time() + 100500,
+                       'createAccount' => true,
+               ) );
+               $block->insert();
+               $this->assertInstanceOf( 'Block', $anon->isBlockedFromCreateAccount(), 'sanity check' );
+               $reset2 = new \ScopedCallback( array( $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( array(
+                       array( 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( array(
+                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate3) - from: XXX' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Test prevention by AuthPlugin
+               global $wgAuth;
+               $oldWgAuth = $wgAuth;
+               $mockWgAuth = $this->getMock( 'AuthPlugin', array( 'autoCreate' ) );
+               $mockWgAuth->expects( $this->once() )->method( 'autoCreate' )
+                       ->will( $this->returnValue( false ) );
+               $this->setMwGlobals( array(
+                       '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( array(
+                       'wgAuth' => $oldWgAuth,
+               ) );
+               $session->clear();
+               $this->assertSame( array(
+                       array( LogLevel::DEBUG, 'denied by AuthPlugin' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Test prevention by wfReadOnly()
+               $this->setMwGlobals( array(
+                       '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( array(
+                       'wgReadOnly' => false,
+               ) );
+               $session->clear();
+               $this->assertSame( array(
+                       array( 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( array(
+                       array( 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( array(
+                       array( LogLevel::DEBUG, 'Invalid username, blacklisting' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Test AbortAutoAccount hook
+               $mock = $this->getMock( __CLASS__, array( 'onAbortAutoAccount' ) );
+               $mock->expects( $this->once() )->method( 'onAbortAutoAccount' )
+                       ->will( $this->returnCallback( function ( User $user, &$msg ) {
+                               $msg = 'No way!';
+                               return false;
+                       } ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'AbortAutoAccount' => array( $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', array( 'AbortAutoAccount' => array() ) );
+               $session->clear();
+               $this->assertSame( array(
+                       array( LogLevel::DEBUG, 'denied by hook: No way!' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Test AbortAutoAccount hook screwing up the name
+               $mock = $this->getMock( 'stdClass', array( 'onAbortAutoAccount' ) );
+               $mock->expects( $this->once() )->method( 'onAbortAutoAccount' )
+                       ->will( $this->returnCallback( function ( User $user ) {
+                               $user->setName( 'UTDoesNotExistEither' );
+                       } ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array( 'AbortAutoAccount' => array( $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', array( 'AbortAutoAccount' => array() ) );
+               $session->clear();
+               $this->assertSame( array(), $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( array(
+                       array( 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 ) use ( $that ) {
+                       $that->assertNotEquals( 0, $user->getId() );
+                       $that->assertSame( 'UTSessionAutoCreate4', $user->getName() );
+                       $that->assertEquals(
+                               $user->getId(), User::idFromName( 'UTSessionAutoCreate4', User::READ_LATEST )
+                       );
+                       return true;
+               } );
+               $mock = $this->getMock( 'stdClass',
+                       array( 'onAuthPluginAutoCreate', 'onLocalUserCreated' ) );
+               $mock->expects( $this->once() )->method( 'onAuthPluginAutoCreate' )
+                       ->with( $cb );
+               $mock->expects( $this->once() )->method( 'onLocalUserCreated' )
+                       ->with( $cb, $this->identicalTo( true ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array(
+                       'AuthPluginAutoCreate' => array( $mock ),
+                       'LocalUserCreated' => array( $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', array(
+                       'AuthPluginAutoCreate' => array(),
+                       'LocalUserCreated' => array(),
+               ) );
+               $this->assertSame( array(
+                       array( LogLevel::INFO, 'creating new user (UTSessionAutoCreate4) - from: XXX' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+       }
+
+       public function onAbortAutoAccount( User $user, &$msg ) {
+       }
+
+       public function testPreventSessionsForUser() {
+               $manager = $this->getManager();
+
+               $providerBuilder = $this->getMockBuilder( 'DummySessionProvider' )
+                       ->setMethods( array( 'preventSessionsForUser', '__toString' ) );
+
+               $provider1 = $providerBuilder->getMock();
+               $provider1->expects( $this->once() )->method( 'preventSessionsForUser' )
+                       ->with( $this->equalTo( 'UTSysop' ) );
+               $provider1->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'MockProvider1' ) );
+
+               $this->config->set( 'SessionProviders', array(
+                       $this->objectCacheDef( $provider1 ),
+               ) );
+
+               $user = User::newFromName( 'UTSysop' );
+               $token = $user->getToken( true );
+
+               $this->assertFalse( $manager->isUserSessionPrevented( 'UTSysop' ) );
+               $manager->preventSessionsForUser( 'UTSysop' );
+               $this->assertNotEquals( $token, User::newFromName( 'UTSysop' )->getToken() );
+               $this->assertTrue( $manager->isUserSessionPrevented( 'UTSysop' ) );
+       }
+
+       public function testLoadSessionInfoFromStore() {
+               $manager = $this->getManager();
+               $logger = new \TestLogger( true, function ( $m ) {
+                       return preg_replace(
+                               '/^Session \[\d+\]\w+<(?:null|anon|[+-]:\d+:\w+)>\w+: /', 'Session X: ', $m
+                       );
+               } );
+               $manager->setLogger( $logger );
+               $request = new \FauxRequest();
+
+               // TestingAccessWrapper can't handle methods with reference arguments, sigh.
+               $rClass = new \ReflectionClass( $manager );
+               $rMethod = $rClass->getMethod( 'loadSessionInfoFromStore' );
+               $rMethod->setAccessible( true );
+               $loadSessionInfoFromStore = function ( &$info ) use ( $rMethod, $manager, $request ) {
+                       return $rMethod->invokeArgs( $manager, array( &$info, $request ) );
+               };
+
+               $userInfo = UserInfo::newFromName( 'UTSysop', true );
+               $unverifiedUserInfo = UserInfo::newFromName( 'UTSysop', false );
+
+               $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+               $metadata = array(
+                       'userId' => $userInfo->getId(),
+                       'userName' => $userInfo->getName(),
+                       'userToken' => $userInfo->getToken( true ),
+                       'provider' => 'Mock',
+               );
+
+               $builder = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->setMethods( array( '__toString', 'mergeMetadata', 'refreshSessionInfo' ) );
+
+               $provider = $builder->getMockForAbstractClass();
+               $provider->setManager( $manager );
+               $provider->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( true ) );
+               $provider->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( true ) );
+               $provider->expects( $this->any() )->method( 'refreshSessionInfo' )
+                       ->will( $this->returnValue( true ) );
+               $provider->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Mock' ) );
+               $provider->expects( $this->any() )->method( 'mergeMetadata' )
+                       ->will( $this->returnCallback( function ( $a, $b ) {
+                               if ( $b === array( 'Throw' ) ) {
+                                       throw new \UnexpectedValueException( 'no merge!' );
+                               }
+                               return array( 'Merged' );
+                       } ) );
+
+               $provider2 = $builder->getMockForAbstractClass();
+               $provider2->setManager( $manager );
+               $provider2->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( false ) );
+               $provider2->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( false ) );
+               $provider2->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Mock2' ) );
+               $provider2->expects( $this->any() )->method( 'refreshSessionInfo' )
+                       ->will( $this->returnCallback( function ( $info, $request, &$metadata ) {
+                               $metadata['changed'] = true;
+                               return true;
+                       } ) );
+
+               $provider3 = $builder->getMockForAbstractClass();
+               $provider3->setManager( $manager );
+               $provider3->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( true ) );
+               $provider3->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( true ) );
+               $provider3->expects( $this->once() )->method( 'refreshSessionInfo' )
+                       ->will( $this->returnValue( false ) );
+               $provider3->expects( $this->any() )->method( '__toString' )
+                       ->will( $this->returnValue( 'Mock3' ) );
+
+               \TestingAccessWrapper::newFromObject( $manager )->sessionProviders = array(
+                       (string)$provider => $provider,
+                       (string)$provider2 => $provider2,
+                       (string)$provider3 => $provider3,
+               );
+
+               // No metadata, basic usage
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertFalse( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertTrue( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Unverified user, no metadata
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $unverifiedUserInfo
+               ) );
+               $this->assertSame( $unverifiedUserInfo, $info->getUserInfo() );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Unverified user provided and no metadata to auth it' )
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // No metadata, missing data
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Null provider and no metadata' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\UserInfo', $info->getUserInfo() );
+               $this->assertTrue( $info->getUserInfo()->isVerified() );
+               $this->assertTrue( $info->getUserInfo()->isAnon() );
+               $this->assertFalse( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::INFO, 'Session X: No user provided and provider cannot set user' )
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Incomplete/bad metadata
+               $this->store->setRawSession( $id, true );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Bad data' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $this->store->setRawSession( $id, array( 'data' => array() ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $this->store->deleteSession( $id );
+               $this->store->setRawSession( $id, array( 'metadata' => $metadata ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $this->store->setRawSession( $id, array( 'metadata' => $metadata, 'data' => true ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $this->store->setRawSession( $id, array( 'metadata' => true, 'data' => array() ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Bad data structure' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               foreach ( $metadata as $key => $dummy ) {
+                       $tmp = $metadata;
+                       unset( $tmp[$key] );
+                       $this->store->setRawSession( $id, array( 'metadata' => $tmp, 'data' => array() ) );
+                       $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+                       $this->assertSame( array(
+                               array( LogLevel::WARNING, 'Session X: Bad metadata' ),
+                       ), $logger->getBuffer() );
+                       $logger->clearBuffer();
+               }
+
+               // Basic usage with metadata
+               $this->store->setRawSession( $id, array( 'metadata' => $metadata, 'data' => array() ) );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Mismatched provider
+               $this->store->setSessionMeta( $id, array( 'provider' => 'Bad' ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Wrong provider, Bad !== Mock' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Unknown provider
+               $this->store->setSessionMeta( $id, array( 'provider' => 'Bad' ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Unknown provider, Bad' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Fill in provider
+               $this->store->setSessionMeta( $id, $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Bad user metadata
+               $this->store->setSessionMeta( $id, array( 'userId' => -1, 'userToken' => null ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::ERROR, 'Session X: Invalid ID' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => 0, 'userName' => '<X>', 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::ERROR, 'Session X: Invalid user name' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Mismatched user by ID
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => $userInfo->getId() + 1, 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: User ID mismatch, 2 !== 1' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Mismatched user by name
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => 0, 'userName' => 'X', 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: User name mismatch, X !== UTSysop' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // ID matches, name doesn't
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => $userInfo->getId(), 'userName' => 'X', 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array(
+                               LogLevel::WARNING, 'Session X: User ID matched but name didn\'t (rename?), X !== UTSysop'
+                       ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Mismatched anon user
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => 0, 'userName' => null, 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array(
+                               LogLevel::WARNING, 'Session X: Metadata has an anonymous user, but a non-anon user was provided'
+                       ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Lookup user by ID
+               $this->store->setSessionMeta( $id, array( 'userToken' => null ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( $userInfo->getId(), $info->getUserInfo()->getId() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Lookup user by name
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => 0, 'userName' => 'UTSysop', 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( $userInfo->getId(), $info->getUserInfo()->getId() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Lookup anonymous user
+               $this->store->setSessionMeta(
+                       $id, array( 'userId' => 0, 'userName' => null, 'userToken' => null ) + $metadata
+               );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->getUserInfo()->isAnon() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Unverified user with metadata
+               $this->store->setSessionMeta( $id, $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $unverifiedUserInfo
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->getUserInfo()->isVerified() );
+               $this->assertSame( $unverifiedUserInfo->getId(), $info->getUserInfo()->getId() );
+               $this->assertSame( $unverifiedUserInfo->getName(), $info->getUserInfo()->getName() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Unverified user with metadata
+               $this->store->setSessionMeta( $id, $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $unverifiedUserInfo
+               ) );
+               $this->assertFalse( $info->isIdSafe(), 'sanity check' );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->getUserInfo()->isVerified() );
+               $this->assertSame( $unverifiedUserInfo->getId(), $info->getUserInfo()->getId() );
+               $this->assertSame( $unverifiedUserInfo->getName(), $info->getUserInfo()->getName() );
+               $this->assertTrue( $info->isIdSafe() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Wrong token
+               $this->store->setSessionMeta( $id, array( 'userToken' => 'Bad' ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: User token mismatch' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Provider metadata
+               $this->store->setSessionMeta( $id, array( 'provider' => 'Mock2' ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider2,
+                       'id' => $id,
+                       'userInfo' => $userInfo,
+                       'metadata' => array( 'Info' ),
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array( 'Info', 'changed' => true ), $info->getProviderMetadata() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $this->store->setSessionMeta( $id, array( 'providerMetadata' => array( 'Saved' ) ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo,
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array( 'Saved' ), $info->getProviderMetadata() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo,
+                       'metadata' => array( 'Info' ),
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array( 'Merged' ), $info->getProviderMetadata() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo,
+                       'metadata' => array( 'Throw' ),
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Metadata merge failed: no merge!' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+
+               // Remember from session
+               $this->store->setSessionMeta( $id, $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertFalse( $info->wasRemembered() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $this->store->setSessionMeta( $id, array( 'remember' => true ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->wasRemembered() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $this->store->setSessionMeta( $id, array( 'remember' => false ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->wasRemembered() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // forceHTTPS from session
+               $this->store->setSessionMeta( $id, $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertFalse( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $this->store->setSessionMeta( $id, array( 'forceHTTPS' => true ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               $this->store->setSessionMeta( $id, array( 'forceHTTPS' => false ) + $metadata );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo,
+                       'forceHTTPS' => true
+               ) );
+               $this->assertTrue( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $info->forceHTTPS() );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Provider refreshSessionInfo() returning false
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider3,
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertSame( array(), $logger->getBuffer() );
+
+               // Hook
+               $that = $this;
+               $called = false;
+               $data = array( 'foo' => 1 );
+               $this->store->setSession( $id, array( 'metadata' => $metadata, 'data' => $data ) );
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $provider,
+                       'id' => $id,
+                       'userInfo' => $userInfo
+               ) );
+               $this->mergeMwGlobalArrayValue( 'wgHooks', array(
+                       'SessionCheckInfo' => array( function ( &$reason, $i, $r, $m, $d ) use (
+                               $that, $info, $metadata, $data, $request, &$called
+                       ) {
+                               $that->assertSame( $info->getId(), $i->getId() );
+                               $that->assertSame( $info->getProvider(), $i->getProvider() );
+                               $that->assertSame( $info->getUserInfo(), $i->getUserInfo() );
+                               $that->assertSame( $request, $r );
+                               $that->assertEquals( $metadata, $m );
+                               $that->assertEquals( $data, $d );
+                               $called = true;
+                               return false;
+                       } )
+               ) );
+               $this->assertFalse( $loadSessionInfoFromStore( $info ) );
+               $this->assertTrue( $called );
+               $this->assertSame( array(
+                       array( LogLevel::WARNING, 'Session X: Hook aborted' ),
+               ), $logger->getBuffer() );
+               $logger->clearBuffer();
+       }
+
+}
diff --git a/tests/phpunit/includes/session/SessionProviderTest.php b/tests/phpunit/includes/session/SessionProviderTest.php
new file mode 100644 (file)
index 0000000..d7aebcd
--- /dev/null
@@ -0,0 +1,195 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\SessionProvider
+ */
+class SessionProviderTest extends MediaWikiTestCase {
+
+       public function testBasics() {
+               $manager = new SessionManager();
+               $logger = new \TestLogger();
+               $config = new \HashConfig();
+
+               $provider = $this->getMockForAbstractClass( 'MediaWiki\\Session\\SessionProvider' );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+
+               $provider->setConfig( $config );
+               $this->assertSame( $config, $priv->config );
+               $provider->setLogger( $logger );
+               $this->assertSame( $logger, $priv->logger );
+               $provider->setManager( $manager );
+               $this->assertSame( $manager, $priv->manager );
+               $this->assertSame( $manager, $provider->getManager() );
+
+               $this->assertSame( array(), $provider->getVaryHeaders() );
+               $this->assertSame( array(), $provider->getVaryCookies() );
+               $this->assertSame( null, $provider->suggestLoginUsername( new \FauxRequest ) );
+
+               $this->assertSame( get_class( $provider ), (string)$provider );
+
+               $this->assertNull( $provider->whyNoSession() );
+
+               $info = new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
+                       'provider' => $provider,
+               ) );
+               $metadata = array( 'foo' );
+               $this->assertTrue( $provider->refreshSessionInfo( $info, new \FauxRequest, $metadata ) );
+               $this->assertSame( array( 'foo' ), $metadata );
+       }
+
+       /**
+        * @dataProvider provideNewSessionInfo
+        * @param bool $persistId Return value for ->persistsSessionId()
+        * @param bool $persistUser Return value for ->persistsSessionUser()
+        * @param bool $ok Whether a SessionInfo is provided
+        */
+       public function testNewSessionInfo( $persistId, $persistUser, $ok ) {
+               $manager = new SessionManager();
+
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->setMethods( array( 'canChangeUser', 'persistsSessionId' ) )
+                       ->getMockForAbstractClass();
+               $provider->expects( $this->any() )->method( 'persistsSessionId' )
+                       ->will( $this->returnValue( $persistId ) );
+               $provider->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( $persistUser ) );
+               $provider->setManager( $manager );
+
+               if ( $ok ) {
+                       $info = $provider->newSessionInfo();
+                       $this->assertNotNull( $info );
+                       $this->assertFalse( $info->wasPersisted() );
+                       $this->assertTrue( $info->isIdSafe() );
+
+                       $id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+                       $info = $provider->newSessionInfo( $id );
+                       $this->assertNotNull( $info );
+                       $this->assertSame( $id, $info->getId() );
+                       $this->assertFalse( $info->wasPersisted() );
+                       $this->assertTrue( $info->isIdSafe() );
+               } else {
+                       $this->assertNull( $provider->newSessionInfo() );
+               }
+       }
+
+       public function testMergeMetadata() {
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->getMockForAbstractClass();
+
+               try {
+                       $provider->mergeMetadata(
+                               array( 'foo' => 1, 'baz' => 3 ),
+                               array( 'bar' => 2, 'baz' => '3' )
+                       );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame( 'Key "baz" changed', $ex->getMessage() );
+               }
+
+               $res = $provider->mergeMetadata(
+                       array( 'foo' => 1, 'baz' => 3 ),
+                       array( 'bar' => 2, 'baz' => 3 )
+               );
+               $this->assertSame( array( 'bar' => 2, 'baz' => 3 ), $res );
+       }
+
+       public static function provideNewSessionInfo() {
+               return array(
+                       array( false, false, false ),
+                       array( true, false, false ),
+                       array( false, true, false ),
+                       array( true, true, true ),
+               );
+       }
+
+       public function testImmutableSessions() {
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->setMethods( array( 'canChangeUser', 'persistsSessionId' ) )
+                       ->getMockForAbstractClass();
+               $provider->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( true ) );
+               $provider->preventSessionsForUser( 'Foo' );
+
+               $provider = $this->getMockBuilder( 'MediaWiki\\Session\\SessionProvider' )
+                       ->setMethods( array( 'canChangeUser', 'persistsSessionId' ) )
+                       ->getMockForAbstractClass();
+               $provider->expects( $this->any() )->method( 'canChangeUser' )
+                       ->will( $this->returnValue( false ) );
+               try {
+                       $provider->preventSessionsForUser( 'Foo' );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \BadMethodCallException $ex ) {
+               }
+
+       }
+
+       public function testHashToSessionId() {
+               $config = new \HashConfig( array(
+                       'SecretKey' => 'Shhh!',
+               ) );
+
+               $provider = $this->getMockForAbstractClass( 'MediaWiki\\Session\\SessionProvider',
+                       array(), 'MockSessionProvider' );
+               $provider->setConfig( $config );
+               $priv = \TestingAccessWrapper::newFromObject( $provider );
+
+               $this->assertSame( 'eoq8cb1mg7j30ui5qolafps4hg29k5bb', $priv->hashToSessionId( 'foobar' ) );
+               $this->assertSame( '4do8j7tfld1g8tte9jqp3csfgmulaun9',
+                       $priv->hashToSessionId( 'foobar', 'secret' ) );
+
+               try {
+                       $priv->hashToSessionId( array() );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               '$data must be a string, array was passed',
+                               $ex->getMessage()
+                       );
+               }
+               try {
+                       $priv->hashToSessionId( '', false );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               '$key must be a string or null, boolean was passed',
+                               $ex->getMessage()
+                       );
+               }
+       }
+
+       public function testDescribe() {
+               $provider = $this->getMockForAbstractClass( 'MediaWiki\\Session\\SessionProvider',
+                       array(), 'MockSessionProvider' );
+
+               $this->assertSame(
+                       'MockSessionProvider sessions',
+                       $provider->describe( \Language::factory( 'en' ) )
+               );
+       }
+
+       public function testGetAllowedUserRights() {
+               $provider = $this->getMockForAbstractClass( 'MediaWiki\\Session\\SessionProvider' );
+               $backend = TestUtils::getDummySessionBackend();
+
+               try {
+                       $provider->getAllowedUserRights( $backend );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame(
+                               'Backend\'s provider isn\'t $this',
+                               $ex->getMessage()
+                       );
+               }
+
+               \TestingAccessWrapper::newFromObject( $backend )->provider = $provider;
+               $this->assertNull( $provider->getAllowedUserRights( $backend ) );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/SessionTest.php b/tests/phpunit/includes/session/SessionTest.php
new file mode 100644 (file)
index 0000000..efc92f7
--- /dev/null
@@ -0,0 +1,202 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @covers MediaWiki\Session\Session
+ */
+class SessionTest extends MediaWikiTestCase {
+
+       public function testConstructor() {
+               $backend = TestUtils::getDummySessionBackend();
+               \TestingAccessWrapper::newFromObject( $backend )->requests = array( -1 => 'dummy' );
+               \TestingAccessWrapper::newFromObject( $backend )->id = new SessionId( 'abc' );
+
+               $session = new Session( $backend, 42 );
+               $priv = \TestingAccessWrapper::newFromObject( $session );
+               $this->assertSame( $backend, $priv->backend );
+               $this->assertSame( 42, $priv->index );
+
+               $request = new \FauxRequest();
+               $priv2 = \TestingAccessWrapper::newFromObject( $session->sessionWithRequest( $request ) );
+               $this->assertSame( $backend, $priv2->backend );
+               $this->assertNotSame( $priv->index, $priv2->index );
+               $this->assertSame( $request, $priv2->getRequest() );
+       }
+
+       /**
+        * @dataProvider provideMethods
+        * @param string $m Method to test
+        * @param array $args Arguments to pass to the method
+        * @param bool $index Whether the backend method gets passed the index
+        * @param bool $ret Whether the method returns a value
+        */
+       public function testMethods( $m, $args, $index, $ret ) {
+               $mock = $this->getMock( 'MediaWiki\\Session\\DummySessionBackend',
+                       array( $m, 'deregisterSession' ) );
+               $mock->expects( $this->once() )->method( 'deregisterSession' )
+                       ->with( $this->identicalTo( 42 ) );
+
+               $tmp = $mock->expects( $this->once() )->method( $m );
+               $expectArgs = array();
+               if ( $index ) {
+                       $expectArgs[] = $this->identicalTo( 42 );
+               }
+               foreach ( $args as $arg ) {
+                       $expectArgs[] = $this->identicalTo( $arg );
+               }
+               $tmp = call_user_func_array( array( $tmp, 'with' ), $expectArgs );
+
+               $retval = new \stdClass;
+               $tmp->will( $this->returnValue( $retval ) );
+
+               $session = TestUtils::getDummySession( $mock, 42 );
+
+               if ( $ret ) {
+                       $this->assertSame( $retval, call_user_func_array( array( $session, $m ), $args ) );
+               } else {
+                       $this->assertNull( call_user_func_array( array( $session, $m ), $args ) );
+               }
+
+               // Trigger Session destructor
+               $session = null;
+       }
+
+       public static function provideMethods() {
+               return array(
+                       array( 'getId', array(), false, true ),
+                       array( 'getSessionId', array(), false, true ),
+                       array( 'resetId', array(), false, true ),
+                       array( 'getProvider', array(), false, true ),
+                       array( 'isPersistent', array(), false, true ),
+                       array( 'persist', array(), false, false ),
+                       array( 'shouldRememberUser', array(), false, true ),
+                       array( 'setRememberUser', array( true ), false, false ),
+                       array( 'getRequest', array(), true, true ),
+                       array( 'getUser', array(), false, true ),
+                       array( 'getAllowedUserRights', array(), false, true ),
+                       array( 'canSetUser', array(), false, true ),
+                       array( 'setUser', array( new \stdClass ), false, false ),
+                       array( 'suggestLoginUsername', array(), true, true ),
+                       array( 'shouldForceHTTPS', array(), false, true ),
+                       array( 'setForceHTTPS', array( true ), false, false ),
+                       array( 'getLoggedOutTimestamp', array(), false, true ),
+                       array( 'setLoggedOutTimestamp', array( 123 ), false, false ),
+                       array( 'getProviderMetadata', array(), false, true ),
+                       array( 'save', array(), false, false ),
+                       array( 'delaySave', array(), false, true ),
+                       array( 'renew', array(), false, false ),
+               );
+       }
+
+       public function testDataAccess() {
+               $session = TestUtils::getDummySession();
+               $backend = \TestingAccessWrapper::newFromObject( $session )->backend;
+
+               $this->assertEquals( 1, $session->get( 'foo' ) );
+               $this->assertEquals( 'zero', $session->get( 0 ) );
+               $this->assertFalse( $backend->dirty );
+
+               $this->assertEquals( null, $session->get( 'null' ) );
+               $this->assertEquals( 'default', $session->get( 'null', 'default' ) );
+               $this->assertFalse( $backend->dirty );
+
+               $session->set( 'foo', 55 );
+               $this->assertEquals( 55, $backend->data['foo'] );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+
+               $session->set( 1, 'one' );
+               $this->assertEquals( 'one', $backend->data[1] );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+
+               $session->set( 1, 'one' );
+               $this->assertFalse( $backend->dirty );
+
+               $this->assertTrue( $session->exists( 'foo' ) );
+               $this->assertTrue( $session->exists( 1 ) );
+               $this->assertFalse( $session->exists( 'null' ) );
+               $this->assertFalse( $session->exists( 100 ) );
+               $this->assertFalse( $backend->dirty );
+
+               $session->remove( 'foo' );
+               $this->assertArrayNotHasKey( 'foo', $backend->data );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+               $session->remove( 1 );
+               $this->assertArrayNotHasKey( 1, $backend->data );
+               $this->assertTrue( $backend->dirty );
+               $backend->dirty = false;
+
+               $session->remove( 101 );
+               $this->assertFalse( $backend->dirty );
+
+               $backend->data = array( 'a', 'b', '?' => 'c' );
+               $this->assertSame( 3, $session->count() );
+               $this->assertSame( 3, count( $session ) );
+               $this->assertFalse( $backend->dirty );
+
+               $data = array();
+               foreach ( $session as $key => $value ) {
+                       $data[$key] = $value;
+               }
+               $this->assertEquals( $backend->data, $data );
+               $this->assertFalse( $backend->dirty );
+
+               $this->assertEquals( $backend->data, iterator_to_array( $session ) );
+               $this->assertFalse( $backend->dirty );
+       }
+
+       public function testClear() {
+               $session = TestUtils::getDummySession();
+               $priv = \TestingAccessWrapper::newFromObject( $session );
+
+               $backend = $this->getMock(
+                       'MediaWiki\\Session\\DummySessionBackend', array( 'canSetUser', 'setUser', 'save' )
+               );
+               $backend->expects( $this->once() )->method( 'canSetUser' )
+                       ->will( $this->returnValue( true ) );
+               $backend->expects( $this->once() )->method( 'setUser' )
+                       ->with( $this->callback( function ( $user ) {
+                               return $user instanceof User && $user->isAnon();
+                       } ) );
+               $backend->expects( $this->once() )->method( 'save' );
+               $priv->backend = $backend;
+               $session->clear();
+               $this->assertSame( array(), $backend->data );
+               $this->assertTrue( $backend->dirty );
+
+               $backend = $this->getMock(
+                       'MediaWiki\\Session\\DummySessionBackend', array( 'canSetUser', 'setUser', 'save' )
+               );
+               $backend->data = array();
+               $backend->expects( $this->once() )->method( 'canSetUser' )
+                       ->will( $this->returnValue( true ) );
+               $backend->expects( $this->once() )->method( 'setUser' )
+                       ->with( $this->callback( function ( $user ) {
+                               return $user instanceof User && $user->isAnon();
+                       } ) );
+               $backend->expects( $this->once() )->method( 'save' );
+               $priv->backend = $backend;
+               $session->clear();
+               $this->assertFalse( $backend->dirty );
+
+               $backend = $this->getMock(
+                       'MediaWiki\\Session\\DummySessionBackend', array( 'canSetUser', 'setUser', 'save' )
+               );
+               $backend->expects( $this->once() )->method( 'canSetUser' )
+                       ->will( $this->returnValue( false ) );
+               $backend->expects( $this->never() )->method( 'setUser' );
+               $backend->expects( $this->once() )->method( 'save' );
+               $priv->backend = $backend;
+               $session->clear();
+               $this->assertSame( array(), $backend->data );
+               $this->assertTrue( $backend->dirty );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/TestBagOStuff.php b/tests/phpunit/includes/session/TestBagOStuff.php
new file mode 100644 (file)
index 0000000..e674e7b
--- /dev/null
@@ -0,0 +1,78 @@
+<?php
+
+namespace MediaWiki\Session;
+
+/**
+ * BagOStuff with utility functions for MediaWiki\\Session\\* testing
+ */
+class TestBagOStuff extends \HashBagOStuff {
+
+       /**
+        * @param string $id Session ID
+        * @param array $data Session data
+        * @param int $expiry Expiry
+        * @param User $user User for metadata
+        */
+       public function setSessionData( $id, array $data, $expiry = 0, User $user = null ) {
+               $this->setSession( $id, array( 'data' => $data ), $expiry, $user );
+       }
+
+       /**
+        * @param string $id Session ID
+        * @param array $metadata Session metadata
+        * @param int $expiry Expiry
+        */
+       public function setSessionMeta( $id, array $metadata, $expiry = 0 ) {
+               $this->setSession( $id, array( 'metadata' => $metadata ), $expiry );
+       }
+
+       /**
+        * @param string $id Session ID
+        * @param array $blob Session metadata and data
+        * @param int $expiry Expiry
+        * @param User $user User for metadata
+        */
+       public function setSession( $id, array $blob, $expiry = 0, User $user = null ) {
+               $blob += array(
+                       'data' => array(),
+                       'metadata' => array(),
+               );
+               $blob['metadata'] += array(
+                       'userId' => $user ? $user->getId() : 0,
+                       'userName' => $user ? $user->getName() : null,
+                       'userToken' => $user ? $user->getToken( true ) : null,
+                       'provider' => 'DummySessionProvider',
+               );
+
+               $this->setRawSession( $id, $blob, $expiry, $user );
+       }
+
+       /**
+        * @param string $id Session ID
+        * @param array|mixed $blob Session metadata and data
+        * @param int $expiry Expiry
+        */
+       public function setRawSession( $id, $blob, $expiry = 0 ) {
+               if ( $expiry <= 0 ) {
+                       $expiry = \RequestContext::getMain()->getConfig()->get( 'ObjectCacheSessionExpiry' );
+               }
+
+               $this->set( wfMemcKey( 'MWSession', $id ), $blob, $expiry );
+       }
+
+       /**
+        * @param string $id Session ID
+        * @return mixed
+        */
+       public function getSession( $id ) {
+               return $this->get( wfMemcKey( 'MWSession', $id ) );
+       }
+
+       /**
+        * @param string $id Session ID
+        */
+       public function deleteSession( $id ) {
+               $this->delete( wfMemcKey( 'MWSession', $id ) );
+       }
+
+}
diff --git a/tests/phpunit/includes/session/TestUtils.php b/tests/phpunit/includes/session/TestUtils.php
new file mode 100644 (file)
index 0000000..1619983
--- /dev/null
@@ -0,0 +1,99 @@
+<?php
+
+namespace MediaWiki\Session;
+
+/**
+ * Utility functions for Session unit tests
+ */
+class TestUtils {
+
+       /**
+        * Override the singleton for unit testing
+        * @param SessionManager|null $manager
+        * @return \\ScopedCallback|null
+        */
+       public static function setSessionManagerSingleton( SessionManager $manager = null ) {
+               session_write_close();
+
+               $rInstance = new \ReflectionProperty(
+                       'MediaWiki\\Session\\SessionManager', 'instance'
+               );
+               $rInstance->setAccessible( true );
+               $rGlobalSession = new \ReflectionProperty(
+                       'MediaWiki\\Session\\SessionManager', 'globalSession'
+               );
+               $rGlobalSession->setAccessible( true );
+               $rGlobalSessionRequest = new \ReflectionProperty(
+                       'MediaWiki\\Session\\SessionManager', 'globalSessionRequest'
+               );
+               $rGlobalSessionRequest->setAccessible( true );
+
+               $oldInstance = $rInstance->getValue();
+
+               $reset = array(
+                       array( $rInstance, $oldInstance ),
+                       array( $rGlobalSession, $rGlobalSession->getValue() ),
+                       array( $rGlobalSessionRequest, $rGlobalSessionRequest->getValue() ),
+               );
+
+               $rInstance->setValue( $manager );
+               $rGlobalSession->setValue( null );
+               $rGlobalSessionRequest->setValue( null );
+               if ( $manager && PHPSessionHandler::isInstalled() ) {
+                       PHPSessionHandler::install( $manager );
+               }
+
+               return new \ScopedCallback( function () use ( &$reset, $oldInstance ) {
+                       foreach ( $reset as &$arr ) {
+                               $arr[0]->setValue( $arr[1] );
+                       }
+                       if ( $oldInstance && PHPSessionHandler::isInstalled() ) {
+                               PHPSessionHandler::install( $oldInstance );
+                       }
+               } );
+       }
+
+       /**
+        * If you need a SessionBackend for testing but don't want to create a real
+        * one, use this.
+        * @return SessionBackend Unconfigured! Use reflection to set any private
+        *  fields necessary.
+        */
+       public static function getDummySessionBackend() {
+               $rc = new \ReflectionClass( 'MediaWiki\\Session\\SessionBackend' );
+               if ( !method_exists( $rc, 'newInstanceWithoutConstructor' ) ) {
+                       \PHPUnit_Framework_Assert::markTestSkipped(
+                               'ReflectionClass::newInstanceWithoutConstructor isn\'t available'
+                       );
+               }
+
+               return $rc->newInstanceWithoutConstructor();
+       }
+
+       /**
+        * If you need a Session for testing but don't want to create a backend to
+        * construct one, use this.
+        * @param object $backend Object to serve as the SessionBackend
+        * @param int $index Index
+        * @return Session
+        */
+       public static function getDummySession( $backend = null, $index = -1 ) {
+               $rc = new \ReflectionClass( 'MediaWiki\\Session\\Session' );
+               if ( !method_exists( $rc, 'newInstanceWithoutConstructor' ) ) {
+                       \PHPUnit_Framework_Assert::markTestSkipped(
+                               'ReflectionClass::newInstanceWithoutConstructor isn\'t available'
+                       );
+               }
+
+               if ( $backend === null ) {
+                       $backend = new DummySessionBackend;
+               }
+
+               $session = $rc->newInstanceWithoutConstructor();
+               $priv = \TestingAccessWrapper::newFromObject( $session );
+               $priv->backend = $backend;
+               $priv->index = $index;
+               return $session;
+       }
+
+}
diff --git a/tests/phpunit/includes/session/UserInfoTest.php b/tests/phpunit/includes/session/UserInfoTest.php
new file mode 100644 (file)
index 0000000..121bb72
--- /dev/null
@@ -0,0 +1,186 @@
+<?php
+
+namespace MediaWiki\Session;
+
+use MediaWikiTestCase;
+use User;
+
+/**
+ * @group Session
+ * @group Database
+ * @covers MediaWiki\Session\UserInfo
+ */
+class UserInfoTest extends MediaWikiTestCase {
+
+       public function testNewAnonymous() {
+               $userinfo = UserInfo::newAnonymous();
+
+               $this->assertTrue( $userinfo->isAnon() );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( 0, $userinfo->getId() );
+               $this->assertSame( null, $userinfo->getName() );
+               $this->assertSame( null, $userinfo->getToken() );
+               $this->assertNotNull( $userinfo->getUser() );
+               $this->assertSame( $userinfo, $userinfo->verified() );
+               $this->assertSame( '<anon>', (string)$userinfo );
+       }
+
+       public function testNewFromId() {
+               $id = wfGetDB( DB_MASTER )->selectField( 'user', 'MAX(user_id)' ) + 1;
+               try {
+                       UserInfo::newFromId( $id );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid ID', $ex->getMessage() );
+               }
+
+               $user = User::newFromName( 'UTSysop' );
+               $userinfo = UserInfo::newFromId( $user->getId() );
+               $this->assertFalse( $userinfo->isAnon() );
+               $this->assertFalse( $userinfo->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo->getId() );
+               $this->assertSame( $user->getName(), $userinfo->getName() );
+               $this->assertSame( $user->getToken( true ), $userinfo->getToken() );
+               $this->assertInstanceOf( 'User', $userinfo->getUser() );
+               $userinfo2 = $userinfo->verified();
+               $this->assertNotSame( $userinfo2, $userinfo );
+               $this->assertSame( "<-:{$user->getId()}:{$user->getName()}>", (string)$userinfo );
+
+               $this->assertFalse( $userinfo2->isAnon() );
+               $this->assertTrue( $userinfo2->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo2->getId() );
+               $this->assertSame( $user->getName(), $userinfo2->getName() );
+               $this->assertSame( $user->getToken( true ), $userinfo2->getToken() );
+               $this->assertInstanceOf( 'User', $userinfo2->getUser() );
+               $this->assertSame( $userinfo2, $userinfo2->verified() );
+               $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
+
+               $userinfo = UserInfo::newFromId( $user->getId(), true );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( $userinfo, $userinfo->verified() );
+       }
+
+       public function testNewFromName() {
+               try {
+                       UserInfo::newFromName( '<bad name>' );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \InvalidArgumentException $ex ) {
+                       $this->assertSame( 'Invalid user name', $ex->getMessage() );
+               }
+
+               // User name that exists
+               $user = User::newFromName( 'UTSysop' );
+               $userinfo = UserInfo::newFromName( $user->getName() );
+               $this->assertFalse( $userinfo->isAnon() );
+               $this->assertFalse( $userinfo->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo->getId() );
+               $this->assertSame( $user->getName(), $userinfo->getName() );
+               $this->assertSame( $user->getToken( true ), $userinfo->getToken() );
+               $this->assertInstanceOf( 'User', $userinfo->getUser() );
+               $userinfo2 = $userinfo->verified();
+               $this->assertNotSame( $userinfo2, $userinfo );
+               $this->assertSame( "<-:{$user->getId()}:{$user->getName()}>", (string)$userinfo );
+
+               $this->assertFalse( $userinfo2->isAnon() );
+               $this->assertTrue( $userinfo2->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo2->getId() );
+               $this->assertSame( $user->getName(), $userinfo2->getName() );
+               $this->assertSame( $user->getToken( true ), $userinfo2->getToken() );
+               $this->assertInstanceOf( 'User', $userinfo2->getUser() );
+               $this->assertSame( $userinfo2, $userinfo2->verified() );
+               $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
+
+               $userinfo = UserInfo::newFromName( $user->getName(), true );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( $userinfo, $userinfo->verified() );
+
+               // User name that does not exist should still be non-anon
+               $user = User::newFromName( 'DoesNotExist' );
+               $this->assertSame( 0, $user->getId(), 'sanity check' );
+               $userinfo = UserInfo::newFromName( $user->getName() );
+               $this->assertFalse( $userinfo->isAnon() );
+               $this->assertFalse( $userinfo->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo->getId() );
+               $this->assertSame( $user->getName(), $userinfo->getName() );
+               $this->assertSame( null, $userinfo->getToken() );
+               $this->assertInstanceOf( 'User', $userinfo->getUser() );
+               $userinfo2 = $userinfo->verified();
+               $this->assertNotSame( $userinfo2, $userinfo );
+               $this->assertSame( "<-:{$user->getId()}:{$user->getName()}>", (string)$userinfo );
+
+               $this->assertFalse( $userinfo2->isAnon() );
+               $this->assertTrue( $userinfo2->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo2->getId() );
+               $this->assertSame( $user->getName(), $userinfo2->getName() );
+               $this->assertSame( null, $userinfo2->getToken() );
+               $this->assertInstanceOf( 'User', $userinfo2->getUser() );
+               $this->assertSame( $userinfo2, $userinfo2->verified() );
+               $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
+
+               $userinfo = UserInfo::newFromName( $user->getName(), true );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( $userinfo, $userinfo->verified() );
+       }
+
+       public function testNewFromUser() {
+               // User that exists
+               $user = User::newFromName( 'UTSysop' );
+               $userinfo = UserInfo::newFromUser( $user );
+               $this->assertFalse( $userinfo->isAnon() );
+               $this->assertFalse( $userinfo->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo->getId() );
+               $this->assertSame( $user->getName(), $userinfo->getName() );
+               $this->assertSame( $user->getToken( true ), $userinfo->getToken() );
+               $this->assertSame( $user, $userinfo->getUser() );
+               $userinfo2 = $userinfo->verified();
+               $this->assertNotSame( $userinfo2, $userinfo );
+               $this->assertSame( "<-:{$user->getId()}:{$user->getName()}>", (string)$userinfo );
+
+               $this->assertFalse( $userinfo2->isAnon() );
+               $this->assertTrue( $userinfo2->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo2->getId() );
+               $this->assertSame( $user->getName(), $userinfo2->getName() );
+               $this->assertSame( $user->getToken( true ), $userinfo2->getToken() );
+               $this->assertSame( $user, $userinfo2->getUser() );
+               $this->assertSame( $userinfo2, $userinfo2->verified() );
+               $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
+
+               $userinfo = UserInfo::newFromUser( $user, true );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( $userinfo, $userinfo->verified() );
+
+               // User name that does not exist should still be non-anon
+               $user = User::newFromName( 'DoesNotExist' );
+               $this->assertSame( 0, $user->getId(), 'sanity check' );
+               $userinfo = UserInfo::newFromUser( $user );
+               $this->assertFalse( $userinfo->isAnon() );
+               $this->assertFalse( $userinfo->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo->getId() );
+               $this->assertSame( $user->getName(), $userinfo->getName() );
+               $this->assertSame( null, $userinfo->getToken() );
+               $this->assertSame( $user, $userinfo->getUser() );
+               $userinfo2 = $userinfo->verified();
+               $this->assertNotSame( $userinfo2, $userinfo );
+               $this->assertSame( "<-:{$user->getId()}:{$user->getName()}>", (string)$userinfo );
+
+               $this->assertFalse( $userinfo2->isAnon() );
+               $this->assertTrue( $userinfo2->isVerified() );
+               $this->assertSame( $user->getId(), $userinfo2->getId() );
+               $this->assertSame( $user->getName(), $userinfo2->getName() );
+               $this->assertSame( null, $userinfo2->getToken() );
+               $this->assertSame( $user, $userinfo2->getUser() );
+               $this->assertSame( $userinfo2, $userinfo2->verified() );
+               $this->assertSame( "<+:{$user->getId()}:{$user->getName()}>", (string)$userinfo2 );
+
+               $userinfo = UserInfo::newFromUser( $user, true );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( $userinfo, $userinfo->verified() );
+
+               // Anonymous user gives anon
+               $userinfo = UserInfo::newFromUser( new User, false );
+               $this->assertTrue( $userinfo->isVerified() );
+               $this->assertSame( 0, $userinfo->getId() );
+               $this->assertSame( null, $userinfo->getName() );
+       }
+
+}
diff --git a/tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php b/tests/phpunit/includes/site/MediaWikiPageNameNormalizerTest.php
new file mode 100644 (file)
index 0000000..163c52d
--- /dev/null
@@ -0,0 +1,85 @@
+<?php
+
+use MediaWiki\Site\MediaWikiPageNameNormalizer;
+
+/**
+ * @covers MediaWiki\Site\MediaWikiPageNameNormalizer
+ *
+ * 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.27
+ *
+ * @group Site
+ * @group medium
+ *
+ * @author Marius Hoch
+ */
+class MediaWikiPageNameNormalizerTest extends PHPUnit_Framework_TestCase {
+
+       protected function setUp() {
+               parent::setUp();
+
+               static $connectivity = null;
+
+               if ( $connectivity === null ) {
+                       // Check whether we have (reasonable fast) connectivity
+                       $res = Http::get(
+                               'https://www.wikidata.org/w/api.php?action=query&meta=siteinfo&format=json',
+                               array( 'timeout' => 3 ),
+                               __METHOD__
+                       );
+
+                       if ( $res === false || strpos( $res, '"sitename":"Wikidata"' ) === false ) {
+                               $connectivity = false;
+                       } else {
+                               $connectivity = true;
+                       }
+               }
+
+               if ( !$connectivity ) {
+                       $this->markTestSkipped( 'MediaWikiPageNameNormalizerTest needs internet connectivity.' );
+               }
+       }
+
+       /**
+        * @dataProvider normalizePageTitleProvider
+        */
+       public function testNormalizePageTitle( $expected, $pageName ) {
+               $normalizer = new MediaWikiPageNameNormalizer();
+
+               $this->assertSame(
+                       $expected,
+                       $normalizer->normalizePageName( $pageName, 'https://www.wikidata.org/w/api.php' )
+               );
+       }
+
+       public function normalizePageTitleProvider() {
+               // Note: This makes (very conservative) assumptions about pages on Wikidata
+               // existing or not.
+               return array(
+                       'universe (Q1)' => array(
+                               'Q1', 'Q1'
+                       ),
+                       'Q404 redirects to Q395' => array(
+                               'Q395', 'Q404'
+                       ),
+                       'there is no Q0' => array(
+                               false, 'Q0'
+                       )
+               );
+       }
+
+}
index 9ec1b46..90051ee 100644 (file)
@@ -125,7 +125,6 @@ class UploadBaseTest extends MediaWikiTestCase {
                );
        }
 
-
        /**
         * @dataProvider provideCheckSvgScriptCallback
         */
index b749662..428fd27 100644 (file)
@@ -16,7 +16,6 @@ class UploadFromUrlTest extends ApiTestCase {
                        'wgAllowCopyUploads' => true,
                        'wgAllowAsyncCopyUploads' => true,
                ) );
-               wfSetupSession();
 
                if ( wfLocalFile( 'UploadFromUrlTest.png' )->exists() ) {
                        $this->deleteFile( 'UploadFromUrlTest.png' );
@@ -26,15 +25,12 @@ class UploadFromUrlTest extends ApiTestCase {
        protected function doApiRequest( array $params, array $unused = null,
                $appendModule = false, User $user = null
        ) {
-               $sessionId = session_id();
-               session_write_close();
+               global $wgRequest;
 
-               $req = new FauxRequest( $params, true, $_SESSION );
+               $req = new FauxRequest( $params, true, $wgRequest->getSession() );
                $module = new ApiMain( $req, true );
                $module->execute();
 
-               wfSetupSession( $sessionId );
-
                return array(
                        $module->getResult()->getResultData( null, array( 'Strip' => 'all' ) ),
                        $req
diff --git a/tests/phpunit/includes/user/BotPasswordTest.php b/tests/phpunit/includes/user/BotPasswordTest.php
new file mode 100644 (file)
index 0000000..c118803
--- /dev/null
@@ -0,0 +1,379 @@
+<?php
+
+use MediaWiki\Session\SessionManager;
+
+/**
+ * @covers BotPassword
+ * @group Database
+ */
+class BotPasswordTest extends MediaWikiTestCase {
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( array(
+                       'wgEnableBotPasswords' => true,
+                       'wgBotPasswordsDatabase' => false,
+                       'wgCentralIdLookupProvider' => 'BotPasswordTest OkMock',
+                       'wgGrantPermissions' => array(
+                               'test' => array( 'read' => true ),
+                       ),
+                       'wgUserrightsInterwikiDelimiter' => '@',
+               ) );
+
+               $mock1 = $this->getMockForAbstractClass( 'CentralIdLookup' );
+               $mock1->expects( $this->any() )->method( 'isAttached' )
+                       ->will( $this->returnValue( true ) );
+               $mock1->expects( $this->any() )->method( 'lookupUserNames' )
+                       ->will( $this->returnValue( array( 'UTSysop' => 42, 'UTDummy' => 43, 'UTInvalid' => 0 ) ) );
+               $mock1->expects( $this->never() )->method( 'lookupCentralIds' );
+
+               $mock2 = $this->getMockForAbstractClass( 'CentralIdLookup' );
+               $mock2->expects( $this->any() )->method( 'isAttached' )
+                       ->will( $this->returnValue( false ) );
+               $mock2->expects( $this->any() )->method( 'lookupUserNames' )
+                       ->will( $this->returnArgument( 0 ) );
+               $mock2->expects( $this->never() )->method( 'lookupCentralIds' );
+
+               $this->mergeMwGlobalArrayValue( 'wgCentralIdLookupProviders', array(
+                       'BotPasswordTest OkMock' => array( 'factory' => function () use ( $mock1 ) {
+                               return $mock1;
+                       } ),
+                       'BotPasswordTest FailMock' => array( 'factory' => function () use ( $mock2 ) {
+                               return $mock2;
+                       } ),
+               ) );
+
+               CentralIdLookup::resetCache();
+       }
+
+       public function addDBData() {
+               $passwordFactory = new \PasswordFactory();
+               $passwordFactory->init( \RequestContext::getMain()->getConfig() );
+               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
+               $passwordFactory->setDefaultType( 'A' );
+               $pwhash = $passwordFactory->newFromPlaintext( 'foobaz' );
+
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->delete(
+                       'bot_passwords',
+                       array( 'bp_user' => array( 42, 43 ), 'bp_app_id' => 'BotPassword' ),
+                       __METHOD__
+               );
+               $dbw->insert(
+                       'bot_passwords',
+                       array(
+                               array(
+                                       'bp_user' => 42,
+                                       'bp_app_id' => 'BotPassword',
+                                       'bp_password' => $pwhash->toString(),
+                                       'bp_token' => 'token!',
+                                       'bp_restrictions' => '{"IPAddresses":["127.0.0.0/8"]}',
+                                       'bp_grants' => '["test"]',
+                               ),
+                               array(
+                                       'bp_user' => 43,
+                                       'bp_app_id' => 'BotPassword',
+                                       'bp_password' => $pwhash->toString(),
+                                       'bp_token' => 'token!',
+                                       'bp_restrictions' => '{"IPAddresses":["127.0.0.0/8"]}',
+                                       'bp_grants' => '["test"]',
+                               ),
+                       ),
+                       __METHOD__
+               );
+       }
+
+       public function testBasics() {
+               $user = User::newFromName( 'UTSysop' );
+               $bp = BotPassword::newFromUser( $user, 'BotPassword' );
+               $this->assertInstanceOf( 'BotPassword', $bp );
+               $this->assertTrue( $bp->isSaved() );
+               $this->assertSame( 42, $bp->getUserCentralId() );
+               $this->assertSame( 'BotPassword', $bp->getAppId() );
+               $this->assertSame( 'token!', trim( $bp->getToken(), " \0" ) );
+               $this->assertEquals( '{"IPAddresses":["127.0.0.0/8"]}', $bp->getRestrictions()->toJson() );
+               $this->assertSame( array( 'test' ), $bp->getGrants() );
+
+               $this->assertNull( BotPassword::newFromUser( $user, 'DoesNotExist' ) );
+
+               $this->setMwGlobals( array(
+                       'wgCentralIdLookupProvider' => 'BotPasswordTest FailMock'
+               ) );
+               $this->assertNull( BotPassword::newFromUser( $user, 'BotPassword' ) );
+
+               $this->assertSame( '@', BotPassword::getSeparator() );
+               $this->setMwGlobals( array(
+                       'wgUserrightsInterwikiDelimiter' => '#',
+               ) );
+               $this->assertSame( '#', BotPassword::getSeparator() );
+       }
+
+       public function testUnsaved() {
+               $user = User::newFromName( 'UTSysop' );
+               $bp = BotPassword::newUnsaved( array(
+                       'user' => $user,
+                       'appId' => 'DoesNotExist'
+               ) );
+               $this->assertInstanceOf( 'BotPassword', $bp );
+               $this->assertFalse( $bp->isSaved() );
+               $this->assertSame( 42, $bp->getUserCentralId() );
+               $this->assertSame( 'DoesNotExist', $bp->getAppId() );
+               $this->assertEquals( MWRestrictions::newDefault(), $bp->getRestrictions() );
+               $this->assertSame( array(), $bp->getGrants() );
+
+               $bp = BotPassword::newUnsaved( array(
+                       'username' => 'UTDummy',
+                       'appId' => 'DoesNotExist2',
+                       'restrictions' => MWRestrictions::newFromJson( '{"IPAddresses":["127.0.0.0/8"]}' ),
+                       'grants' => array( 'test' ),
+               ) );
+               $this->assertInstanceOf( 'BotPassword', $bp );
+               $this->assertFalse( $bp->isSaved() );
+               $this->assertSame( 43, $bp->getUserCentralId() );
+               $this->assertSame( 'DoesNotExist2', $bp->getAppId() );
+               $this->assertEquals( '{"IPAddresses":["127.0.0.0/8"]}', $bp->getRestrictions()->toJson() );
+               $this->assertSame( array( 'test' ), $bp->getGrants() );
+
+               $user = User::newFromName( 'UTSysop' );
+               $bp = BotPassword::newUnsaved( array(
+                       'centralId' => 45,
+                       'appId' => 'DoesNotExist'
+               ) );
+               $this->assertInstanceOf( 'BotPassword', $bp );
+               $this->assertFalse( $bp->isSaved() );
+               $this->assertSame( 45, $bp->getUserCentralId() );
+               $this->assertSame( 'DoesNotExist', $bp->getAppId() );
+
+               $user = User::newFromName( 'UTSysop' );
+               $bp = BotPassword::newUnsaved( array(
+                       'user' => $user,
+                       'appId' => 'BotPassword'
+               ) );
+               $this->assertInstanceOf( 'BotPassword', $bp );
+               $this->assertFalse( $bp->isSaved() );
+
+               $this->assertNull( BotPassword::newUnsaved( array(
+                       'user' => $user,
+                       'appId' => '',
+               ) ) );
+               $this->assertNull( BotPassword::newUnsaved( array(
+                       'user' => $user,
+                       'appId' => str_repeat( 'X', BotPassword::APPID_MAXLENGTH + 1 ),
+               ) ) );
+               $this->assertNull( BotPassword::newUnsaved( array(
+                       'user' => 'UTSysop',
+                       'appId' => 'Ok',
+               ) ) );
+               $this->assertNull( BotPassword::newUnsaved( array(
+                       'username' => 'UTInvalid',
+                       'appId' => 'Ok',
+               ) ) );
+               $this->assertNull( BotPassword::newUnsaved( array(
+                       'appId' => 'Ok',
+               ) ) );
+       }
+
+       public function testGetPassword() {
+               $bp = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42, 'BotPassword' ) );
+
+               $password = $bp->getPassword();
+               $this->assertInstanceOf( 'Password', $password );
+               $this->assertTrue( $password->equals( 'foobaz' ) );
+
+               $bp->centralId = 44;
+               $password = $bp->getPassword();
+               $this->assertInstanceOf( 'InvalidPassword', $password );
+
+               $bp = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42, 'BotPassword' ) );
+               $dbw = wfGetDB( DB_MASTER );
+               $dbw->update(
+                       'bot_passwords',
+                       array( 'bp_password' => 'garbage' ),
+                       array( 'bp_user' => 42, 'bp_app_id' => 'BotPassword' ),
+                       __METHOD__
+               );
+               $password = $bp->getPassword();
+               $this->assertInstanceOf( 'InvalidPassword', $password );
+       }
+
+       public function testInvalidateAllPasswordsForUser() {
+               $bp1 = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42, 'BotPassword' ) );
+               $bp2 = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 43, 'BotPassword' ) );
+
+               $this->assertNotInstanceOf( 'InvalidPassword', $bp1->getPassword(), 'sanity check' );
+               $this->assertNotInstanceOf( 'InvalidPassword', $bp2->getPassword(), 'sanity check' );
+               BotPassword::invalidateAllPasswordsForUser( 'UTSysop' );
+               $this->assertInstanceOf( 'InvalidPassword', $bp1->getPassword() );
+               $this->assertNotInstanceOf( 'InvalidPassword', $bp2->getPassword() );
+
+               $bp = TestingAccessWrapper::newFromObject( BotPassword::newFromCentralId( 42, 'BotPassword' ) );
+               $this->assertInstanceOf( 'InvalidPassword', $bp->getPassword() );
+       }
+
+       public function testRemoveAllPasswordsForUser() {
+               $this->assertNotNull( BotPassword::newFromCentralId( 42, 'BotPassword' ), 'sanity check' );
+               $this->assertNotNull( BotPassword::newFromCentralId( 43, 'BotPassword' ), 'sanity check' );
+
+               BotPassword::removeAllPasswordsForUser( 'UTSysop' );
+
+               $this->assertNull( BotPassword::newFromCentralId( 42, 'BotPassword' ) );
+               $this->assertNotNull( BotPassword::newFromCentralId( 43, 'BotPassword' ) );
+       }
+
+       public function testLogin() {
+               // Test failure when bot passwords aren't enabled
+               $this->setMwGlobals( 'wgEnableBotPasswords', false );
+               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', new FauxRequest );
+               $this->assertEquals( Status::newFatal( 'botpasswords-disabled' ), $status );
+               $this->setMwGlobals( 'wgEnableBotPasswords', true );
+
+               // Test failure when BotPasswordSessionProvider isn't configured
+               $manager = new SessionManager( array(
+                       'logger' => new Psr\Log\NullLogger,
+                       'store' => new EmptyBagOStuff,
+               ) );
+               $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton( $manager );
+               $this->assertNull(
+                       $manager->getProvider( 'MediaWiki\\Session\\BotPasswordSessionProvider' ),
+                       'sanity check'
+               );
+               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', new FauxRequest );
+               $this->assertEquals( Status::newFatal( 'botpasswords-no-provider' ), $status );
+               ScopedCallback::consume( $reset );
+
+               // Now configure BotPasswordSessionProvider for further tests...
+               $mainConfig = RequestContext::getMain()->getConfig();
+               $config = new HashConfig( array(
+                       'SessionProviders' => $mainConfig->get( 'SessionProviders' ) + array(
+                               'MediaWiki\\Session\\BotPasswordSessionProvider' => array(
+                                       'class' => 'MediaWiki\\Session\\BotPasswordSessionProvider',
+                                       'args' => array( array( 'priority' => 40 ) ),
+                               )
+                       ),
+               ) );
+               $manager = new SessionManager( array(
+                       'config' => new MultiConfig( array( $config, RequestContext::getMain()->getConfig() ) ),
+                       'logger' => new Psr\Log\NullLogger,
+                       'store' => new EmptyBagOStuff,
+               ) );
+               $reset = MediaWiki\Session\TestUtils::setSessionManagerSingleton( $manager );
+
+               // No "@"-thing in the username
+               $status = BotPassword::login( 'UTSysop', 'foobaz', new FauxRequest );
+               $this->assertEquals( Status::newFatal( 'botpasswords-invalid-name', '@' ), $status );
+
+               // No base user
+               $status = BotPassword::login( 'UTDummy@BotPassword', 'foobaz', new FauxRequest );
+               $this->assertEquals( Status::newFatal( 'nosuchuser', 'UTDummy' ), $status );
+
+               // No bot password
+               $status = BotPassword::login( 'UTSysop@DoesNotExist', 'foobaz', new FauxRequest );
+               $this->assertEquals(
+                       Status::newFatal( 'botpasswords-not-exist', 'UTSysop', 'DoesNotExist' ),
+                       $status
+               );
+
+               // Failed restriction
+               $request = $this->getMock( 'FauxRequest', array( 'getIP' ) );
+               $request->expects( $this->any() )->method( 'getIP' )
+                       ->will( $this->returnValue( '10.0.0.1' ) );
+               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', $request );
+               $this->assertEquals( Status::newFatal( 'botpasswords-restriction-failed' ), $status );
+
+               // Wrong password
+               $status = BotPassword::login( 'UTSysop@BotPassword', 'UTSysopPassword', new FauxRequest );
+               $this->assertEquals( Status::newFatal( 'wrongpassword' ), $status );
+
+               // Success!
+               $request = new FauxRequest;
+               $this->assertNotInstanceOf(
+                       'MediaWiki\\Session\\BotPasswordSessionProvider',
+                       $request->getSession()->getProvider(),
+                       'sanity check'
+               );
+               $status = BotPassword::login( 'UTSysop@BotPassword', 'foobaz', $request );
+               $this->assertInstanceOf( 'Status', $status );
+               $this->assertTrue( $status->isGood() );
+               $session = $status->getValue();
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertInstanceOf(
+                       'MediaWiki\\Session\\BotPasswordSessionProvider', $session->getProvider()
+               );
+               $this->assertSame( $session->getId(), $request->getSession()->getId() );
+
+               ScopedCallback::consume( $reset );
+       }
+
+       /**
+        * @dataProvider provideSave
+        * @param string|null $password
+        */
+       public function testSave( $password ) {
+               $passwordFactory = new \PasswordFactory();
+               $passwordFactory->init( \RequestContext::getMain()->getConfig() );
+               // A is unsalted MD5 (thus fast) ... we don't care about security here, this is test only
+               $passwordFactory->setDefaultType( 'A' );
+
+               $bp = BotPassword::newUnsaved( array(
+                       'centralId' => 42,
+                       'appId' => 'TestSave',
+                       'restrictions' => MWRestrictions::newFromJson( '{"IPAddresses":["127.0.0.0/8"]}' ),
+                       'grants' => array( 'test' ),
+               ) );
+               $this->assertFalse( $bp->isSaved(), 'sanity check' );
+               $this->assertNull(
+                       BotPassword::newFromCentralId( 42, 'TestSave', BotPassword::READ_LATEST ), 'sanity check'
+               );
+
+               $pwhash = $password ? $passwordFactory->newFromPlaintext( $password ) : null;
+               $this->assertFalse( $bp->save( 'update', $pwhash ) );
+               $this->assertTrue( $bp->save( 'insert', $pwhash ) );
+               $bp2 = BotPassword::newFromCentralId( 42, 'TestSave', BotPassword::READ_LATEST );
+               $this->assertInstanceOf( 'BotPassword', $bp2 );
+               $this->assertEquals( $bp->getUserCentralId(), $bp2->getUserCentralId() );
+               $this->assertEquals( $bp->getAppId(), $bp2->getAppId() );
+               $this->assertEquals( $bp->getToken(), $bp2->getToken() );
+               $this->assertEquals( $bp->getRestrictions(), $bp2->getRestrictions() );
+               $this->assertEquals( $bp->getGrants(), $bp2->getGrants() );
+               $pw = TestingAccessWrapper::newFromObject( $bp )->getPassword();
+               if ( $password === null ) {
+                       $this->assertInstanceOf( 'InvalidPassword', $pw );
+               } else {
+                       $this->assertTrue( $pw->equals( $password ) );
+               }
+
+               $token = $bp->getToken();
+               $this->assertFalse( $bp->save( 'insert' ) );
+               $this->assertTrue( $bp->save( 'update' ) );
+               $this->assertNotEquals( $token, $bp->getToken() );
+               $bp2 = BotPassword::newFromCentralId( 42, 'TestSave', BotPassword::READ_LATEST );
+               $this->assertInstanceOf( 'BotPassword', $bp2 );
+               $this->assertEquals( $bp->getToken(), $bp2->getToken() );
+               $pw = TestingAccessWrapper::newFromObject( $bp )->getPassword();
+               if ( $password === null ) {
+                       $this->assertInstanceOf( 'InvalidPassword', $pw );
+               } else {
+                       $this->assertTrue( $pw->equals( $password ) );
+               }
+
+               $pwhash = $passwordFactory->newFromPlaintext( 'XXX' );
+               $token = $bp->getToken();
+               $this->assertTrue( $bp->save( 'update', $pwhash ) );
+               $this->assertNotEquals( $token, $bp->getToken() );
+               $pw = TestingAccessWrapper::newFromObject( $bp )->getPassword();
+               $this->assertTrue( $pw->equals( 'XXX' ) );
+
+               $this->assertTrue( $bp->delete() );
+               $this->assertFalse( $bp->isSaved() );
+               $this->assertNull( BotPassword::newFromCentralId( 42, 'TestSave', BotPassword::READ_LATEST ) );
+
+               $this->assertFalse( $bp->save( 'foobar' ) );
+       }
+
+       public static function provideSave() {
+               return array(
+                       array( null ),
+                       array( 'foobar' ),
+               );
+       }
+}
index 45c4b8c..aadc5c9 100644 (file)
@@ -446,89 +446,4 @@ class UserTest extends MediaWikiTestCase {
                $this->assertGreaterThan(
                        $touched, $user->getDBTouched(), "user_touched increased with casOnTouched() #2" );
        }
-
-       public static function setExtendedLoginCookieDataProvider() {
-               $data = array();
-               $now = time();
-
-               $secondsInDay = 86400;
-
-               // Arbitrary durations, in units of days, to ensure it chooses the
-               // right one.  There is a 5-minute grace period (see testSetExtendedLoginCookie)
-               // to work around slow tests, since we're not currently mocking time() for PHP.
-
-               $durationOne = $secondsInDay * 5;
-               $durationTwo = $secondsInDay * 29;
-               $durationThree = $secondsInDay * 17;
-
-               // If $wgExtendedLoginCookieExpiration is null, then the expiry passed to
-               // set cookie is time() + $wgCookieExpiration
-               $data[] = array(
-                       null,
-                       $durationOne,
-                       $now + $durationOne,
-               );
-
-               // If $wgExtendedLoginCookieExpiration isn't null, then the expiry passed to
-               // set cookie is $now + $wgExtendedLoginCookieExpiration
-               $data[] = array(
-                       $durationTwo,
-                       $durationThree,
-                       $now + $durationTwo,
-               );
-
-               return $data;
-       }
-
-       /**
-        * @dataProvider setExtendedLoginCookieDataProvider
-        * @covers User::getRequest
-        * @covers User::setCookie
-        * @backupGlobals enabled
-        */
-       public function testSetExtendedLoginCookie(
-               $extendedLoginCookieExpiration,
-               $cookieExpiration,
-               $expectedExpiry
-       ) {
-               $this->setMwGlobals( array(
-                       'wgExtendedLoginCookieExpiration' => $extendedLoginCookieExpiration,
-                       'wgCookieExpiration' => $cookieExpiration,
-               ) );
-
-               $response = $this->getMock( 'WebResponse' );
-               $setcookieSpy = $this->any();
-               $response->expects( $setcookieSpy )
-                       ->method( 'setcookie' );
-
-               $request = new MockWebRequest( $response );
-               $user = new UserProxy( User::newFromSession( $request ) );
-               $user->setExtendedLoginCookie( 'name', 'value', true );
-
-               $setcookieInvocations = $setcookieSpy->getInvocations();
-               $setcookieInvocation = end( $setcookieInvocations );
-               $actualExpiry = $setcookieInvocation->parameters[2];
-
-               // TODO: ± 600 seconds compensates for
-               // slow-running tests. However, the dependency on the time
-               // function should be removed.  This requires some way
-               // to mock/isolate User->setExtendedLoginCookie's call to time()
-               $this->assertEquals( $expectedExpiry, $actualExpiry, '', 600 );
-       }
-}
-
-class UserProxy extends User {
-
-       /**
-        * @var User
-        */
-       protected $user;
-
-       public function __construct( User $user ) {
-               $this->user = $user;
-       }
-
-       public function setExtendedLoginCookie( $name, $value, $secure ) {
-               $this->user->setExtendedLoginCookie( $name, $value, $secure );
-       }
 }
index d224af8..61d9a70 100644 (file)
@@ -221,7 +221,6 @@ class BatchRowUpdateTest extends MediaWikiTestCase {
                return call_user_func_array( array( $this, 'onConsecutiveCalls' ), $retvals );
        }
 
-
        protected function genSelectResult( $batchSize, $numRows, $rowGenerator ) {
                $res = array();
                for ( $i = 0; $i < $numRows; $i += $batchSize ) {
index 2c51af3..5dc0498 100644 (file)
@@ -91,6 +91,4 @@ class MWCryptHKDFTest extends MediaWikiTestCase {
                );
                // @codingStandardsIgnoreEnd
        }
-
-
 }
diff --git a/tests/phpunit/includes/utils/MWGrantsTest.php b/tests/phpunit/includes/utils/MWGrantsTest.php
new file mode 100644 (file)
index 0000000..9d0d962
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+class MWGrantsTest extends MediaWikiTestCase {
+
+       protected function setUp() {
+               parent::setUp();
+
+               $this->setMwGlobals( array(
+                       'wgGrantPermissions' => array(
+                               'hidden1' => array( 'read' => true, 'autoconfirmed' => false ),
+                               'hidden2' => array( 'autoconfirmed' => true ),
+                               'normal' => array( 'edit' => true ),
+                               'normal2' => array( 'edit' => true, 'create' => true ),
+                               'admin' => array( 'protect' => true, 'delete' => true ),
+                       ),
+                       'wgGrantPermissionGroups' => array(
+                               'hidden1' => 'hidden',
+                               'hidden2' => 'hidden',
+                               'normal' => 'normal-group',
+                               'admin' => 'admin',
+                       ),
+               ) );
+       }
+
+       /**
+        * @covers MWGrants::getValidGrants
+        */
+       public function testGetValidGrants() {
+               $this->assertSame(
+                       array( 'hidden1', 'hidden2', 'normal', 'normal2', 'admin' ),
+                       MWGrants::getValidGrants()
+               );
+       }
+
+       /**
+        * @covers MWGrants::getRightsByGrant
+        */
+       public function testGetRightsByGrant() {
+               $this->assertSame(
+                       array(
+                               'hidden1' => array( 'read' ),
+                               'hidden2' => array( 'autoconfirmed' ),
+                               'normal' => array( 'edit' ),
+                               'normal2' => array( 'edit', 'create' ),
+                               'admin' => array( 'protect', 'delete' ),
+                       ),
+                       MWGrants::getRightsByGrant()
+               );
+       }
+
+       /**
+        * @dataProvider provideGetGrantRights
+        * @covers MWGrants::getGrantRights
+        * @param array|string $grants
+        * @param array $rights
+        */
+       public function testGetGrantRights( $grants, $rights ) {
+               $this->assertSame( $rights, MWGrants::getGrantRights( $grants ) );
+       }
+
+       public static function provideGetGrantRights() {
+               return array(
+                       array( 'hidden1', array( 'read' ) ),
+                       array( array( 'hidden1', 'hidden2', 'hidden3' ), array( 'read', 'autoconfirmed' ) ),
+                       array( array( 'normal1', 'normal2' ), array( 'edit', 'create' ) ),
+               );
+       }
+
+       /**
+        * @dataProvider provideGrantsAreValid
+        * @covers MWGrants::grantsAreValid
+        * @param array $grants
+        * @param bool $valid
+        */
+       public function testGrantsAreValid( $grants, $valid ) {
+               $this->assertSame( $valid, MWGrants::grantsAreValid( $grants ) );
+       }
+
+       public static function provideGrantsAreValid() {
+               return array(
+                       array( array( 'hidden1', 'hidden2' ), true ),
+                       array( array( 'hidden1', 'hidden3' ), false ),
+               );
+       }
+
+       /**
+        * @dataProvider provideGetGrantGroups
+        * @covers MWGrants::getGrantGroups
+        * @param array|null $grants
+        * @param array $expect
+        */
+       public function testGetGrantGroups( $grants, $expect ) {
+               $this->assertSame( $expect, MWGrants::getGrantGroups( $grants ) );
+       }
+
+       public static function provideGetGrantGroups() {
+               return array(
+                       array( null, array(
+                               'hidden' => array( 'hidden1', 'hidden2' ),
+                               'normal-group' => array( 'normal' ),
+                               'other' => array( 'normal2' ),
+                               'admin' => array( 'admin' ),
+                       ) ),
+                       array( array( 'hidden1', 'normal' ), array(
+                               'hidden' => array( 'hidden1' ),
+                               'normal-group' => array( 'normal' ),
+                       ) ),
+               );
+       }
+
+       /**
+        * @covers MWGrants::getHiddenGrants
+        */
+       public function testGetHiddenGrants() {
+               $this->assertSame( array( 'hidden1', 'hidden2' ), MWGrants::getHiddenGrants() );
+       }
+
+}
diff --git a/tests/phpunit/includes/utils/MWRestrictionsTest.php b/tests/phpunit/includes/utils/MWRestrictionsTest.php
new file mode 100644 (file)
index 0000000..66a1130
--- /dev/null
@@ -0,0 +1,215 @@
+<?php
+class MWRestrictionsTest extends PHPUnit_Framework_TestCase {
+
+       protected static $restrictionsForChecks;
+
+       public static function setUpBeforeClass() {
+               self::$restrictionsForChecks = MWRestrictions::newFromArray( array(
+                       'IPAddresses' => array(
+                               '10.0.0.0/8',
+                               '172.16.0.0/12',
+                               '2001:db8::/33',
+                       )
+               ) );
+       }
+
+       /**
+        * @covers MWRestrictions::newDefault
+        * @covers MWRestrictions::__construct
+        */
+       public function testNewDefault() {
+               $ret = MWRestrictions::newDefault();
+               $this->assertInstanceOf( 'MWRestrictions', $ret );
+               $this->assertSame(
+                       '{"IPAddresses":["0.0.0.0/0","::/0"]}',
+                       $ret->toJson()
+               );
+       }
+
+       /**
+        * @covers MWRestrictions::newFromArray
+        * @covers MWRestrictions::__construct
+        * @covers MWRestrictions::loadFromArray
+        * @covers MWRestrictions::toArray
+        * @dataProvider provideArray
+        * @param array $data
+        * @param bool|InvalidArgumentException $expect True if the call succeeds,
+        *  otherwise the exception that should be thrown.
+        */
+       public function testArray( $data, $expect ) {
+               if ( $expect === true ) {
+                       $ret = MWRestrictions::newFromArray( $data );
+                       $this->assertInstanceOf( 'MWRestrictions', $ret );
+                       $this->assertSame( $data, $ret->toArray() );
+               } else {
+                       try {
+                               MWRestrictions::newFromArray( $data );
+                               $this->fail( 'Expected exception not thrown' );
+                       } catch ( InvalidArgumentException $ex ) {
+                               $this->assertEquals( $expect, $ex );
+                       }
+               }
+       }
+
+       public static function provideArray() {
+               return array(
+                       array( array( 'IPAddresses' => array() ), true ),
+                       array( array( 'IPAddresses' => array( '127.0.0.1/32' ) ), true ),
+                       array(
+                               array( 'IPAddresses' => array( '256.0.0.1/32' ) ),
+                               new InvalidArgumentException( 'Invalid IP address: 256.0.0.1/32' )
+                       ),
+                       array(
+                               array( 'IPAddresses' => '127.0.0.1/32' ),
+                               new InvalidArgumentException( 'IPAddresses is not an array' )
+                       ),
+                       array(
+                               array(),
+                               new InvalidArgumentException( 'Array is missing required keys: IPAddresses' )
+                       ),
+                       array(
+                               array( 'foo' => 'bar', 'bar' => 42 ),
+                               new InvalidArgumentException( 'Array contains invalid keys: foo, bar' )
+                       ),
+               );
+       }
+
+       /**
+        * @covers MWRestrictions::newFromJson
+        * @covers MWRestrictions::__construct
+        * @covers MWRestrictions::loadFromArray
+        * @covers MWRestrictions::toJson
+        * @covers MWRestrictions::__toString
+        * @dataProvider provideJson
+        * @param string $json
+        * @param array|InvalidArgumentException $expect
+        */
+       public function testJson( $json, $expect ) {
+               if ( is_array( $expect ) ) {
+                       $ret = MWRestrictions::newFromJson( $json );
+                       $this->assertInstanceOf( 'MWRestrictions', $ret );
+                       $this->assertSame( $expect, $ret->toArray() );
+
+                       $this->assertSame( $json, $ret->toJson( false ) );
+                       $this->assertSame( $json, (string)$ret );
+
+                       $this->assertSame(
+                               FormatJson::encode( $expect, true, FormatJson::ALL_OK ),
+                               $ret->toJson( true )
+                       );
+               } else {
+                       try {
+                               MWRestrictions::newFromJson( $json );
+                               $this->fail( 'Expected exception not thrown' );
+                       } catch ( InvalidArgumentException $ex ) {
+                               $this->assertTrue( true );
+                       }
+               }
+       }
+
+       public static function provideJson() {
+               return array(
+                       array(
+                               '{"IPAddresses":[]}',
+                               array( 'IPAddresses' => array() )
+                       ),
+                       array(
+                               '{"IPAddresses":["127.0.0.1/32"]}',
+                               array( 'IPAddresses' => array( '127.0.0.1/32' ) )
+                       ),
+                       array(
+                               '{"IPAddresses":["256.0.0.1/32"]}',
+                               new InvalidArgumentException( 'Invalid IP address: 256.0.0.1/32' )
+                       ),
+                       array(
+                               '{"IPAddresses":"127.0.0.1/32"}',
+                               new InvalidArgumentException( 'IPAddresses is not an array' )
+                       ),
+                       array(
+                               '{}',
+                               new InvalidArgumentException( 'Array is missing required keys: IPAddresses' )
+                       ),
+                       array(
+                               '{"foo":"bar","bar":42}',
+                               new InvalidArgumentException( 'Array contains invalid keys: foo, bar' )
+                       ),
+                       array(
+                               '{"IPAddresses":[]',
+                               new InvalidArgumentException( 'Invalid restrictions JSON' )
+                       ),
+                       array(
+                               '"IPAddresses"',
+                               new InvalidArgumentException( 'Invalid restrictions JSON' )
+                       ),
+               );
+       }
+
+       /**
+        * @covers MWRestrictions::checkIP
+        * @dataProvider provideCheckIP
+        * @param string $ip
+        * @param bool $pass
+        */
+       public function testCheckIP( $ip, $pass ) {
+               $this->assertSame( $pass, self::$restrictionsForChecks->checkIP( $ip ) );
+       }
+
+       public static function provideCheckIP() {
+               return array(
+                       array( '10.0.0.1', true ),
+                       array( '172.16.0.0', true ),
+                       array( '192.0.2.1', false ),
+                       array( '2001:db8:1::', true ),
+                       array( '2001:0db8:0000:0000:0000:0000:0000:0000', true ),
+                       array( '2001:0DB8:8000::', false ),
+               );
+       }
+
+       /**
+        * @covers MWRestrictions::check
+        * @dataProvider provideCheck
+        * @param WebRequest $request
+        * @param Status $expect
+        */
+       public function testCheck( $request, $expect ) {
+               $this->assertEquals( $expect, self::$restrictionsForChecks->check( $request ) );
+       }
+
+       public function provideCheck() {
+               $ret = array();
+
+               $mockBuilder = $this->getMockBuilder( 'FauxRequest' )
+                       ->setMethods( array( 'getIP' ) );
+
+               foreach ( self::provideCheckIP() as $checkIP ) {
+                       $ok = array();
+                       $request = $mockBuilder->getMock();
+
+                       $request->expects( $this->any() )->method( 'getIP' )
+                               ->will( $this->returnValue( $checkIP[0] ) );
+                       $ok['ip'] = $checkIP[1];
+
+                       /* If we ever add more restrictions, add nested for loops here:
+                        *  foreach ( self::provideCheckFoo() as $checkFoo ) {
+                        *      $request->expects( $this->any() )->method( 'getFoo' )
+                        *          ->will( $this->returnValue( $checkFoo[0] );
+                        *      $ok['foo'] = $checkFoo[1];
+                        *
+                        *      foreach ( self::provideCheckBar() as $checkBar ) {
+                        *          $request->expects( $this->any() )->method( 'getBar' )
+                        *              ->will( $this->returnValue( $checkBar[0] );
+                        *          $ok['bar'] = $checkBar[1];
+                        *
+                        *          // etc.
+                        *      }
+                        *  }
+                        */
+
+                       $status = Status::newGood();
+                       $status->setResult( $ok === array_filter( $ok ), $ok );
+                       $ret[] = array( $request, $status );
+               }
+
+               return $ret;
+       }
+}
index 5c6a6cd..245a97a 100644 (file)
@@ -120,6 +120,16 @@ class MaintenanceFixup extends Maintenance {
                return call_user_func_array( array( "parent", __FUNCTION__ ), func_get_args() );
        }
 
+       public function addOption( $name, $description, $required = false,
+               $withArg = false, $shortName = false, $multiOccurance = false
+       ) {
+               return call_user_func_array( array( "parent", __FUNCTION__ ), func_get_args() );
+       }
+
+       public function getOption( $name, $default = null ) {
+               return call_user_func_array( array( "parent", __FUNCTION__ ), func_get_args() );
+       }
+
        // --- Requirements for getting instance of abstract class
 
        public function execute() {
@@ -829,4 +839,37 @@ class MaintenanceTest extends MediaWikiTestCase {
                $this->m->setConfig( $conf );
                $this->assertSame( $conf, $this->m->getConfig() );
        }
+
+       function testParseArgs() {
+               $m2 = new MaintenanceFixup( $this );
+               // Create an option with an argument allowed to be specified multiple times
+               $m2->addOption( 'multi', 'This option does stuff', false, true, false, true );
+               $m2->loadWithArgv( array( '--multi', 'this1', '--multi', 'this2' ) );
+
+               $this->assertEquals( array( 'this1', 'this2' ), $m2->getOption( 'multi' ) );
+               $this->assertEquals( array( array( 'multi', 'this1' ), array( 'multi', 'this2' ) ),
+                       $m2->orderedOptions );
+
+               $m2->simulateShutdown();
+
+               $m2 = new MaintenanceFixup( $this );
+
+               $m2->addOption( 'multi', 'This option does stuff', false, false, false, true );
+               $m2->loadWithArgv( array( '--multi', '--multi' ) );
+
+               $this->assertEquals( array( 1, 1 ), $m2->getOption( 'multi' ) );
+               $this->assertEquals( array( array( 'multi', 1 ), array( 'multi', 1 ) ), $m2->orderedOptions );
+
+               $m2->simulateShutdown();
+
+               $m2 = new MaintenanceFixup( $this );
+               // Create an option with an argument allowed to be specified multiple times
+               $m2->addOption( 'multi', 'This option doesn\'t actually support multiple occurrences' );
+               $m2->loadWithArgv( array( '--multi=yo' ) );
+
+               $this->assertEquals( 'yo', $m2->getOption( 'multi' ) );
+               $this->assertEquals( array( array( 'multi', 'yo' ) ), $m2->orderedOptions );
+
+               $m2->simulateShutdown();
+       }
 }
index f5dd98b..893e4f9 100644 (file)
@@ -1,10 +1,15 @@
 <?php
 
-require_once __DIR__ . "/../../../maintenance/backupTextPass.inc";
+require_once __DIR__ . "/../../../maintenance/dumpTextPass.php";
 
 /**
  * Tests for TextPassDumper that rely on the database
  *
+ * Some of these tests use the old constuctor for TextPassDumper
+ * and the dump() function, while others use the new loadWithArgv( $args )
+ * function and execute(). This is to ensure both the old and new methods
+ * work properly.
+ *
  * @group Database
  * @group Dump
  * @covers TextPassDumper
@@ -172,8 +177,10 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                // Setting up of the dump
                $nameStub = $this->setUpStub();
                $nameFull = $this->getNewTempFile();
-               $dumper = new TextPassDumper( array( "--stub=file:"
-                       . $nameStub, "--output=file:" . $nameFull ) );
+
+               $dumper = new TextPassDumper( array( "--stub=file:" . $nameStub,
+                       "--output=file:" . $nameFull ) );
+
                $dumper->prefetch = $prefetchMock;
                $dumper->reporting = false;
                $dumper->setDb( $this->db );
@@ -261,7 +268,8 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
                        $this->assertTrue( wfMkdirParents( $nameOutputDir ),
                                "Creating temporary output directory " );
                        $this->setUpStub( $nameStub, $iterations );
-                       $dumper = new TextPassDumper( array( "--stub=file:" . $nameStub,
+                       $dumper = new TextPassDumper();
+                       $dumper->loadWithArgv( array( "--stub=file:" . $nameStub,
                                "--output=" . $checkpointFormat . ":" . $nameOutputDir . "/full",
                                "--maxtime=1" /*This is in minutes. Fixup is below*/,
                                "--buffersize=32768", // The default of 32 iterations fill up 32KB about twice
@@ -272,7 +280,7 @@ class TextPassDumperDatabaseTest extends DumpTestCase {
 
                        // The actual dump and taking time
                        $ts_before = microtime( true );
-                       $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
+                       $dumper->execute();
                        $ts_after = microtime( true );
                        $lastDuration = $ts_after - $ts_before;
 
@@ -634,7 +642,9 @@ class TextPassDumperDatabaselessTest extends MediaWikiLangTestCase {
         * @dataProvider bufferSizeProvider
         */
        function testBufferSizeSetting( $expected, $size, $msg ) {
-               $dumper = new TextPassDumperAccessor( array( "--buffersize=" . $size ) );
+               $dumper = new TextPassDumperAccessor();
+               $dumper->loadWithArgv( array( "--buffersize=" . $size ) );
+               $dumper->execute();
                $this->assertEquals( $expected, $dumper->getBufferSize(), $msg );
        }
 
@@ -674,4 +684,8 @@ class TextPassDumperAccessor extends TextPassDumper {
        public function getBufferSize() {
                return $this->bufferSize;
        }
+
+       function dump( $history, $text = null ) {
+               return true;
+       }
 }
index 7ca4596..6629b67 100644 (file)
@@ -2,6 +2,11 @@
 /**
  * Tests for log dumps of BackupDumper
  *
+ * Some of these tests use the old constuctor for TextPassDumper
+ * and the dump() function, while others use the new loadWithArgv( $args )
+ * function and execute(). This is to ensure both the old and new methods
+ * work properly.
+ *
  * @group Database
  * @group Dump
  * @covers BackupDumper
@@ -136,7 +141,8 @@ class BackupDumperLoggerTest extends DumpTestCase {
 
                // Preparing the dump
                $fname = $this->getNewTempFile();
-               $dumper = new BackupDumper( array( "--output=file:" . $fname ) );
+
+               $dumper = new DumpBackup( array( '--output=file:' . $fname ) );
                $dumper->startId = $this->logId1;
                $dumper->endId = $this->logId3 + 1;
                $dumper->reporting = false;
@@ -173,8 +179,10 @@ class BackupDumperLoggerTest extends DumpTestCase {
 
                // Preparing the dump
                $fname = $this->getNewTempFile();
-               $dumper = new BackupDumper( array( "--output=gzip:" . $fname,
-                       "--reporting=2" ) );
+
+               $dumper = new DumpBackup();
+               $dumper->loadWithArgv( array( '--logs', '--output=gzip:' . $fname,
+                       '--reporting=2' ) );
                $dumper->startId = $this->logId1;
                $dumper->endId = $this->logId3 + 1;
                $dumper->setDb( $this->db );
@@ -190,7 +198,7 @@ class BackupDumperLoggerTest extends DumpTestCase {
                }
 
                // Performing the dump
-               $dumper->dump( WikiExporter::LOGS, WikiExporter::TEXT );
+               $dumper->execute();
 
                $this->assertTrue( fclose( $dumper->stderr ), "Closing stderr handle" );
 
index 8b6221b..5781d1c 100644 (file)
@@ -6,6 +6,7 @@
  * @group Dump
  * @covers BackupDumper
  */
+
 class BackupDumperPageTest extends DumpTestCase {
 
        // We'll add several pages, revision and texts. The following variables hold the
@@ -98,14 +99,15 @@ class BackupDumperPageTest extends DumpTestCase {
        function testFullTextPlain() {
                // Preparing the dump
                $fname = $this->getNewTempFile();
-               $dumper = new BackupDumper( array( "--output=file:" . $fname ) );
+
+               $dumper = new DumpBackup();
+               $dumper->loadWithArgv( array( '--full', '--quiet', '--output', 'file:' . $fname ) );
                $dumper->startId = $this->pageId1;
                $dumper->endId = $this->pageId4 + 1;
-               $dumper->reporting = false;
                $dumper->setDb( $this->db );
 
                // Performing the dump
-               $dumper->dump( WikiExporter::FULL, WikiExporter::TEXT );
+               $dumper->execute();
 
                // Checking the dumped data
                $this->assertDumpStart( $fname );
@@ -153,14 +155,15 @@ class BackupDumperPageTest extends DumpTestCase {
        function testFullStubPlain() {
                // Preparing the dump
                $fname = $this->getNewTempFile();
-               $dumper = new BackupDumper( array( "--output=file:" . $fname ) );
+
+               $dumper = new DumpBackup();
+               $dumper->loadWithArgv( array( '--full', '--quiet', '--output', 'file:' . $fname, '--stub' ) );
                $dumper->startId = $this->pageId1;
                $dumper->endId = $this->pageId4 + 1;
-               $dumper->reporting = false;
                $dumper->setDb( $this->db );
 
                // Performing the dump
-               $dumper->dump( WikiExporter::FULL, WikiExporter::STUB );
+               $dumper->execute();
 
                // Checking the dumped data
                $this->assertDumpStart( $fname );
@@ -202,7 +205,8 @@ class BackupDumperPageTest extends DumpTestCase {
        function testCurrentStubPlain() {
                // Preparing the dump
                $fname = $this->getNewTempFile();
-               $dumper = new BackupDumper( array( "--output=file:" . $fname ) );
+
+               $dumper = new DumpBackup( array( '--output', 'file:' . $fname ) );
                $dumper->startId = $this->pageId1;
                $dumper->endId = $this->pageId4 + 1;
                $dumper->reporting = false;
@@ -247,7 +251,8 @@ class BackupDumperPageTest extends DumpTestCase {
 
                // Preparing the dump
                $fname = $this->getNewTempFile();
-               $dumper = new BackupDumper( array( "--output=gzip:" . $fname ) );
+
+               $dumper = new DumpBackup( array( '--output', 'gzip:' . $fname ) );
                $dumper->startId = $this->pageId1;
                $dumper->endId = $this->pageId4 + 1;
                $dumper->reporting = false;
@@ -306,7 +311,7 @@ class BackupDumperPageTest extends DumpTestCase {
                $fnameMetaCurrent = $this->getNewTempFile();
                $fnameArticles = $this->getNewTempFile();
 
-               $dumper = new BackupDumper( array( "--output=gzip:" . $fnameMetaHistory,
+               $dumper = new DumpBackup( array( "--full", "--stub", "--output=gzip:" . $fnameMetaHistory,
                        "--output=gzip:" . $fnameMetaCurrent, "--filter=latest",
                        "--output=gzip:" . $fnameArticles, "--filter=latest",
                        "--filter=notalk", "--filter=namespace:!NS_USER",
diff --git a/tests/phpunit/mocks/session/DummySessionBackend.php b/tests/phpunit/mocks/session/DummySessionBackend.php
new file mode 100644 (file)
index 0000000..f96e61c
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+
+namespace MediaWiki\Session;
+
+/**
+ * Dummy session backend
+ *
+ * This isn't a real backend, but implements some methods that SessionBackend
+ * does so tests can run.
+ */
+class DummySessionBackend {
+       public $data = array(
+               'foo' => 1,
+               'bar' => 2,
+               0 => 'zero',
+       );
+       public $dirty = false;
+
+       public function &getData() {
+               return $this->data;
+       }
+
+       public function dirty() {
+               $this->dirty = true;
+       }
+
+       public function deregisterSession( $index ) {
+       }
+}
diff --git a/tests/phpunit/mocks/session/DummySessionProvider.php b/tests/phpunit/mocks/session/DummySessionProvider.php
new file mode 100644 (file)
index 0000000..4468191
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+use MediaWiki\Session\SessionProvider;
+use MediaWiki\Session\SessionInfo;
+use MediaWiki\Session\SessionBackend;
+use MediaWiki\Session\UserInfo;
+
+/**
+ * Dummy session provider
+ *
+ * An implementation of a session provider that doesn't actually do anything.
+ */
+class DummySessionProvider extends SessionProvider {
+
+       const ID = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa';
+
+       public function provideSessionInfo( WebRequest $request ) {
+               return new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'provider' => $this,
+                       'id' => self::ID,
+                       'persisted' => true,
+                       'userInfo' => UserInfo::newAnonymous(),
+               ) );
+       }
+
+       public function newSessionInfo( $id = null ) {
+               return new SessionInfo( SessionInfo::MIN_PRIORITY, array(
+                       'id' => $id,
+                       'idIsSafe' => true,
+                       'provider' => $this,
+                       'persisted' => false,
+                       'userInfo' => UserInfo::newAnonymous(),
+               ) );
+       }
+
+       public function persistsSessionId() {
+               return true;
+       }
+
+       public function canChangeUser() {
+               return $this->persistsSessionId();
+       }
+
+       public function persistSession( SessionBackend $session, WebRequest $request ) {
+       }
+
+       public function unpersistSession( WebRequest $request ) {
+       }
+
+       public function immutableSessionCouldExistForUser( $user ) {
+               return false;
+       }
+
+       public function preventImmutableSessionsForUser( $user ) {
+       }
+
+       public function suggestLoginUsername( WebRequest $request ) {
+               return $request->getCookie( 'UserName' );
+       }
+
+}
index aaa7751..0ae0b21 100755 (executable)
@@ -70,9 +70,12 @@ class PHPUnitMaintClass extends Maintenance {
                parent::finalSetup();
 
                global $wgMainCacheType, $wgMessageCacheType, $wgParserCacheType, $wgMainWANCache;
+               global $wgMainStash;
                global $wgLanguageConverterCacheType, $wgUseDatabaseMessages;
                global $wgLocaltimezone, $wgLocalisationCacheConf;
                global $wgDevelopmentWarnings;
+               global $wgSessionProviders;
+               global $wgJobTypeConf;
 
                // Inject test autoloader
                require_once __DIR__ . '/../TestsAutoLoader.php';
@@ -95,6 +98,10 @@ class PHPUnitMaintClass extends Maintenance {
                $wgLanguageConverterCacheType = 'hash';
                // Uses db-replicated in DefaultSettings
                $wgMainStash = 'hash';
+               // Use memory job queue
+               $wgJobTypeConf = array(
+                       'default' => array( 'class' => 'JobQueueMemory', 'order' => 'fifo' ),
+               );
 
                $wgUseDatabaseMessages = false; # Set for future resets
 
@@ -103,6 +110,19 @@ class PHPUnitMaintClass extends Maintenance {
 
                $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 = array(
+                       array(
+                               'class' => 'MediaWiki\\Session\\CookieSessionProvider',
+                               'args' => array( array(
+                                       'priority' => 30,
+                                       'callUserSetCookiesHook' => true,
+                               ) ),
+                       ),
+               );
+
                // Bug 44192 Do not attempt to send a real e-mail
                Hooks::clear( 'AlternateUserMailer' );
                Hooks::register(
@@ -242,7 +262,6 @@ if ( version_compare( PHP_VERSION, '5.4.0', '<' ) ) {
        } );
 }
 
-
 $ok = false;
 
 if ( class_exists( 'PHPUnit_TextUI_Command' ) ) {
@@ -251,12 +270,19 @@ if ( class_exists( 'PHPUnit_TextUI_Command' ) ) {
 } else {
        foreach ( array(
                                stream_resolve_include_path( 'phpunit.phar' ),
+                               stream_resolve_include_path( 'phpunit-old.phar' ),
                                'PHPUnit/Runner/Version.php',
                                'PHPUnit/Autoload.php'
                        ) as $includePath ) {
-               // @codingStandardsIgnoreStart
-               @include_once $includePath;
-               // @codingStandardsIgnoreEnd
+
+               if ( $includePath === false ) {
+                       // stream_resolve_include_path can return false
+                       continue;
+               }
+
+               \MediaWiki\suppressWarnings();
+               include_once $includePath;
+               \MediaWiki\restoreWarnings();
                if ( class_exists( 'PHPUnit_TextUI_Command' ) ) {
                        $ok = true;
                        echo "Using PHPUnit from $includePath\n";
diff --git a/tests/phpunit/structure/ApiDocumentationTest.php b/tests/phpunit/structure/ApiDocumentationTest.php
new file mode 100644 (file)
index 0000000..d2f96dc
--- /dev/null
@@ -0,0 +1,177 @@
+<?php
+
+/**
+ * Checks that all API modules, core and extensions, have documentation i18n messages
+ *
+ * It won't catch everything since i18n messages can vary based on the wiki
+ * configuration, but it should catch many cases for forgotten i18n.
+ *
+ * @group API
+ */
+class ApiDocumentationTest extends MediaWikiTestCase {
+
+       /** @var ApiMain */
+       private static $main;
+
+       /** @var array Sets of globals to test. Each array element is input to HashConfig */
+       private static $testGlobals = array(
+               array(
+                       'MiserMode' => false,
+                       'AllowCategorizedRecentChanges' => false,
+               ),
+               array(
+                       'MiserMode' => true,
+                       'AllowCategorizedRecentChanges' => true,
+               ),
+       );
+
+       /**
+        * Initialize/fetch the ApiMain instance for testing
+        * @return ApiMain
+        */
+       private static function getMain() {
+               if ( !self::$main ) {
+                       self::$main = new ApiMain( RequestContext::getMain() );
+                       self::$main->getContext()->setLanguage( 'en' );
+               }
+               return self::$main;
+       }
+
+       /**
+        * Test a message
+        * @param Message $msg
+        * @param string $what Which message is being checked
+        */
+       private function checkMessage( $msg, $what ) {
+               $msg = ApiBase::makeMessage( $msg, self::getMain()->getContext() );
+               $this->assertInstanceOf( 'Message', $msg, "$what message" );
+               $this->assertTrue( $msg->exists(), "$what message {$msg->getKey()} exists" );
+       }
+
+       /**
+        * @dataProvider provideDocumentationExists
+        * @param string $path Module path
+        * @param array $globals Globals to set
+        */
+       public function testDocumentationExists( $path, array $globals ) {
+               $main = self::getMain();
+
+               // Set configuration variables
+               $main->getContext()->setConfig( new MultiConfig( array(
+                       new HashConfig( $globals ),
+                       RequestContext::getMain()->getConfig(),
+               ) ) );
+               foreach ( $globals as $k => $v ) {
+                       $this->setMWGlobals( "wg$k", $v );
+               }
+
+               // Fetch module.
+               $module = TestingAccessWrapper::newFromObject( $main->getModuleFromPath( $path ) );
+
+               // Test messages for flags.
+               foreach ( $module->getHelpFlags() as $flag ) {
+                       $this->checkMessage( "api-help-flag-$flag", "Flag $flag" );
+               }
+
+               // Module description messages.
+               $this->checkMessage( $module->getDescriptionMessage(), 'Module description' );
+
+               // Parameters. Lots of messages in here.
+               $params = $module->getFinalParams( ApiBase::GET_VALUES_FOR_HELP );
+               $tags = array();
+               foreach ( $params as $name => $settings ) {
+                       if ( !is_array( $settings ) ) {
+                               $settings = array();
+                       }
+
+                       // Basic description message
+                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) {
+                               $msg = $settings[ApiBase::PARAM_HELP_MSG];
+                       } else {
+                               $msg = "apihelp-{$path}-param-{$name}";
+                       }
+                       $this->checkMessage( $msg, "Parameter $name description" );
+
+                       // If param-per-value is in use, each value's message
+                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) {
+                               $this->assertInternalType( 'array', $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE],
+                                       "Parameter $name PARAM_HELP_MSG_PER_VALUE is array" );
+                               $this->assertInternalType( 'array', $settings[ApiBase::PARAM_TYPE],
+                                       "Parameter $name PARAM_TYPE is array for msg-per-value mode" );
+                               $valueMsgs = $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE];
+                               foreach ( $settings[ApiBase::PARAM_TYPE] as $value ) {
+                                       if ( isset( $valueMsgs[$value] ) ) {
+                                               $msg = $valueMsgs[$value];
+                                       } else {
+                                               $msg = "apihelp-{$path}-paramvalue-{$name}-{$value}";
+                                       }
+                                       $this->checkMessage( $msg, "Parameter $name value $value" );
+                               }
+                       }
+
+                       // Appended messages (e.g. "disabled in miser mode")
+                       if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) {
+                               $this->assertInternalType( 'array', $settings[ApiBase::PARAM_HELP_MSG_APPEND],
+                                       "Parameter $name PARAM_HELP_MSG_APPEND is array" );
+                               foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $i => $msg ) {
+                                       $this->checkMessage( $msg, "Parameter $name HELP_MSG_APPEND #$i" );
+                               }
+                       }
+
+                       // Info tags (e.g. "only usable in mode 1") are typically shared by
+                       // several parameters, so accumulate them and test them later.
+                       if ( !empty( $settings[ApiBase::PARAM_HELP_MSG_INFO] ) ) {
+                               foreach ( $settings[ApiBase::PARAM_HELP_MSG_INFO] as $i ) {
+                                       $tags[array_shift( $i )] = 1;
+                               }
+                       }
+               }
+
+               // Info tags (e.g. "only usable in mode 1") accumulated above
+               foreach ( $tags as $tag => $dummy ) {
+                       $this->checkMessage( "apihelp-{$path}-paraminfo-{$tag}", "HELP_MSG_INFO tag $tag" );
+               }
+
+               // Messages for examples.
+               foreach ( $module->getExamplesMessages() as $qs => $msg ) {
+                       $this->checkMessage( $msg, "Example $qs" );
+               }
+       }
+
+       public static function provideDocumentationExists() {
+               $main = self::getMain();
+               $paths = self::getSubModulePaths( $main->getModuleManager() );
+               array_unshift( $paths, $main->getModulePath() );
+
+               $ret = array();
+               foreach ( $paths as $path ) {
+                       foreach ( self::$testGlobals as $globals ) {
+                               $g = array();
+                               foreach ( $globals as $k => $v ) {
+                                       $g[] = "$k=" . var_export( $v, 1 );
+                               }
+                               $k = "Module $path with " . join( ', ', $g );
+                               $ret[$k] = array( $path, $globals );
+                       }
+               }
+               return $ret;
+       }
+
+       /**
+        * Return paths of all submodules in an ApiModuleManager, recursively
+        * @param ApiModuleManager $manager
+        * @return string[]
+        */
+       protected static function getSubModulePaths( ApiModuleManager $manager ) {
+               $paths = array();
+               foreach ( $manager->getNames() as $name ) {
+                       $module = $manager->getModule( $name );
+                       $paths[] = $module->getModulePath();
+                       $subManager = $module->getModuleManager();
+                       if ( $subManager ) {
+                               $paths = array_merge( $paths, self::getSubModulePaths( $subManager ) );
+                       }
+               }
+               return $paths;
+       }
+}
index 80893da..5e70455 100644 (file)
@@ -86,55 +86,9 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite {
                RepoGroup::destroySingleton();
                FileBackendGroup::destroySingleton();
 
-               $this->teardownUploadDir( $this->uploadDir );
-
                parent::tearDown();
        }
 
-       private $uploadDir;
-       private $keepUploads;
-
-       /**
-        * 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(
-                       array(
-                               "$dir/3/3a/Foobar.jpg",
-                               "$dir/thumb/3/3a/Foobar.jpg/180px-Foobar.jpg",
-                               "$dir/thumb/3/3a/Foobar.jpg/200px-Foobar.jpg",
-                               "$dir/thumb/3/3a/Foobar.jpg/640px-Foobar.jpg",
-                               "$dir/thumb/3/3a/Foobar.jpg/120px-Foobar.jpg",
-
-                               "$dir/0/09/Bad.jpg",
-                       )
-               );
-
-               self::deleteDirs(
-                       array(
-                               "$dir/3/3a",
-                               "$dir/3",
-                               "$dir/thumb/6/65",
-                               "$dir/thumb/6",
-                               "$dir/thumb/3/3a/Foobar.jpg",
-                               "$dir/thumb/3/3a",
-                               "$dir/thumb/3",
-
-                               "$dir/0/09/",
-                               "$dir/0/",
-
-                               "$dir/thumb",
-                               "$dir",
-                       )
-               );
-       }
-
        /**
         * Delete the specified files, if they exist.
         *
@@ -170,15 +124,7 @@ class UploadFromUrlTestSuite extends PHPUnit_Framework_TestSuite {
        private function setupUploadDir() {
                global $IP;
 
-               if ( $this->keepUploads ) {
-                       $dir = wfTempDir() . '/mwParser-images';
-
-                       if ( is_dir( $dir ) ) {
-                               return $dir;
-                       }
-               } else {
-                       $dir = $this->getNewTempDirectory();
-               }
+               $dir = $this->getNewTempDirectory();
 
                wfDebug( "Creating upload directory $dir\n" );
 
index 2846fde..64def91 100644 (file)
@@ -6,58 +6,81 @@
  */
 class MediaWikiTestCaseTest extends MediaWikiTestCase {
 
-       const GLOBAL_KEY_EXISTING = 'MediaWikiTestCaseTestGLOBAL-Existing';
        const GLOBAL_KEY_NONEXISTING = 'MediaWikiTestCaseTestGLOBAL-NONExisting';
 
+       private static $startGlobals = array(
+               'MediaWikiTestCaseTestGLOBAL-ExistingString' => 'foo',
+               'MediaWikiTestCaseTestGLOBAL-ExistingStringEmpty' => '',
+               'MediaWikiTestCaseTestGLOBAL-ExistingArray' => array( 1, 'foo' => 'bar' ),
+               'MediaWikiTestCaseTestGLOBAL-ExistingArrayEmpty' => array(),
+       );
+
        public static function setUpBeforeClass() {
                parent::setUpBeforeClass();
-               $GLOBALS[self::GLOBAL_KEY_EXISTING] = 'foo';
+               foreach ( self::$startGlobals as $key => $value ) {
+                       $GLOBALS[$key] = $value;
+               }
        }
 
        public static function tearDownAfterClass() {
                parent::tearDownAfterClass();
-               unset( $GLOBALS[self::GLOBAL_KEY_EXISTING] );
+               foreach ( self::$startGlobals as $key => $value ) {
+                       unset( $GLOBALS[$key] );
+               }
+       }
+
+       public function provideExistingKeysAndNewValues() {
+               $providedArray = array();
+               foreach ( array_keys( self::$startGlobals ) as $key ) {
+                       $providedArray[] = array( $key, 'newValue' );
+                       $providedArray[] = array( $key, array( 'newValue' ) );
+               }
+               return $providedArray;
        }
 
        /**
+        * @dataProvider provideExistingKeysAndNewValues
+        *
         * @covers MediaWikiTestCase::setMwGlobals
         * @covers MediaWikiTestCase::tearDown
         */
-       public function testSetGlobalsAreRestoredOnTearDown() {
-               $this->setMwGlobals( self::GLOBAL_KEY_EXISTING, 'bar' );
+       public function testSetGlobalsAreRestoredOnTearDown( $globalKey, $newValue ) {
+               $this->setMwGlobals( $globalKey, $newValue );
                $this->assertEquals(
-                       'bar',
-                       $GLOBALS[self::GLOBAL_KEY_EXISTING],
+                       $newValue,
+                       $GLOBALS[$globalKey],
                        'Global failed to correctly set'
                );
 
                $this->tearDown();
 
                $this->assertEquals(
-                       'foo',
-                       $GLOBALS[self::GLOBAL_KEY_EXISTING],
+                       self::$startGlobals[$globalKey],
+                       $GLOBALS[$globalKey],
                        'Global failed to be restored on tearDown'
                );
        }
 
        /**
+        * @dataProvider provideExistingKeysAndNewValues
+        *
         * @covers MediaWikiTestCase::stashMwGlobals
         * @covers MediaWikiTestCase::tearDown
         */
-       public function testStashedGlobalsAreRestoredOnTearDown() {
-               $this->stashMwGlobals( self::GLOBAL_KEY_EXISTING );
-               $GLOBALS[self::GLOBAL_KEY_EXISTING] = 'bar';
+       public function testStashedGlobalsAreRestoredOnTearDown( $globalKey, $newValue ) {
+               $this->stashMwGlobals( $globalKey );
+               $GLOBALS[$globalKey] = $newValue;
                $this->assertEquals(
-                       'bar',
-                       $GLOBALS[self::GLOBAL_KEY_EXISTING],
+                       $newValue,
+                       $GLOBALS[$globalKey],
                        'Global failed to correctly set'
                );
 
                $this->tearDown();
 
                $this->assertEquals(
-                       'foo',
-                       $GLOBALS[self::GLOBAL_KEY_EXISTING],
+                       self::$startGlobals[$globalKey],
+                       $GLOBALS[$globalKey],
                        'Global failed to be restored on tearDown'
                );
        }
index 545718a..926e986 100644 (file)
@@ -71,6 +71,7 @@ return array(
                        'tests/qunit/suites/resources/mediawiki/mediawiki.RegExp.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.storage.test.js',
                        '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.html.test.js',
                        'tests/qunit/suites/resources/mediawiki/mediawiki.Title.test.js',
@@ -126,6 +127,7 @@ return array(
                        'mediawiki.toc',
                        'mediawiki.Uri',
                        'mediawiki.user',
+                       'mediawiki.template.mustache',
                        'mediawiki.template',
                        'mediawiki.util',
                        'mediawiki.special.recentchanges',
index 4bcb12e..932ba7d 100644 (file)
                }
        } );
 
+       QUnit.test( 'makeTitle', 6, function ( assert ) {
+               var cases, i, title, expected,
+                       NS_MAIN = 0,
+                       NS_TALK = 1,
+                       NS_TEMPLATE = 10;
+
+               cases = [
+                       [ NS_TEMPLATE, 'Foo', 'Template:Foo' ],
+                       [ NS_TEMPLATE, 'Category:Foo', 'Template:Category:Foo' ],
+                       [ NS_TEMPLATE, 'Template:Foo', 'Template:Template:Foo' ],
+                       [ NS_TALK, 'Help:Foo', null ],
+                       [ NS_TEMPLATE, '<', null ],
+                       [ NS_MAIN, 'Help:Foo', 'Help:Foo' ]
+               ];
+
+               for ( i = 0; i < cases.length; i++ ) {
+                       title = mw.Title.makeTitle( cases[ i ][ 0 ], cases[ i ][ 1 ] );
+                       expected = cases[ i ][ 2 ];
+                       if ( expected === null ) {
+                               assert.strictEqual( title, expected );
+                       } else {
+                               assert.strictEqual( title.getPrefixedText(), expected );
+                       }
+               }
+       } );
+
        QUnit.test( 'Basic parsing', 21, function ( assert ) {
                var title;
                title = new mw.Title( 'File:Foo_bar.JPG' );
 
        } );
 
-       QUnit.test( 'getUrl', 3, function ( assert ) {
+       QUnit.test( 'getUrl', 4, function ( assert ) {
                var title;
 
                // Config
 
                title = new mw.Title( 'John Doe', 3 );
                assert.equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
+
+               title = new mw.Title( 'John Cena#And_His_Name_Is', 3 );
+               assert.equal( title.getUrl( { meme: true } ), '/wiki/User_talk:John_Cena?meme=true#And_His_Name_Is', 'title with fragment and query parameter' );
        } );
 
        QUnit.test( 'newFromImg', 44, function ( assert ) {
index 288b527..b3c4bee 100644 (file)
@@ -21,7 +21,7 @@
                        function () {
                                mw.messagePoster.factory.register( TEST_MODEL, testMessagePosterConstructor );
                        },
-                       new RegExp( 'The content model \'' + TEST_MODEL + '\' is already registered.' ),
+                       new RegExp( 'Content model "' + TEST_MODEL + '" is already registered' ),
                        'Throws exception is same model is registered a second time'
                );
        } );
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.template.mustache.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.template.mustache.test.js
new file mode 100644 (file)
index 0000000..38ae5e4
--- /dev/null
@@ -0,0 +1,32 @@
+( function ( mw ) {
+
+       QUnit.module( 'mediawiki.template.mustache', {
+               setup: function () {
+                       // Stub register some templates
+                       this.sandbox.stub( mw.templates, 'get' ).returns( {
+                               'test_greeting.mustache': '<div>{{foo}}{{>suffix}}</div>',
+                               'test_greeting_suffix.mustache': ' goodbye'
+                       } );
+               }
+       } );
+
+       QUnit.test( 'render', 2, function ( assert ) {
+               var html, htmlPartial, data, partials,
+                       template = mw.template.get( 'stub', 'test_greeting.mustache' ),
+                       partial = mw.template.get( 'stub', 'test_greeting_suffix.mustache' );
+
+               data = {
+                       foo: 'Hello'
+               };
+               partials = {
+                       suffix: partial
+               };
+
+               html = template.render( data ).html();
+               htmlPartial = template.render( data, partials ).html();
+
+               assert.strictEqual( html, 'Hello', 'Render without partial' );
+               assert.strictEqual( htmlPartial, 'Hello goodbye', 'Render with partial' );
+       } );
+
+}( mediaWiki ) );
index 0b98106..5d72179 100644 (file)
        } );
 
        QUnit.test( 'getUrl', 12, function ( assert ) {
-               // Not part of startUp module
-               mw.config.set( 'wgArticlePath', '/wiki/$1' );
-               mw.config.set( 'wgPageName', 'Foobar' );
+               mw.config.set( {
+                       wgArticlePath: '/wiki/$1',
+                       wgPageName: 'Foobar'
+               } );
 
                var href = mw.util.getUrl( 'Sandbox' );
                assert.equal( href, '/wiki/Sandbox', 'simple title' );
 
        QUnit.test( 'wikiScript', 4, function ( assert ) {
                mw.config.set( {
-                       wgScript: '/w/i.php', // customized wgScript for bug 39103
-                       wgLoadScript: '/w/l.php', // customized wgLoadScript for bug 39103
+                       // customized wgScript for T41103
+                       wgScript: '/w/i.php',
+                       // customized wgLoadScript for T41103
+                       wgLoadScript: '/w/l.php',
                        wgScriptPath: '/w'
                } );
 
index d3c4748..d3f528c 100644 (file)
@@ -21,8 +21,7 @@
                        'Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.95 Safari/537.36 OPR/15.0.1147.153',
                        'Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.57 Safari/537.36 OPR/16.0.1196.62',
                        'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36 OPR/23.0.1522.75',
-                       // Internet Explorer 8+
-                       'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)',
+                       // Internet Explorer 9+
                        'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 7.1; Trident/5.0)',
                        'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
                        'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko',
                        'Mozilla/5.0 (Linux; U; Android 2.1; en-us; Nexus One Build/ERD62) AppleWebKit/530.17 (KHTML, like Gecko) Version/4.0 Mobile Safari/530.17'
                ],
                gradeC: [
-                       // Internet Explorer < 8
+                       // Internet Explorer < 9
                        'Mozilla/2.0 (compatible; MSIE 3.03; Windows 3.1)',
                        'Mozilla/4.0 (compatible; MSIE 4.01; Windows 95)',
                        'Mozilla/4.0 (compatible; MSIE 5.0; Windows 98;)',
                        'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)',
                        'Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1)',
                        'Mozilla/5.0 (compatible; MSIE 7.0; Windows NT 6.0; en-US)',
+                       'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)',
                        // Firefox < 3
                        'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.2) Gecko/20060308 Firefox/1.5.0.2',
                        'Mozilla/5.0 (X11; U; Linux i686; nl; rv:1.8.1.1) Gecko/20070311 Firefox/2.0.0.1',
index fed0258..04b3e42 100644 (file)
--- a/thumb.php
+++ b/thumb.php
@@ -567,7 +567,6 @@ function wfExtractThumbParams( $file, $params ) {
        return null;
 }
 
-
 /**
  * Output a thumbnail generation error message
  *