Merge "Update README from mediawiki.org upstream"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 19 Jan 2016 19:21:24 +0000 (19:21 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 19 Jan 2016 19:21:24 +0000 (19:21 +0000)
417 files changed:
CREDITS
Gruntfile.js
RELEASE-NOTES-1.27
autoload.php
composer.json
docs/hooks.txt
includes/AjaxDispatcher.php
includes/Block.php
includes/DefaultSettings.php
includes/DerivativeRequest.php
includes/FauxRequest.php
includes/GlobalFunctions.php
includes/Linker.php
includes/MediaWiki.php
includes/OutputPage.php
includes/PHPVersionCheck.php
includes/PageProps.php [new file with mode: 0644]
includes/Setup.php
includes/Title.php
includes/WebRequest.php
includes/WebResponse.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/SubmitAction.php
includes/actions/UnprotectAction.php
includes/actions/UnwatchAction.php
includes/actions/WatchAction.php
includes/api/ApiCreateAccount.php
includes/api/ApiHelp.php
includes/api/ApiLogin.php
includes/api/ApiLogout.php
includes/api/ApiMain.php
includes/api/ApiQuery.php
includes/api/ApiQueryImageInfo.php
includes/api/ApiQueryMyStashedFiles.php [new file with mode: 0644]
includes/api/ApiQueryRecentChanges.php
includes/api/ApiQueryWatchlist.php
includes/api/ApiResult.php
includes/api/i18n/ast.json
includes/api/i18n/bg.json [new file with mode: 0644]
includes/api/i18n/de.json
includes/api/i18n/en.json
includes/api/i18n/fr.json
includes/api/i18n/gl.json
includes/api/i18n/he.json
includes/api/i18n/it.json
includes/api/i18n/lki.json
includes/api/i18n/my.json [new file with mode: 0644]
includes/api/i18n/nl.json
includes/api/i18n/ps.json
includes/api/i18n/qqq.json
includes/api/i18n/ru.json
includes/api/i18n/sr-ec.json
includes/api/i18n/tr.json
includes/api/i18n/uk.json
includes/api/i18n/zh-hans.json
includes/changetags/ChangeTags.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/deferred/AtomicSectionUpdate.php [new file with mode: 0644]
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/HTMLForm.php
includes/installer/Installer.php
includes/installer/MysqlUpdater.php
includes/installer/PostgresUpdater.php
includes/installer/SqliteUpdater.php
includes/installer/i18n/be-tarask.json
includes/installer/i18n/bg.json
includes/installer/i18n/es.json
includes/installer/i18n/fa.json
includes/installer/i18n/fr.json
includes/installer/i18n/gl.json
includes/installer/i18n/it.json
includes/installer/i18n/ko.json
includes/installer/i18n/lki.json
includes/installer/i18n/mk.json
includes/installer/i18n/nl.json
includes/installer/i18n/ru.json
includes/installer/i18n/sv.json
includes/installer/i18n/tt-cyrl.json
includes/installer/i18n/uk.json
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/objectcache/ObjectCacheSessionHandler.php [deleted file]
includes/page/Article.php
includes/page/WikiPage.php
includes/parser/CoreParserFunctions.php
includes/parser/ParserOutput.php
includes/search/SearchEngine.php
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/skins/BaseTemplate.php
includes/skins/SkinTemplate.php
includes/specialpage/SpecialPage.php
includes/specialpage/SpecialPageFactory.php
includes/specials/SpecialAllMessages.php
includes/specials/SpecialBlock.php
includes/specials/SpecialBotPasswords.php [new file with mode: 0644]
includes/specials/SpecialChangeContentModel.php
includes/specials/SpecialChangeEmail.php
includes/specials/SpecialChangePassword.php
includes/specials/SpecialComparePages.php
includes/specials/SpecialConfirmemail.php
includes/specials/SpecialCreateAccount.php
includes/specials/SpecialEditTags.php
includes/specials/SpecialEditWatchlist.php
includes/specials/SpecialEmailInvalidate.php
includes/specials/SpecialEmailuser.php
includes/specials/SpecialExpandTemplates.php
includes/specials/SpecialImport.php
includes/specials/SpecialListgrants.php [new file with mode: 0644]
includes/specials/SpecialListgrouprights.php
includes/specials/SpecialLockdb.php
includes/specials/SpecialLog.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialMovepage.php
includes/specials/SpecialPageLanguage.php
includes/specials/SpecialPasswordReset.php
includes/specials/SpecialPreferences.php
includes/specials/SpecialRandomrootpage.php [new file with mode: 0644]
includes/specials/SpecialRedirect.php
includes/specials/SpecialResetTokens.php
includes/specials/SpecialRevisiondelete.php
includes/specials/SpecialRunJobs.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/upload/UploadFromUrl.php
includes/user/BotPassword.php [new file with mode: 0644]
includes/user/CentralIdLookup.php
includes/user/User.php
includes/utils/MWGrants.php [new file with mode: 0644]
includes/utils/MWRestrictions.php [new file with mode: 0644]
languages/i18n/ar.json
languages/i18n/arz.json
languages/i18n/ast.json
languages/i18n/az.json
languages/i18n/azb.json
languages/i18n/ba.json
languages/i18n/bar.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/bg.json
languages/i18n/bn.json
languages/i18n/br.json
languages/i18n/bs.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/de.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/fa.json
languages/i18n/fi.json
languages/i18n/fr.json
languages/i18n/gl.json
languages/i18n/gsw.json
languages/i18n/gu.json
languages/i18n/he.json
languages/i18n/hi.json
languages/i18n/hr.json
languages/i18n/hu.json
languages/i18n/id.json
languages/i18n/is.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/ka.json
languages/i18n/kk-cyrl.json
languages/i18n/ko.json
languages/i18n/krc.json
languages/i18n/ksh.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/nap.json
languages/i18n/nb.json
languages/i18n/ne.json
languages/i18n/nl.json
languages/i18n/pl.json
languages/i18n/ps.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ro.json
languages/i18n/ru.json
languages/i18n/sa.json
languages/i18n/sk.json
languages/i18n/sl.json
languages/i18n/so.json
languages/i18n/sr-ec.json
languages/i18n/sr-el.json
languages/i18n/sv.json
languages/i18n/te.json
languages/i18n/th.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/uk.json
languages/i18n/ur.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/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
maintenance/archives/patch-bot_passwords.sql [new file with mode: 0644]
maintenance/importTextFiles.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/postgres/archives/patch-bot_passwords.sql [new file with mode: 0644]
maintenance/postgres/tables.sql
maintenance/tables.sql
resources/Resources.php
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/mediawiki.less/mediawiki.mixins.less
resources/src/mediawiki.less/mediawiki.mixins.rotation.less
resources/src/mediawiki.skinning/content.parsoid.less
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.preferences.js
resources/src/mediawiki/mediawiki.ForeignStructuredUpload.BookletLayout.js
resources/src/mediawiki/mediawiki.Upload.BookletLayout.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
tests/TestsAutoLoader.php
tests/phpunit/MediaWikiTestCase.php
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/context/RequestContextTest.php
tests/phpunit/includes/jobqueue/JobQueueMemoryTest.php [new file with mode: 0644]
tests/phpunit/includes/page/WikiPageTest.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/upload/UploadFromUrlTest.php
tests/phpunit/includes/user/BotPasswordTest.php [new file with mode: 0644]
tests/phpunit/includes/user/UserTest.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/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/suites/UploadFromUrlTestSuite.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)
index 32f2939..def80ea 100644 (file)
@@ -62,6 +62,30 @@ production.
   $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
@@ -106,6 +130,20 @@ production.
 * 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 ===
 
@@ -119,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 ====
 
@@ -141,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.
@@ -239,11 +281,17 @@ changes to languages because of Phabricator reports.
 * 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 1f8c93a..037fa5d 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',
@@ -610,6 +613,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',
@@ -730,11 +734,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',
@@ -778,6 +784,19 @@ $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',
@@ -862,7 +881,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',
@@ -905,6 +923,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',
@@ -1146,6 +1165,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',
@@ -1166,6 +1186,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',
@@ -1191,6 +1212,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',
index 89ad8a6..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",
@@ -31,6 +31,7 @@
                "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",
index 135a113..2b5e1e0 100644 (file)
@@ -741,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).
@@ -1645,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
@@ -2574,6 +2580,20 @@ $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
@@ -3292,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
@@ -3384,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 7b287dc..c017c6c 100644 (file)
@@ -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
+                               );
+                       }
+               ) );
        }
 
        /**
index d46bc03..9e945ea 100644 (file)
@@ -2181,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.
  */
@@ -2316,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
  */
@@ -4660,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 }
 
 /************************************************************************//**
@@ -4871,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;
@@ -5362,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
 
 /************************************************************************//**
index dda1358..4c149ae 100644 (file)
@@ -61,6 +61,10 @@ class DerivativeRequest extends FauxRequest {
                return $this->base->getAllHeaders();
        }
 
+       public function getSession() {
+               return $this->base->getSession();
+       }
+
        public function getSessionData( $key ) {
                return $this->base->getSessionData( $key );
        }
index 888f853..f049d2e 100644 (file)
@@ -23,6 +23,8 @@
  * @file
  */
 
+use MediaWiki\Session\SessionManager;
+
 /**
  * WebRequest clone which takes values from a provided array.
  *
@@ -30,7 +32,6 @@
  */
 class FauxRequest extends WebRequest {
        private $wasPosted = false;
-       private $session = array();
        private $requestUrl;
        protected $cookies = array();
 
@@ -38,7 +39,8 @@ class FauxRequest extends WebRequest {
         * @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 MediaWiki\\Session\\Session|array|null $session Session, session
+        *  data array, or null
         * @param string $protocol 'http' or 'https'
         * @throws MWException
         */
@@ -53,8 +55,16 @@ class FauxRequest extends WebRequest {
                        throw new MWException( "FauxRequest() got bogus data" );
                }
                $this->wasPosted = $wasPosted;
-               if ( $session ) {
-                       $this->session = $session;
+               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;
        }
@@ -140,10 +150,6 @@ class FauxRequest extends WebRequest {
                }
        }
 
-       public function checkSessionCookie() {
-               return false;
-       }
-
        /**
         * @since 1.25
         */
@@ -186,31 +192,15 @@ class FauxRequest extends WebRequest {
        }
 
        /**
-        * @param string $key
         * @return array|null
         */
-       public function getSessionData( $key ) {
-               if ( isset( $this->session[$key] ) ) {
-                       return $this->session[$key];
+       public function getSessionArray() {
+               if ( $this->sessionId !== null ) {
+                       return iterator_to_array( $this->getSession() );
                }
                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
index 26fb223..eda636a 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
@@ -3007,9 +3008,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' )
@@ -3018,83 +3022,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' );
 }
 
 /**
index 80e4c62..0dd4c47 100644 (file)
@@ -2150,13 +2150,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 {
@@ -2306,15 +2306,18 @@ class Linker {
 
        /**
         * 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 3b5a1b1..0e3a734 100644 (file)
@@ -458,7 +458,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 +470,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 +499,6 @@ class MediaWiki {
                        $output->setStatusCode( 404 );
                        $output->showErrorPage( 'nosuchaction', 'nosuchactiontext' );
                }
-
        }
 
        /**
@@ -562,15 +566,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 +631,7 @@ class MediaWiki {
        }
 
        private function main() {
-               global $wgTitle, $wgTrxProfilerLimits;
+               global $wgTitle;
 
                $request = $this->context->getRequest();
 
@@ -647,13 +655,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 +673,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.
                                (
index 97165b4..93ba702 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Session\SessionManager;
 use WrappedString\WrappedString;
 
 /**
@@ -1977,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' )
                        );
@@ -2033,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 ) );
        }
 
@@ -2050,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;
@@ -2173,8 +2179,8 @@ 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
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 2723258..e962a49 100644 (file)
@@ -497,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 );
@@ -523,13 +538,15 @@ 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 );
@@ -655,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' );
 
 /**
@@ -681,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
  */
@@ -701,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
  */
@@ -737,6 +785,16 @@ 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;
 
index 3f880f5..e549037 100644 (file)
@@ -3020,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
+                               );
+                       }
+               ) );
        }
 
        /**
@@ -3547,7 +3549,7 @@ class Title {
                if ( $pageLang->hasVariants() ) {
                        $variants = $pageLang->getVariants();
                        foreach ( $variants as $vCode ) {
-                               $urls[] = $this->getInternalURL( '', $vCode );
+                               $urls[] = $this->getInternalURL( $vCode );
                        }
                }
 
@@ -4629,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 7b76592..7306105 100644 (file)
@@ -23,6 +23,8 @@
  * @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
@@ -63,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 );
@@ -638,18 +647,44 @@ 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 ) {
+                       return SessionManager::singleton()->getSessionById( (string)$this->sessionId, false, $this );
+               }
+
+               $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;
        }
 
        /**
@@ -907,26 +942,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 );
        }
 
        /**
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 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 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 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 b1942bc..9bbb0b0 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' );
                }
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 ef9f901..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" );
index d12a68f..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',
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 ) {
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 5426fb8..49d4c8c 100644 (file)
@@ -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 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 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 d86a1db..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+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+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-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+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-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-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-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.",
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 b386905..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.",
index a878f3b..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.",
index 5b54d0c..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+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 cfa1417..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.",
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 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 31541a5..804a8d6 100644 (file)
        "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}}",
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 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.",
index ebe0571..8698dc8 100644 (file)
@@ -9,7 +9,8 @@
                        "Umherirrender",
                        "Macofe",
                        "Mix Gerder",
-                       "Piramidion"
+                       "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>.",
index c7c6a9c..fffe170 100644 (file)
@@ -74,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": "è¦\81å¼\80å§\8bæ\9e\9a举ç\9a\84æ\97¶é\97´æ\88³ã\80\82å\9c¨å¤\84ç\90\86修订IDå\88\97表æ\97¶æ\97 è§\86。",
-       "apihelp-query+deletedrevisions-param-end": "è¦\81å\81\9cæ­¢æ\9e\9a举ç\9a\84æ\97¶é\97´æ\88³ã\80\82å\9c¨å¤\84ç\90\86修订IDå\88\97表æ\97¶æ\97 è§\86。",
+       "apihelp-query+deletedrevisions-param-start": "è¦\81å¼\80å§\8bæ\9e\9a举ç\9a\84æ\97¶é\97´æ\88³ã\80\82å½\93å¤\84ç\90\86修订IDå\88\97表æ\97¶ä¼\9a被忽ç\95¥。",
+       "apihelp-query+deletedrevisions-param-end": "è¦\81å\81\9cæ­¢æ\9e\9a举ç\9a\84æ\97¶é\97´æ\88³ã\80\82å½\93å¤\84ç\90\86修订IDå\88\97表æ\97¶ä¼\9a被忽ç\95¥。",
        "apihelp-query+deletedrevisions-param-tag": "只列出被此标签标记的修订。",
        "apihelp-query+deletedrevisions-param-user": "只列出此用户做出的修订。",
        "apihelp-query+deletedrevisions-param-excludeuser": "不要列出此用户做出的修订。",
        "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+watchlistraw-param-fromtitle": "要列举的起始标题(带名字空间前缀)。",
        "apihelp-query+watchlistraw-param-totitle": "要列举的最终标题(带名字空间前缀)。",
        "apihelp-query+watchlistraw-example-simple": "列出当前用户的监视列表中的页面。",
-       "apihelp-query+watchlistraw-example-generator": "获取在此用户的监视列表中的页面的信息。",
+       "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上传)。",
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 36c644a..16f11ee 100644 (file)
@@ -513,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()
                );
        }
@@ -541,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." );
@@ -563,23 +565,37 @@ 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
+                               $session = MediaWiki\Session\SessionManager::singleton()
+                                       ->getSessionById( $params['sessionId'] );
+                       }
+
+                       // 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 3cac22a..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() );
        }
index 37c9d6f..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
         *
@@ -282,9 +289,19 @@ abstract class DatabaseBase implements IDatabase {
         * @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;
        }
 
        /**
@@ -377,6 +394,10 @@ abstract class DatabaseBase implements IDatabase {
                return $this->mTrxLevel ? $this->mTrxWriteDuration : false;
        }
 
+       public function pendingWriteCallers() {
+               return $this->mTrxLevel ? $this->mTrxWriteCallers : array();
+       }
+
        public function isOpen() {
                return $this->mOpened;
        }
@@ -881,6 +902,7 @@ abstract class DatabaseBase implements IDatabase {
 
                if ( $isWriteQuery && $this->mTrxLevel ) {
                        $this->mTrxWriteDuration += $queryRuntime;
+                       $this->mTrxWriteCallers[] = $fname;
                }
 
                return $res;
@@ -2600,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
@@ -2675,14 +2698,10 @@ abstract class DatabaseBase implements IDatabase {
                        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!" );
                        }
                }
 
index 5bcfd66..3a8f737 100644 (file)
@@ -654,6 +654,13 @@ abstract class DatabaseMysqlBase extends Database {
        protected function getLagFromPtHeartbeat() {
                $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
                }
 
@@ -666,6 +673,13 @@ abstract class DatabaseMysqlBase extends Database {
                        return max( $nowUnix - $timeUnix, 0.0 );
                }
 
+               wfLogDBError(
+                       "Unable to find pt-heartbeat row for {db_server}",
+                       $this->getLogContext( array(
+                               'method' => __METHOD__
+                       ) )
+               );
+
                return false;
        }
 
index 31b2758..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
index 4aed718..37ab9ca 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' );
        }
 
        /**
@@ -205,6 +216,8 @@ abstract class LBFactory {
         * @param string $fname Caller name
         */
        public function commitAll( $fname = __METHOD__ ) {
+               $this->logMultiDbTransaction();
+
                $start = microtime( true );
                $this->forEachLBCallMethod( 'commitAll', array( $fname ) );
                $timeMs = 1000 * ( microtime( true ) - $start );
@@ -217,6 +230,8 @@ abstract class LBFactory {
         * @param string $fname Caller name
         */
        public function commitMasterChanges( $fname = __METHOD__ ) {
+               $this->logMultiDbTransaction();
+
                $start = microtime( true );
                $this->forEachLBCallMethod( 'commitMasterChanges', array( $fname ) );
                $timeMs = 1000 * ( microtime( true ) - $start );
@@ -233,6 +248,29 @@ abstract class LBFactory {
                $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 );
+               }
+       }
+
        /**
         * Determine if any master connection has pending changes
         * @return bool
index e25499c..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
                ) );
        }
 
index 3349e1f..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
                ) );
        }
 
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 5d1b745..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 ) {
@@ -840,6 +849,7 @@ class LoadBalancer {
                $db->setLazyMasterHandle(
                        $this->getLazyConnectionRef( DB_MASTER, array(), $db->getWikiID() )
                );
+               $db->setTransactionProfiler( $this->trxProfiler );
 
                return $db;
        }
@@ -1158,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 )
+               );
        }
 }
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 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 debdeb5..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;
                                }
                        }
                }
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 b7d6f98..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
index ee7faa5..2282dc2 100644 (file)
@@ -1222,8 +1222,6 @@ class HTMLForm extends ContextSource {
         */
        public function setSubmitDestructive() {
                $this->mSubmitFlags = array( 'destructive', 'primary' );
-
-               return $this;
        }
 
        /**
@@ -1232,8 +1230,6 @@ class HTMLForm extends ContextSource {
         */
        public function setSubmitProgressive() {
                $this->mSubmitFlags = array( 'progressive', 'primary' );
-
-               return $this;
        }
 
        /**
index ea700c7..de84199 100644 (file)
@@ -169,6 +169,7 @@ abstract class Installer {
                'wgEnotifUserTalk',
                'wgEnotifWatchlist',
                'wgEmailAuthentication',
+               'wgDBname',
                'wgDBtype',
                'wgDiff3',
                'wgImageMagickConvertCommand',
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' ),
                );
        }
 
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 27a5449..f455ced 100644 (file)
        "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 наръчника].",
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 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 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 1a1cc44..bd9e762 100644 (file)
@@ -89,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 cd3403d..0f62503 100644 (file)
@@ -13,6 +13,6 @@
        "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 708536f..65ffc10 100644 (file)
@@ -81,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>.",
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 6d2ce0e..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( __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( __METHOD__ );
-                               // 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
index ade4810..28e3c40 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,55 @@ 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 $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, $result, $dataKey, $dataValue
+       ) {
+               $data = self::getSessionData( $session, $this->params['sessionKey'] );
+               $data['result'] = $result;
+               $data[$dataKey] = $dataValue;
+               self::setSessionData( $session, $this->params['sessionKey'], $data );
        }
 
        /**
         * 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 );
        }
 }
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 22e24ae..c9af075 100644 (file)
@@ -1229,7 +1229,7 @@ class Article implements Page {
         */
        public static function purgePatrolFooterCache( $articleID ) {
                $cache = ObjectCache::getMainWANInstance();
-               $cache->touchCheckKey( wfMemcKey( 'unpatrollable-page', $articleID ) );
+               $cache->delete( wfMemcKey( 'unpatrollable-page', $articleID ) );
        }
 
        /**
index 00ac127..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 );
 
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 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 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/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..c59cc96
--- /dev/null
@@ -0,0 +1,369 @@
+<?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, true );
+               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 );
+
+               // 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, true );
+               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..5743b12
--- /dev/null
@@ -0,0 +1,632 @@
+<?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 mixed
+        */
+       public function getProviderMetadata() {
+               return $this->providerMetadata;
+       }
+
+       /**
+        * 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..1c8686c
--- /dev/null
@@ -0,0 +1,997 @@
+<?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, false, $request );
+                       }
+               }
+               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, $noEmpty = 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 ( !$noEmpty && $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;
+                       }
+                       if ( $session === null ) {
+                               throw new \UnexpectedValueException(
+                                       'Can neither load the session nor create an empty session', 0, $ex
+                               );
+                       }
+               }
+
+               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 ) );
+
+               # 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 ) {
+               $blob = $this->store->get( wfMemcKey( 'MWSession', $info->getId() ) );
+
+               $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" );
+                               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" );
+                               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" );
+                               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'] );
+                                       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..67d6f5d
--- /dev/null
@@ -0,0 +1,109 @@
+<?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 $noEmpty Don't return an empty session
+        * @param WebRequest|null $request Corresponding request. Any existing
+        *  session associated with this WebRequest object will be overwritten.
+        * @return Session|null
+        */
+       public function getSessionById( $id, $noEmpty = 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() .
+                       '>';
+       }
+
+}
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 2ecbafb..1328870 100644 (file)
@@ -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 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 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 226d633..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
         *
@@ -646,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;
                }
@@ -695,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'] );
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 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 371ad19..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
index 50a48c3..fbc5984 100644 (file)
@@ -49,8 +49,8 @@ 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 = HTMLForm::factory( 'ooui', array(
                        'Page1' => array(
                                'type' => 'title',
index 37d3636..5ed33e0 100644 (file)
@@ -34,6 +34,10 @@ class EmailConfirmation extends UnlistedSpecialPage {
                parent::__construct( 'Confirmemail', 'editmyprivateinfo' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        /**
         * Main execution point
         *
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 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
         *
index 30f9d2e..b5c66ff 100644 (file)
@@ -32,6 +32,10 @@ class EmailInvalidation extends UnlistedSpecialPage {
                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.
index 3aeadc9..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 ) {
@@ -85,6 +89,7 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                        'Text' => array(
                                'type' => 'textarea',
                                'rows' => 20,
+                               'cols' => 80,
                                'label-message' => 'emailmessage',
                                'required' => true,
                        ),
@@ -98,16 +103,12 @@ class SpecialEmailUser extends UnlistedSpecialPage {
 
        public function execute( $par ) {
                $out = $this->getOutput();
-               $request = $this->getRequest();
                $out->addModuleStyles( 'mediawiki.special' );
 
                $this->mTarget = is_null( $par )
-                       ? $request->getVal( 'wpTarget', '' )
+                       ? $this->getRequest()->getVal( 'wpTarget', $this->getRequest()->getVal( 'target', '' ) )
                        : $par;
 
-               // make sure, that HTMLForm uses the correct target
-               $request->setVal( 'wpTarget', $this->mTarget );
-
                // This needs to be below assignment of $this->mTarget because
                // getDescription() needs it to determine the correct page title.
                $this->setHeaders();
@@ -138,16 +139,41 @@ class SpecialEmailUser extends UnlistedSpecialPage {
                                list( $title, $msg, $params ) = $error;
                                throw new ErrorPageError( $title, $msg, $params );
                }
+               // Got a valid target user name? Else ask for one.
+               $ret = self::getTarget( $this->mTarget );
+               if ( !$ret instanceof User ) {
+                       if ( $this->mTarget != '' ) {
+                               // Messages used here: notargettext, noemailtext, nowikiemailtext
+                               $ret = ( $ret == 'notarget' ) ? 'emailnotarget' : ( $ret . 'text' );
+                               $out->wrapWikiMsg( "<p class='error'>$1</p>", $ret );
+                       }
+                       $out->addHTML( $this->userForm( $this->mTarget ) );
 
-               // a little hack: HTMLForm will check $this->mTarget only, if the form was posted, not
-               // if the user opens Special:EmailUser/Florian (e.g.). So check, if the user did that,
-               // and show the "Send email to user" form directly, if so. Show the "enter username"
-               // form, otherwise.
-               $this->mTargetObj = self::getTarget( $this->mTarget );
-               if ( !$this->mTargetObj instanceof User ) {
-                       $this->userForm( $this->mTarget );
-               } else {
-                       $this->sendEmailForm();
+                       return;
+               }
+
+               $this->mTargetObj = $ret;
+
+               $context = new DerivativeContext( $this->getContext() );
+               $context->setTitle( $this->getPageTitle() ); // Remove subpage
+               $form = new HTMLForm( $this->getFormFields(), $context );
+               // By now we are supposed to be sure that $this->mTarget is a user name
+               $form->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() );
+               $form->setSubmitTextMsg( 'emailsend' );
+               $form->setSubmitCallback( array( __CLASS__, 'uiSubmit' ) );
+               $form->setWrapperLegendMsg( 'email-legend' );
+               $form->loadData();
+
+               if ( !Hooks::run( 'EmailUserForm', array( &$form ) ) ) {
+                       return;
+               }
+
+               $result = $form->show();
+
+               if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
+                       $out->setPageTitle( $this->msg( 'emailsent' ) );
+                       $out->addWikiMsg( 'emailsenttext', $this->mTarget );
+                       $out->returnToMain( false, $this->mTargetObj->getUserPage() );
                }
        }
 
@@ -238,63 +264,31 @@ class SpecialEmailUser extends UnlistedSpecialPage {
         * @return string Form asking for user name.
         */
        protected function userForm( $name ) {
-               $form = HTMLForm::factory( 'ooui', array(
-                       'Target' => array(
-                               'type' => 'user',
-                               'exists' => true,
-                               'label' => $this->msg( 'emailusername' )->text(),
-                               'id' => 'emailusertarget',
-                               'autofocus' => true,
-                               'value' => $name,
-                       ),
-               ), $this->getContext() );
-
-               $form
-                       ->setMethod( 'post' )
-                       ->setSubmitCallback( array( $this, 'sendEmailForm' ) )
-                       ->setSubmitProgressive()
-                       ->setId( 'askusername' )
-                       ->addHiddenField( 'title', $this->getPageTitle()->getPrefixedText() )
-                       ->setWrapperLegendMsg( 'emailtarget' )
-                       ->setSubmitTextMsg( 'emailusernamesubmit' )
-                       ->show();
-       }
-
-       public function sendEmailForm() {
-               $out = $this->getOutput();
-
-               $ret = $this->mTargetObj;
-               if ( !$ret instanceof User ) {
-                       if ( $this->mTarget != '' ) {
-                               // Messages used here: notargettext, noemailtext, nowikiemailtext
-                               $ret = ( $ret == 'notarget' ) ? 'emailnotarget' : ( $ret . 'text' );
-                               return Status::newFatal( $ret );
-                       }
-                       return false;
-               }
-
-               $context = new DerivativeContext( $this->getContext() );
-               $context->setTitle( $this->getPageTitle() ); // Remove subpage
-               $form = HTMLForm::factory( 'ooui', $this->getFormFields(), $context );
-               // By now we are supposed to be sure that $this->mTarget is a user name
-               $form->addPreText( $this->msg( 'emailpagetext', $this->mTarget )->parse() );
-               $form->setSubmitTextMsg( 'emailsend' );
-               $form->setSubmitCallback( array( __CLASS__, 'uiSubmit' ) );
-               $form->setWrapperLegendMsg( 'email-legend' );
-               $form->loadData();
-
-               if ( !Hooks::run( 'EmailUserForm', array( &$form ) ) ) {
-                       return false;
-               }
-
-               $result = $form->show();
-
-               if ( $result === true || ( $result instanceof Status && $result->isGood() ) ) {
-                       $out->setPageTitle( $this->msg( 'emailsent' ) );
-                       $out->addWikiMsg( 'emailsenttext', $this->mTarget );
-                       $out->returnToMain( false, $ret->getUserPage() );
-               }
-               return true;
+               $this->getOutput()->addModules( 'mediawiki.userSuggest' );
+               $string = Xml::openElement(
+                       'form',
+                       array( 'method' => 'get', 'action' => wfScript(), 'id' => 'askusername' )
+               ) .
+                       Html::hidden( 'title', $this->getPageTitle()->getPrefixedText() ) .
+                       Xml::openElement( 'fieldset' ) .
+                       Html::rawElement( 'legend', null, $this->msg( 'emailtarget' )->parse() ) .
+                       Xml::inputLabel(
+                               $this->msg( 'emailusername' )->text(),
+                               'target',
+                               'emailusertarget',
+                               30,
+                               $name,
+                               array(
+                                       'class' => 'mw-autocomplete-user',  // used by mediawiki.userSuggest
+                                       'autofocus' => true,
+                               )
+                       ) .
+                       ' ' .
+                       Xml::submitButton( $this->msg( 'emailusernamesubmit' )->text() ) .
+                       Xml::closeElement( 'fieldset' ) .
+                       Xml::closeElement( 'form' ) . "\n";
+
+               return $string;
        }
 
        /**
index 14d8675..00f6609 100644 (file)
@@ -168,35 +168,35 @@ class SpecialExpandTemplates extends SpecialPage {
                        'input' => array(
                                'type' => 'textarea',
                                'name' => 'wpInput',
-                               'label-message' => 'expand_templates_input',
+                               'label' => $this->msg( 'expand_templates_input' )->text(),
                                'rows' => 10,
                                'default' => $input,
                                'id' => 'input',
                        ),
                        'removecomments' => array(
                                'type' => 'check',
-                               'label-message' => 'expand_templates_remove_comments',
+                               'label' => $this->msg( 'expand_templates_remove_comments' )->text(),
                                'name' => 'wpRemoveComments',
                                'id' => 'removecomments',
                                'default' => $this->removeComments,
                        ),
                        'removenowiki' => array(
                                'type' => 'check',
-                               'label-message' => 'expand_templates_remove_nowiki',
+                               'label' => $this->msg( 'expand_templates_remove_nowiki' )->text(),
                                'name' => 'wpRemoveNowiki',
                                'id' => 'removenowiki',
                                'default' => $this->removeNowiki,
                        ),
                        'generate_xml' => array(
                                'type' => 'check',
-                               'label-message' => 'expand_templates_generate_xml',
+                               'label' => $this->msg( 'expand_templates_generate_xml' )->text(),
                                'name' => 'wpGenerateXml',
                                'id' => 'generate_xml',
                                'default' => $this->generateXML,
                        ),
                        'generate_rawhtml' => array(
                                'type' => 'check',
-                               'label-message' => 'expand_templates_generate_rawhtml',
+                               'label' => $this->msg( 'expand_templates_generate_rawhtml' )->text(),
                                'name' => 'wpGenerateRawHtml',
                                'id' => 'generate_rawhtml',
                                'default' => $this->generateRawHtml,
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();
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 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 0c0d929..4bdad79 100644 (file)
@@ -62,6 +62,10 @@ class MovePageForm extends UnlistedSpecialPage {
                parent::__construct( 'Movepage' );
        }
 
+       public function doesWrites() {
+               return true;
+       }
+
        public function execute( $par ) {
                $this->useTransactionalTimeLimit();
 
index 7509bbc..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' );
        }
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 3fa5fd5..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();
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 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 f776832..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();
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 b4470f5..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 */
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 cf94e50..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' )
                                                ) .
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 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..286538b
--- /dev/null
@@ -0,0 +1,429 @@
+<?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 ) {
+               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 ) {
+               $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 ) {
+               $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 b406bac..6ec0ff0 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;
        }
 
@@ -1069,8 +1085,9 @@ class User implements IDBAccessObject {
                $this->mOptionOverrides = null;
                $this->mOptionsLoaded = false;
 
-               $loggedOut = $this->getRequest()->getCookie( 'LoggedOut' );
-               if ( $loggedOut !== null ) {
+               $request = $this->getRequest();
+               $loggedOut = $request ? $request->getSession()->getLoggedOutTimestamp() : 0;
+               if ( $loggedOut !== 0 ) {
                        $this->mTouched = wfTimestamp( TS_MW, $loggedOut );
                } else {
                        $this->mTouched = '1'; # Allow any pages to be cached
@@ -1115,84 +1132,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;
        }
 
        /**
@@ -1555,9 +1520,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;
@@ -1576,7 +1539,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' );
@@ -2442,6 +2404,9 @@ class User implements IDBAccessObject {
                        ),
                        __METHOD__
                );
+
+               // When the main password is changed, invalidate all bot passwords too
+               BotPassword::invalidateAllPasswordsForUser( $this->getName() );
        }
 
        /**
@@ -3015,6 +2980,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 ) );
@@ -3276,13 +3247,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 );
@@ -3502,6 +3466,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 +3482,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 +3492,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 +3501,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 +3512,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 +3523,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 +3534,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 +3542,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 +3584,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 +4104,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 +4503,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 +4539,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/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 4bce5b1..31bb696 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": "سجل صلاحيات المستخدمين",
index 14d07ba..1c978ee 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": "تعديل الصفحات المحمية (من غير الحماية المتضمنة)",
index 3520f64..69fa33f 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",
        "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",
        "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",
        "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",
        "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",
        "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",
        "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.",
        "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."
 }
index 3ae26e9..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üş",
        "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",
        "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": "Dəyişiklikdən sonrakı ölçü: $1 bayt",
        "newsectionsummary": "/* $1 */ yeni bölmə",
        "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ı",
        "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",
        "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",
index 988c912..c05a3ae 100644 (file)
        "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 تاریخینه کیمی سون حال",
        "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": "بو صحیفه‌نی اوخو",
        "statistics-header-hooks": "باشقا آمارلار",
        "statistics-articles": "مقاله‌لر",
        "statistics-pages": "صفحه‌لر:",
-       "statistics-pages-desc": "بو ویکی‌ده بوتون صحیفه‌لر، او جومله‌دن دانیشیق صحیفه‌لری، یول‌لاندیرمالار و سونرا.",
+       "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قئید نؤوو، ایستیفاده‌چی آدی و یا تأثیر ائدیلمیش صحیفنی سئچمکله داها ایسپئسیفیک اولا بیلرسینیز.",
        "move-watch": "بۇ صفحه‌نی ایزله",
        "movepagebtn": "صفحه‌نین آدینی دَییش",
        "pagemovedsub": "یئردییشمه ائدیلمیش‌دیر",
-       "movepage-moved": "'\"$1\" صحیفه‌سی \"$2\" صحیفه‌سینه یئرلشدیریلمیشدیر",
+       "movepage-moved": "'''«$1»، «$2» صفحه‌سینه داشیندی'''",
        "movepage-moved-redirect": "یؤنلندیرمه یارادیلدی.",
        "movepage-moved-noredirect": "یؤنلندیرمه‌نین یارادیلماسینین قارشییس آلیندی.",
        "articleexists": "بو آددا صحیفه آرتیق مؤوجوددور و یا سیزین سئچدیگینیز آد اویغون دئییل.\nزحمت اولماسا باشقا آد سئچین.",
        "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_submit": "گئت",
        "table_pager_empty": "نتیجه سیز",
        "autosumm-blank": "صفحه‌‌نی بوْشالتدی",
-       "autosumm-replace": "صحیفه‌‌نین مظمونو ' $1' يازیسی ایله ديَیشدیریلدی",
+       "autosumm-replace": "صفحه‌‌نین مضمونونو ' $1' يازیسی ایله ديَیشدیریلدی",
        "autoredircomment": "[[$1]] صفحه‌‌سینه یوْللاندیریلیر",
        "autosumm-new": "صفحه‌‌نی ' $1' ایله ياراتدی",
        "autosumm-newblank": "بوش صحفه یاراندی",
        "specialpages-group-changes": "سون دییشیک‌لیک‌لر و قئیدلر",
        "specialpages-group-media": "مئدیا مروزه‌لری و یوکلمه‌لر",
        "specialpages-group-users": "ایشلدن‌لر و حاقلار",
-       "specialpages-group-highuse": "ان چوْخ ایشله‌نن صفحه‌لر",
+       "specialpages-group-highuse": "ان چوْخ ایشلدیلمیش صفحه‌لر",
        "specialpages-group-pages": "صفحه‌لرین لیست‌لری",
        "specialpages-group-pagetools": "صفحه آلتلری",
        "specialpages-group-wiki": "بیلگیلر و آلتلر",
-       "specialpages-group-redirects": "خصوصی ایستیقامتلندیرمه صحیفه‌لری",
+       "specialpages-group-redirects": "مخصوص یوْللاندیرما صفحه‌لری",
        "specialpages-group-spam": "هرزه یازماق آلت‌لری",
        "blankpage": "بوش صفحه",
        "intentionallyblankpage": "بو صحیفه خصوصیله بوش‌دور.",
        "logentry-newusers-create": "$1 ایشلدن حسابی {{GENDER:$2|یارادیلدی}}",
        "logentry-newusers-create2": "$1 ایستیفاده‌چی، $3 حسابی {{GENDER:$2|یاراتدی}}",
        "logentry-newusers-byemail": "$3 ایستیفاده‌چی حسابی، $1 ایله {{GENDER:$2|یارادیلیب}} و رمز، ایمیل ایله گؤندریلیب‌دیر",
-       "logentry-newusers-autocreate": "$1 Ø§Û\8cستÛ\8cÙ\81ادÙ\87â\80\8cÚ\86Û\8c Ø­Ø³Ø§Ø¨Û\8c Ø§Ù\88توماتیک {{GENDER:$2|یارادیلدی}}",
+       "logentry-newusers-autocreate": "$1 Ø§Û\8cØ´Ù\84دÙ\86 Ø­Ø³Ø§Ø¨Û\8c Ø§Ù\88Ù\92توماتیک {{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|آرتیریلدی}}",
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 fd6b239..2b3ea14 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-label-create": "Стварыць",
+       "botpasswords-label-update": "Абнавіць",
+       "botpasswords-label-cancel": "Скасаваць",
+       "botpasswords-label-delete": "Выдаліць",
+       "botpasswords-label-resetpassword": "Ачысьціць пароль",
        "resetpass_forbidden": "Пароль ня можа быць зьменены",
        "resetpass-no-info": "Для непасрэднага доступу да гэтай старонкі Вам неабходна ўвайсьці ў сыстэму.",
        "resetpass-submit-loggedin": "Зьмяніць пароль",
        "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": "Журнал правоў удзельнікаў",
        "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": "Сьпіс усіх спэцыяльных старонак",
        "newimages-legend": "Фільтар",
        "newimages-label": "Назва файла (альбо яе частка):",
        "newimages-showbots": "Паказаць загружаныя робатамі",
+       "newimages-hidepatrolled": "Схаваць патруляваныя загрузкі",
        "noimages": "Выявы адсутнічаюць.",
        "ilsubmit": "Шукаць",
        "bydate": "па даце",
        "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-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» ня можа быць выдаленая.",
index 2144684..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 не існуе.",
index 0f3eefe..0288531 100644 (file)
        "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": "Съответствия в заглавията на страници",
        "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 файловете на други потребители",
        "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-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 не съществува.",
        "unblocked": "[[User:$1|$1]] беше отблокиран.",
        "unblocked-range": "$1 беше отблокиран",
        "unblocked-id": "Блок № $1 беше премахнат",
+       "unblocked-ip": "[[Special:Contributions/$1|$1]] е отблокиран.",
        "blocklist": "Блокирани потребители",
        "ipblocklist": "Блокирани потребители",
        "ipblocklist-legend": "Откриване на блокиран потребител",
        "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-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": "Отваряне",
        "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 (върху пренасочване)",
        "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": "ГГГГ-ММ",
        "mw-widgets-titleinput-description-new-page": "страницата все още не съществува",
index 3c1156c..55f58ad 100644 (file)
        "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": "ব্যবহারকারীর অধিকার লগ",
        "listgrouprights-removegroup-self-all": "নিজের অ্যাকাউন্ট থেকে সকল দল অপসারণ",
        "listgrouprights-namespaceprotection-header": "নামস্থান নিষেধাজ্ঞাসমূহ",
        "listgrouprights-namespaceprotection-namespace": "নামস্থান",
+       "listgrants-rights": "অধিকার",
        "trackingcategories": "বিষয়শ্রেণীসমূহ অনুসরণ করা হচ্ছে",
        "trackingcategories-msg": "বিষয়শ্রেণী অনুসরণ করা হচ্ছে",
        "trackingcategories-name": "বার্তা নাম",
index b00dab0..2c63c4c 100644 (file)
        "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",
index 2833ee9..e0b8992 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\"",
        "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",
        "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",
        "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}}",
index 2461ed3..d5ee305 100644 (file)
        "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": "Декъашхочун бакъона тéптар",
        "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": "Ӏалашъян",
        "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».",
        "listgrouprights-namespaceprotection-header": "ЦӀеран анан бехкам",
        "listgrouprights-namespaceprotection-namespace": "ЦӀерийн ана",
        "listgrouprights-namespaceprotection-restrictedto": "Декъашхочун хийцамаш бан таро хуьлуьйту бакъонаш",
+       "listgrants-grant": "Бакъо",
+       "listgrants-rights": "Бакъонаш",
        "trackingcategories": "Хьожуш йолу категореш",
        "trackingcategories-summary": "ХӀокху агӀонгахь ю хьожуш йолу категореш, MediaWikiс тӀеюзаш ю уьш. {{ns:8}} цӀерийн меттигера системин хаам хийцина цера цӀерш хийца йиш ю.",
        "trackingcategories-msg": "Категореш зер",
        "watchlistall2": "массо",
        "watchlist-hide": "Къайлаяккха",
        "watchlist-submit": "Гайта",
+       "wlshowtime": "Гаран хенан мур:",
        "wlshowhideminor": "жима нисдарш",
        "wlshowhidebots": "Боташ",
        "wlshowhideliu": "ДӀабазбелла декъашхой",
        "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он чулацам",
        "newimages-summary": "ХӀокху белхан агӀона чохь гойтуш ю дукха хан йоццуш чуяьхна файлаш.",
        "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 шо}}",
index dce0e3d..e9e7f13 100644 (file)
        "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'''ی وێبگەڕەکەت کلیک بکە.",
        "right-blockemail": "بەربەستنی بەکارھێنەرێک لە ناردنی ئیمەیل",
        "right-hideuser": "بەربەستنی ناوێکی بەکارهێنەری، شاردنەوەی لەبەر چاوی ھەمووان",
        "right-ipblock-exempt": "لادان لە بەربەستنەکانی ئایپی، بەربەستنە خۆگەڕەکان و بەربەستنەکانی زنجیرە",
-       "right-proxyunbannable": "لادان لە بەربەستنە خۆگەڕەکانی پرۆکسییەکان",
        "right-unblockself": "کردنەوەی خۆیان",
        "right-protect": "گۆڕینی ئاستەکانی پاراستن و دەستکاریی پەڕە پارێزراوە تاڤگەیییەکان",
        "right-editprotected": "دەستکاریی پەڕە پارێزراوەکانی وەک «{{int:protect-level-sysop}}»",
        "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": "لۆگی بارکردن",
        "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": "پەیامنێری سیستەم",
        "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 زۆر درێژە]",
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 8e1bd3e..0fdbe98 100644 (file)
        "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",
        "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-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",
        "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",
        "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",
        "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.",
        "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",
        "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?",
        "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šů.",
        "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",
        "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."
 }
index 7b44872..c3f4ef9 100644 (file)
        "otherlanguages": "дроугꙑ ѩꙁꙑкꙑ",
        "redirectedfrom": "(прѣнаправлѥниѥ отъ ⁖ $1 ⁖)",
        "redirectpagesub": "прѣнаправлѥниѥ",
+       "redirectto": "прѣнаправлѥниѥ къ :",
        "lastmodifiedat": "страницѧ послѣдьнꙗ мѣна сътворѥна $2 · $1 бѣ ⁙",
        "jumpto": "прѣиди къ :",
        "jumptonavigation": "плаваниѥ",
        "media_tip": "дѣла съвѧꙁь",
        "sig_tip": "твои аѵтографъ и нꙑнѣшьна врѣмѧ и дьнь",
        "summary": "опьсаниѥ :",
+       "subject": "ѳєма :",
        "minoredit": "малаꙗ мѣна",
        "watchthis": "си страницѧ блюдєниѥ",
        "savearticle": "съхранѥниѥ",
        "recentchangeslinked": "съвѧꙁанꙑ страницѧ",
        "recentchangeslinked-feed": "съвѧꙁанꙑ страницѧ",
        "recentchangeslinked-toolbox": "съвѧꙁанꙑ страницѧ",
+       "recentchangeslinked-title": "съвѧꙁанꙑ съ ⁖ $1 ⁖ мѣнꙑ",
        "recentchangeslinked-page": "страницѧ имѧ :",
        "upload": "положєниѥ дѣла",
        "uploadbtn": "положєниѥ дѣла",
        "invert": "обрати иꙁборъ",
        "namespace_association": "съвѧꙁанꙑ имєнъ просторꙑ",
        "blanknamespace": "(главьно)",
-       "contributions": "{{GENDER:$1|польꙃєватєлꙗ}} добродѣꙗниꙗ",
+       "contributions": "{{GENDER:$1|польꙃєватєлꙗ|польꙃєватєлицѧ}} добродѣꙗниꙗ",
        "contributions-title": "польꙃєватєлꙗ ⁖ $1 ⁖ добродѣꙗниꙗ",
        "mycontris": "добродѣꙗниꙗ",
        "anoncontribs": "добродѣꙗниꙗ",
        "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-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": "оꙁначи ꙗко малоу мѣноу",
index f47d451..9352638 100644 (file)
        "rollback": "Тӳрлетнисене каялла куçарасси",
        "rollbacklink": "каялла тавăр",
        "rollbackfailed": "Каялла куçарнă çухна йăнăш тухнă",
-       "protectlogpage": "Ð¥Ó³Ñ\82Ä\95лев Ð¶Ñ\83Ñ\80налĕ",
+       "protectlogpage": "Ð¥Ó³Ñ\82Ä\95ленин Ð»Ð¾Ð³ĕ",
        "protectlogtext": "Аяларах эсир статьясене хӳтĕленин тата хӳтĕлев паллине пăрахаçланин журналне куратăр.",
        "protectedarticle": "«[[$1]]» страницăна хӳтĕлерĕмĕр",
        "unprotectedarticle": "«[[$1]]» страницăн хӳтĕлевне пăрахăçланă",
index bd7ba5a..4c99d87 100644 (file)
@@ -84,7 +84,8 @@
                        "J. 'mach' wust",
                        "R4c0r",
                        "MGChecker",
-                       "FriedhelmW"
+                       "FriedhelmW",
+                       "Schniggendiller"
                ]
        },
        "tog-underline": "Links unterstreichen:",
        "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",
        "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",
        "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",
        "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",
        "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",
        "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",
        "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-hidepatrolled": "Kontrollierte Uploads ausblenden",
+       "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?",
        "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.",
        "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."
 }
index f80dacb..6db5b77 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) ζήτησε την επαναφορά του κωδικού σας σε {{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": "Επεξεργασία προστατευμένων σελίδων (χωρίς διαδοχική προστασία)",
        "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 b436f90..a54a88b 100644 (file)
        "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",
        "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",
        "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",
        "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.",
        "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",
        "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",
        "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.",
        "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 f2d344a..cc9989f 100644 (file)
@@ -52,6 +52,7 @@
        "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",
@@ -81,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.",
        "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)",
index 97a8bd6..0429dd2 100644 (file)
        "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",
        "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",
        "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.",
        "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-disabled": "Las contraseñas de bot están desactivadas.",
+       "botpasswords-existing": "Contraseñas de bots existentes",
+       "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-restrictions": "Restricciones de uso:",
+       "botpasswords-label-grants-column": "Concedido",
+       "botpasswords-bad-appid": "El nombre del bot \"$1\" no es válido.",
+       "botpasswords-created-title": "Se creó la contraseña de bot",
+       "botpasswords-created-body": "La contraseña de bot \"$1\" se creó correctamente.",
+       "botpasswords-no-provider": "BotPasswordsSessionProvider no está disponible.",
+       "botpasswords-invalid-name": "El nombre de usuario especificado no contiene el separador de contraseña de bot (\"$1\").",
        "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",
        "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",
        "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",
        "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:",
        "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.",
        "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?",
        "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.",
        "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",
        "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."
 }
index d334122..2ea85a3 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",
index 91da6b7..b80513d 100644 (file)
        "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.",
index 4c31544..cf32786 100644 (file)
        "october-date": "$1 اکتبر",
        "november-date": "$1 نوامبر",
        "december-date": "$1 دسامبر",
+       "period-am": "صبح",
+       "period-pm": "عصر",
        "pagecategories": "{{PLURAL:$1|رده|رده‌ها}}",
        "category_header": "صفحه‌های ردهٔ «$1»",
        "subcategories": "زیررده‌ها",
        "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-viewdeleted": "مشاهدهٔ اطلاعات حذف",
+       "grant-viewmywatchlist": "مشاهدۀ فهرست پیگیری‌هایتان",
        "newuserlogpage": "سیاههٔ ایجاد کاربر",
        "newuserlogpagetext": "این سیاهه‌ای از نام‌های کاربری تازه‌ساخته‌شده است.",
        "rightslog": "سیاههٔ اختیارات کاربر",
        "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": "ردهٔ ردیابی",
        "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> نباید خالی باشند.",
index 1962b3f..8622705 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",
        "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ä",
        "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."
 }
index a316142..ace6361 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",
        "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",
        "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",
        "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",
        "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é",
        "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",
        "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) ?",
        "version-libraries-license": "Licence",
        "version-libraries-description": "Description",
        "version-libraries-authors": "Auteurs",
-       "redirect": "Redirigé par fichier, utilisateur, page ou ID de révision",
+       "redirect": "Redirigé 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.",
        "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."
 }
index 93fd3a7..1994326 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",
        "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",
        "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",
        "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",
        "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",
        "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",
        "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)",
        "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",
        "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",
        "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).",
        "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",
        "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."
 }
index 41f1ec9..53a6c25 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",
index d67eb56..5112bf8 100644 (file)
        "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}}\" તરીકે ફેરફાર કરો",
        "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": "પ્રસ્થાન",
        "tooltip-pt-createaccount": "તમને ખાતું બનાવીને પ્રવેશ કરવા માટે આમંત્રણ છે; તેમ કરવું જોકે, આવશ્યક નથી",
        "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 e13fb30..586d879 100644 (file)
@@ -31,7 +31,8 @@
                        "Ldorfman",
                        "LaG roiL",
                        "Eraab",
-                       "Geagea"
+                       "Geagea",
+                       "פוילישער"
                ]
        },
        "tog-underline": "סימון קישורים בקו תחתי:",
        "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": "שינוי סיסמה",
        "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": "להעביר דף זה",
        "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": "קטגוריית מעקב",
        "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\" כבר נחסם.",
        "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": "רשימת כל הדפים המיוחדים",
        "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\".",
        "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": "אפשר לכבות עוגיות. יש לוודא שהעוגיות מופעלות ולהתחיל מחדש."
 }
index 82a42ee..408871b 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}}\"",
        "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> गुण खाली नहीं रहना चाहिए।",
index 19062c2..027f988 100644 (file)
        "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-highvolume": "Uređivanja velikog opsega",
        "newuserlogpage": "Evidencija novih suradnika",
        "newuserlogpagetext": "Ispod je popis nedavno otvorenih suradničkih imena.",
        "rightslog": "Evidencija suradničkih prava",
        "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.",
        "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",
        "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",
        "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",
index d71203c..577417c 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ó.",
        "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-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 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",
        "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",
        "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",
        "watchlist-hide": "Elrejtés",
        "watchlist-submit": "Megjelenítés",
        "wlshowtime": "Időszak:",
-       "wlshowhideminor": "apró",
+       "wlshowhideminor": "apró szerkesztések",
        "wlshowhidebots": "botok",
-       "wlshowhideliu": "bejelentkezett",
-       "wlshowhideanons": "névtelen",
-       "wlshowhidemine": "saját",
+       "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...",
        "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",
        "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]].",
        "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''')",
index 9477837..87c992e 100644 (file)
        "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",
        "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:",
        "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": "Total ukuran file untuk bagian ini: {{PLURAL: $ 1 | $ 1 byte | $ 1 byte}} ($ 2; $ 3%).",
-       "mediastatistics-allbytes": "Total ukuran untuk semua file: {{PLURAL: $ 1 | $ 1 byte | $ 1 byte}} ($ 2).",
+       "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",
index c7c8a4b..f1b13ed 100644 (file)
        "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}}“",
        "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",
index 2e26bb7..61c3b96 100644 (file)
@@ -93,7 +93,8 @@
                        "Bultro",
                        "Oggioniale",
                        "Wim b",
-                       "V6rg"
+                       "V6rg",
+                       "JackLantern"
                ]
        },
        "tog-underline": "Sottolinea i collegamenti:",
        "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",
        "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",
        "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",
        "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",
        "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",
        "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?",
        "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''.",
        "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",
        "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."
 }
index 94700cc..a0d81dc 100644 (file)
        "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": "利用者権限の変更記録",
        "listgrouprights-namespaceprotection-header": "名前空間ごとの制限",
        "listgrouprights-namespaceprotection-namespace": "名前空間",
        "listgrouprights-namespaceprotection-restrictedto": "編集を可能にする権限",
+       "listgrants-rights": "権限",
        "trackingcategories": "追跡用カテゴリ",
        "trackingcategories-summary": "このページでは、MediaWiki ソフトウェアが自動的に追加した追跡用カテゴリを列挙します。これらの名前は、{{ns:8}} 名前空間内の対応するシステム メッセージを修正することで変更できます。",
        "trackingcategories-msg": "追跡用カテゴリ",
        "tooltip-t-recentchangeslinked": "このページからリンクしているページの最近の更新",
        "tooltip-feed-rss": "このページのRSSフィード",
        "tooltip-feed-atom": "このページのAtomフィード",
-       "tooltip-t-contributions": "この利用者の投稿の一覧",
+       "tooltip-t-contributions": "{{GENDER:$1|この利用者}}による投稿の一覧",
        "tooltip-t-emailuser": "この利用者にメールを送信する",
        "tooltip-t-info": "このページについての詳細情報",
        "tooltip-t-upload": "ファイルをアップロードする",
        "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": "このページのキャッシュを破棄しますか?",
index fbab2fc..3a4472a 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": "მომხმარებლის უფლებების ჟურნალი",
index 73efe8f..64bc6c5 100644 (file)
        "right-blockemail": "Қатысушының хат жөнелтуін бұғаттау",
        "right-hideuser": "Баршадан жасырып, қатысушы атын бұғаттау",
        "right-ipblock-exempt": "IP бұғаттауларды, өзбұғаттауларды және ауқым бұғаттауларды орағыту",
-       "right-proxyunbannable": "Прокси серверлердің өзбұғаттауларын орағыту",
        "right-unblockself": "Өзін бұғатынан шығару",
        "right-protect": "Қорғау деңгейлерін өзгерту және баулы-қорғаулы беттерді өңдеу",
        "right-editprotected": "Қорғалған беттерді өңдеу \"{{int:protect-level-sysop}}\"",
        "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": "Нұсқа",
        "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|өзгертті}}",
        "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 0d5f011..8d5fc93 100644 (file)
        "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": "삭제 이유 편집",
        "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": "사용자 권한 기록",
        "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": "추적용 분류",
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 5c7ed50..e4ea791 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.",
        "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",
        "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",
index 6b8c9f8..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": "In indice paginarum observatarum omnes, non solum recentissimas mutationes ostendere",
-       "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-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 deleo in paginarum observatarum indicem addere",
+       "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-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-shownumberswatching": "Numerum usorum observantium monstrare",
        "tog-oldsig": "Subscriptio, qua nunc uteris:",
        "tog-fancysig": "Subscriptio vicitext (sine nexu automatico)",
-       "tog-uselivepreview": "Praevisum viventem adhibere",
+       "tog-uselivepreview": "Prospectum viventem perhibere",
        "tog-forceeditsummary": "Si recensionem non summatim descripsero, me roga si continuare velim",
-       "tog-watchlisthideown": "Recensiones meas in paginarum observatarum indice supprimere",
-       "tog-watchlisthidebots": "Recensiones per automaton factas in paginarum observatarum indice supprimere",
-       "tog-watchlisthideminor": "Minores recensiones in paginarum observatarum indice supprimere",
-       "tog-watchlisthideliu": "Recensiones ab usoribus notis factas in paginarum observatarum indice supprimere",
-       "tog-watchlistreloadautomatically": "Quamprimum aliquis selectus mutatus erit, indicem paginarum observatarum reficere (JavaScript necesse est)",
-       "tog-watchlisthideanons": "Recensiones ab usoribus ignotis factas in paginarum observatarum indice supprimere",
+       "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",
        "disclaimers": "Repudiationes",
        "disclaimerpage": "Project:Repudiationes",
        "edithelp": "Opes recensendi",
+       "helppage-top-gethelp": "Auxilium",
        "mainpage": "Pagina prima",
        "mainpage-description": "Pagina prima",
        "policy-url": "Project:Consilium",
        "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)",
        "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": "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": "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:",
        "yourlanguage": "Lingua:",
        "yourvariant": "Differentia linguae contentorum:",
        "yournick": "Subscriptio nova:",
-       "prefs-help-signature": "Commentationibus ad quanque disputationem factis ne subscribe nisi quater undulam (<nowiki>~</nowiki>) ponens! Quae quatuor undulae (<nowiki>~~~~</nowiki>) automatice in subscriptionem tuam et tempus subscribendi convertentur.",
+       "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:",
        "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 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 electronica usoribus, qui tibi scribeant, non erit visibilis.",
+       "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-preview": "Prospectus",
+       "prefs-advancedrc": "Modi speciales",
        "prefs-advancedwatchlist": "Indicis modi speciales",
        "prefs-displayrc": "Praeferentiae vultus",
        "prefs-displaywatchlist": "Praeferentiae vultus",
        "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",
        "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",
+       "watchlist": "Paginae observandae",
+       "mywatchlist": "Paginae observandae",
        "watchlistfor2": "ab usore \"$1\" $2",
-       "nowatchlist": "Sunt nullas paginas in indice tuo paginarum observatarum.",
-       "watchlistanontext": "Ad inspiciendum vel recensendum indicem paginarum observatarum necesse est nomen dare.",
+       "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",
        "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...",
        "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-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-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": "Addere hanc paginam tuis paginis observatis",
+       "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",
        "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 b0c6727..bcc06f0 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",
        "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.",
        "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",
        "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-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",
        "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",
        "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",
        "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:",
        "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"
 }
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 2e134e4..c8e05ec 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",
+       "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": "گؤەڕانن/تغییر محافظت ئێ وەڵگە",
+       "protectthispage": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø§Û\8e Ù\88Û\95ÚµÚ¯Û\95",
+       "unprotect": "پڵۆم کردن بگؤەڕِن(تغییر ده)",
+       "unprotectthispage": "گؤەڕانن(تغییر)پڵۆم کردن اێ وەڵگە",
        "newpage": "وةڵگة  تازۀ",
        "talkpage": "دەربارە ئێ وەڵگە گەپ بووشن",
-       "talkpagelinktext": "قسۀ-گۀپ",
+       "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-main": "وةڵگە(پەڕە)",
+       "nstab-user": "Ù\88Û\95ÚµÚ¯Û\95 Ú©Ø§Ø±Ø¨Û\95ر",
+       "nstab-media": "وەڵگە رسانه",
        "nstab-special": "وەڵگە(پەڕە)ویژە",
-       "nstab-project": "Ù\88Û\80Ù\84Ú¯Û\80 Ù¾Ø±Ù\88Ú\98Û\80",
+       "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}} وجود داشته باشد.",
        "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": "گذرواژه را دوباره وارد کنید",
        "createaccount": "حساووئ أرا ووژتان بِسازِن",
        "gotaccount": "حساوو کاربۀری دِرین؟$1",
        "gotaccountlink": "نؤم هۀتن سیستم",
-       "userlogin-resetlink": "جزئیات ورود إ ویرتان/یادتان چئة؟",
+       "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": ". دِرێ حساوو دؤرسة مةکإ . خواهشا صبر کةن",
        "loginlanguagelabel": "$1:زوون",
        "suspicious-userlogout": "درخواست هؤمة ئةرا  دةرچئن إژ سیستم  رد بیة زیرا به نظر می‌رسد که این  .درخواست توسط یک مرورگر معیوب یا پروکسی میانگیر کل/ارسال بیة",
        "createacct-another-realname-tip": "نام راسکانی/واقعی دڵ بخواهیة.\nاگر آن را وارد کنید هنگام ارجاع به آثارتان و انتساب آن‌ها به شما از نام واقعی‌تان استفاده خواهد شد.",
-       "pt-login": "نؤم هۀتن.",
+       "pt-login": "إنۆم هەتِن.",
        "pt-login-button": "نؤم هۀتن سیستم",
        "pt-createaccount": "حساووئ أرا ووژتان بِسازِن",
        "pt-userlogout": "دەرچێن|خروج",
        "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",
        "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}}» را فشار دهید ویرایش شما بدون آن ذخیره خواهد شد.",
        "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-user": "نام کاربری/نشانی آی‌پی",
        "revdelete-hide-restricted": "فرونشانی اطلاعات برای مدیران به همراه دیگران",
        "revdelete-radio-same": "(بدون گؤەڕانن/تغییر )",
-       "revdelete-radio-set": "آشاریا/پنهان",
+       "revdelete-radio-set": "آشاریا",
        "revdelete-radio-unset": "دیارۀ-نمایان",
        "revdelete-suppress": "از دسترسی مدیران به داده نیز مانند سایر کاربران جلوگیری به عمل آید.",
        "revdelete-unsuppress": "حذف محدودیت‌ها در بازبینی‌های ترمیم‌شده",
        "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.",
        "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* علامت * به این معنی‌است که اگر آن گروه را بیفزایید نمی‌توانید بعداً برش دارید، و برعکس.",
        "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-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}} را ندارید، لطفاً این پنجره را ببندید و از روش‌های دیگر استفاده کنید.",
        "listfiles": "فهرست پرونده‌ها",
        "listfiles_thumb": "چنه کلئکی/گؤجۀر بی",
        "listfiles_date": "تاریخ",
-       "listfiles_name": "نؤم",
+       "listfiles_name": "نۆم",
        "listfiles_user": "کاربۀر",
        "listfiles_size": "اندازۀ",
        "listfiles_description": "توضیحةل",
        "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": "صفحه‌های دارای بیشترین نسخه",
        "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": "نمایش عناوین",
        "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": "مجری:",
        "prevpage": "وةڵگة قبلی ($1)",
        "allpagesfrom": "نمایش وةڵگةل با شروع إژ:",
        "allpagesto": "نمایش صفحات با پایان در:",
-       "allarticles": "کول وۀلگۀل",
+       "allarticles": "کؤل(گشت)وەڵگەل",
        "allinnamespace": "همهٔ صفحات (فضای نام $1)",
        "allpagessubmit": "بِچۆ",
        "allpagesprefix": "نمایش صفحه‌های دارای پیشوند:",
        "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": "صفحه محتوایی نیست",
        "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|بحث]])» بود",
        "sessionfailure": "به نظر می‌رسد مشکلی در مورد نشست کاربری شما وجود دارد؛\nعمل درخواست شده در اقدامی پیشگیرانه در برابر ربوده‌شدن اطلاعات نشست کاربری، لغو شد.\nلطفاً دکمهٔ «بازگشت» را در مرورگر خود بفشارید و صفحه‌ای که از آن به اینجا رسیده‌اید را دوباره فراخوانی کنید، سپس مجدداً سعی کنید.",
        "changecontentmodel": "ویرایش نمونه محتوای یک صفحه",
        "changecontentmodel-legend": "گؤەڕانن/تغییر نوع محتوی",
-       "changecontentmodel-title-label": "عنوان وةڵگة",
+       "changecontentmodel-title-label": "عنوان وەڵگە",
        "changecontentmodel-model-label": "نمونه محتوای جدید",
        "changecontentmodel-reason-label": ":دةلیل",
        "changecontentmodel-success-title": "نمونه محتوی گؤەڕیا/تغییر یافت",
        "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": "گئردین/مهِ نی",
+       "sp-contributions-submit": "مِنِی کِردِن(گێردین)",
        "whatlinkshere": "پیوندەل وە ئێ وەڵگە",
        "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] را ببینید.",
        "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-createaccount": "مکئس تانۀ مۀکیم حساووئ بسازن و بونإ سامانۀ؛ هرچۀند حساوو کاربری سازین دل .بخوایۀ",
-       "tooltip-ca-talk": "Ú¯Û\80Ù¾/Ù\82سÛ\80 Ø¯Û\80ربارÛ\80 Ø¨Ù\86Ú\86Û\80Ú©/Ù\85حتÙ\88ا Ù\88Û\80Ù\84Ú¯Û\80",
-       "tooltip-ca-edit": "ئێ وەڵگە دەسکاری کە",
+       "tooltip-ca-talk": "Ú¯Û\95Ù¾(Ù\82سÛ\95)دÛ\95ربارÛ\95 Ù\86Û\86Ù\85 Ø¬Ù\90Ú©(Ù\85حتÙ\88ا)Ù\88Û\95ÚµÚ¯Û\95",
+       "tooltip-ca-edit": "اێ وەڵگە دەسکاری کە",
        "tooltip-ca-addsection": "بۀخش تازه بسازِن",
        "tooltip-ca-viewsource": ".ئئ وۀلگۀ محافظۀت بیۀ\nمۀتؤنین  متن مبدأ/بنچۀک بؤینین",
        "tooltip-ca-history": "ورژن دؤمائن/پئش ئئ وۀلگۀ",
-       "tooltip-ca-protect": "Ù\85حاÙ\81ظت Ø¥Ú\98 Ø¦ێ وەڵگە",
-       "tooltip-ca-unprotect": "گؤەڕانن/تغییر محافظت ئێ وةڵگە",
-       "tooltip-ca-delete": "حةذف ئئ وةڵگة",
+       "tooltip-ca-protect": "Ù¾ÚµÛ\86Ù\85 Ú©Ø±Ø¯Ù\86 Ø§ێ وەڵگە",
+       "tooltip-ca-unprotect": "گؤەڕانن(تغییر)پڵۆم کردن اێ وەڵگە",
+       "tooltip-ca-delete": "حەذف اێ وەڵگە",
        "tooltip-ca-undelete": "بازگرداندن نسخه‌های صفحهٔ حذف‌شده",
-       "tooltip-ca-move": "ئێ وەڵگە جاووآز کە",
+       "tooltip-ca-move": "اێ وەڵگە جاووآز کە",
        "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": "لیست هام کاریەل(هەمآرتیەل)ئێ کاربرە",
        "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اعات وەڵگە(پەڕە)",
+       "pageinfo-toolboxlink": "زاÙ\86ستÛ\95Ù\86Û\8cÛ\95Ù\84 وەڵگە(پەڕە)",
        "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-preferredattributionname": "در زمان استفاده مجدد، لطفاً اعتبار دهید به",
        "exif-pngfilecomment": "توضیحات پرونده PNG",
        "exif-disclaimer": "تکذیب‌نامه/درۆنامة",
-       "exif-contentwarning": "هشدار محتوا",
+       "exif-contentwarning": "هوشدار  نۆم جِک(محتوا)",
        "exif-giffilecomment": "توضیحات پرونده GIF",
        "exif-intellectualgenre": "نوع مورد",
        "exif-subjectnewscode": "کد موضوع",
        "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-source-extension": "تعریف‌شده بر پایه افزونه",
        "tags-source-manual": "اعمال شده به صورت دستی توسط ربات‌ها یا کاربرها",
        "tags-source-none": "دیگر استفاده نمی‌شود",
-       "tags-edit": "دةسکاری",
+       "tags-edit": "دەسکاری",
        "tags-delete": "حۀذف کردن/پاک کردن",
        "tags-activate": "فعال کردن",
        "tags-deactivate": "غیرفعال کردن/إکار کةتن",
        "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": "مدیران را محدود کرد",
        "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": "زوون انتخاب کۀ",
        "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 ('''غیر فعال''')",
        "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-new-page": "اÛ\8e Ù\88Û\95ÚµÚ¯Û\95 Ù\87Û\95Ù\86Û\8c\87اÙ\84Û\8c)وجود نِئرێ",
        "mw-widgets-titleinput-description-redirect": "گؤەڕانن/تغییر مسیر به $1",
        "api-error-blacklisted": "لطفاً یک عنوان توصیفی متفاوت انتخاب کنید."
 }
index b8e899a..d79cfe2 100644 (file)
@@ -6,7 +6,8 @@
                        "아라",
                        "Macofe",
                        "Mjbmr",
-                       "Matma Rex"
+                       "Matma Rex",
+                       "Lakzon"
                ]
        },
        "tog-underline": "هوم پئیڤأند زیرخأط دار:",
        "right-blockemail": "کاریار نه د کل کردن انجومانامه منع کو",
        "right-hideuser": "قلف کردن یه گل نوم کاریاری،قام کردن وه د ور تیه کل خلک",
        "right-ipblock-exempt": "کارگرایی نگرتن د قطع دسرسیا آی پی، خودانجوم یا فاصله دار",
-       "right-proxyunbannable": "کارگرایی نگرتن د قطع دسرسی خودکار پروکسیا",
        "right-unblockself": "خوشه قلف نکید",
        "right-protect": "آلشت دئن انازه پر و پیم کردن بلگه یا و ویرایشت بلگه یا پر و پیم بیه تافی",
        "right-editprotected": "ویرایشت بلگه یا پر و پیم بیه چی «{{int:protect-level-sysop}}»",
        "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..55be92f 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-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.",
        "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-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-submit": "Eiti",
        "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."
 }
index 0b07200..ece560a 100644 (file)
        "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",
index ca4f720..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": "सहायता पृष्ठ",
        "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)",
        "right-blockemail": "प्रयोक्ताकेँ ई-पत्र पठेबासँ रोकू",
        "right-hideuser": "एकटा प्रयोक्तानामकेँ प्रतिबन्धित करू, लोकसँ एकरा नुका कऽ",
        "right-ipblock-exempt": "अनिकेत प्रतिबन्ध, स्वचालित प्रतिबन्ध आ परिक्षेत्र प्रतिबन्धकेँ नै मानू",
-       "right-proxyunbannable": "दोसराइतक स्वचालित प्रतिबन्धकेँ नै मानू",
        "right-unblockself": "स्वयंकेँ प्रतिबन्धसँ हटाउ",
        "right-protect": "सुरक्षा स्तर बदलू आ संरक्षित पन्ना सम्पादित करू",
        "right-editprotected": "संरक्षित पन्ना सम्पादित करू (बिना तराउपड़ी सुरक्षाक)",
        "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": "पन्नाक नाम",
        "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\" नामगाम नै अछि।",
        "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]]''':",
        "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 98a177f..2a60218 100644 (file)
        "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": "Дневник на корисничките права",
        "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": "Следечка категорија",
index ffd2a23..8cce890 100644 (file)
        "october-date": "ഒക്ടോബർ $1",
        "november-date": "നവംബർ $1",
        "december-date": "ഡിസംബർ $1",
+       "period-am": "എ.എം.",
+       "period-pm": "പി.എം.",
        "pagecategories": "{{PLURAL:$1|വർഗ്ഗം|വർഗ്ഗങ്ങൾ}}",
        "category_header": "\"$1\" എന്ന വർഗ്ഗത്തിലെ താളുകൾ",
        "subcategories": "ഉപവർഗ്ഗങ്ങൾ",
        "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": "രഹസ്യവാക്ക് മാറ്റുക",
        "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": "ഈ താൾ മാറ്റുക",
        "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": "പിന്തുടരൽ വർഗ്ഗം",
        "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": "കാരണം:",
        "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": "ഒരേ പ്രമാണം തന്നെ വിവിധ പേരിലുണ്ടോയെന്നു ഹാഷ് വാല്യൂവധിഷ്ഠിതമായി തിരയുക.",
        "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": "കൂക്കികൾ സജ്ജമല്ലായിരിക്കാം. കൂക്കികൾ സജ്ജമാണോയെന്ന് ഉറപ്പാക്കിയ ശേഷം വീണ്ടും തുടങ്ങുക."
 }
index 81b3308..d6c47fb 100644 (file)
        "october-date": "ऑक्टोबर $1",
        "november-date": "नोव्हेंबर $1",
        "december-date": "डिसेंबर $1",
+       "period-am": "मा.पू.",
+       "period-pm": "मा.नं.",
        "pagecategories": "{{PLURAL:$1|वर्ग}}",
        "category_header": "\"$1\" वर्गातील लेख",
        "subcategories": "उपवर्ग",
        "virus-scanfailed": "क्रमवीक्षण (स्कॅन) अयशस्वी (कोड $1)",
        "virus-unknownscanner": "अनोळखी ऍन्टीव्हायरस:",
        "logouttext": "'''तुम्ही आता सनोंद-निर्गमित(लॉग-आउट) झाला आहात.'''\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": "परवलीचा शब्द बदला",
        "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 ही संचिका अस्तित्वात नाही.",
        "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": "पहारा काढत आहे...",
        "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": "सर्व विशेष पृष्ठांची यादी",
        "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": "अविचल भाषा वापरा",
        "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": "कुकिज अक्षम असू शकतात. याची खात्री करा कि कुकिज सक्षम केल्या आहेत व पुन्हा सुरुवात करा."
 }
index cb92008..6685373 100644 (file)
        "october-date": "အောက်တိုဘာ $1",
        "november-date": "နိုဝင်ဘာ $1",
        "december-date": "ဒီဇင်ဘာ $1",
+       "period-am": "နံနက်",
+       "period-pm": "ညနေ",
        "pagecategories": "{{PLURAL:$1|ကဏ္ဍ|ကဏ္ဍများ}}",
        "category_header": "ကဏ္ဍ \"$1\" မှ စာမျက်နှာများ",
        "subcategories": "ကဏ္ဍခွဲ",
        "directorycreateerror": "လမ်းညွှန် \"$1\" ကို ဖန်တီးမရနိုင်ပါ။",
        "filenotfound": "ဖိုင် \"$1\" ကို ရှာမတွေ့ပါ။",
        "formerror": "အမှား - ဖောင်သွင်းနိုင်ခြင်းမရှိပါ",
+       "cannotdelete": "\"$1\" စာမျက်နှာ သို့မဟုတ် ဖိုင်ကို ဖျက်၍ မရပါ။\nတစ်စုံတစ်ဦးမှ ဖျက်နှင့်ပြီး ဖြစ်နိုင်ပါသည်။",
        "cannotdelete-title": "\"$1\" စာမျက်နှာကို ဖျက်၍ မရပါ",
        "badtitle": "ညံ့ဖျင်းသော ခေါင်းစဉ်",
        "badtitletext": "တောင်းဆိုထားသော စာမျက်နှာ ခေါင်းစဉ်သည် တရားမဝင်ပါ (သို့) ဗလာဖြစ်နေသည် (သို့) အခြားဘာသာများ(inter-language or inter-wiki title)သို့ မှားယွင်းစွာ လင့်ချိတ်ထားသည်။",
        "viewsource": "ရင်းမြစ်ကို ကြည့်ရန်",
+       "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": "<strong>သင်သည် လော့ဂ်အောက် လုပ်လိုက်ပြီဖြစ်သည်။</strong>",
        "welcomeuser": "ကြိုဆိုပါတယ် $1!",
+       "welcomecreation-msg": "သင့်အကောင့်အား ဖန်တီးပြီးပါပြီ။\nသင် ဆန္ဒရှိပါက {{SITENAME}} [[Special:Preferences|ပုဂ္ဂိုလ်ရေးအချက်အလက်များ]]ကို ပြောင်းလဲနိုင်ပါသည်။",
        "yourname": "အသုံးပြုသူအမည် -",
        "userlogin-yourname": "အသုံးပြုသူအမည်",
        "userlogin-yourname-ph": "သင်၏ အသုံးပြုသူအမည် ရိုက်ထည့်ပါ",
        "createacct-benefit-body2": "{{PLURAL:$1|စာမျက်နှာ|စာမျက်နှာများ}}",
        "createacct-benefit-body3": "မကြာမီက {{PLURAL:$1|ဆောင်ရွက်သူ|ဆောင်ရွက်သူများ}}",
        "badretype": "သင်ထည့်သွင်းလိုက်သော စကားဝှက်များ ကိုက်ညီမှု မရှိပါ။",
+       "usernameinprogress": "ဤအသုံးပြုသူအမည်အတွက် အကောင့်ကို ဖန်တီးနေဆဲဖြစ်သည်။\nကျေးဇူးပြု၍ ခတ္တစောင့်ဆိုင်းပါ။",
        "userexists": "သင်ရွေးသော အသုံးပြုသူအမည်မှာ ရှိပြီးဖြစ်သည်။\nအခြား အမည် ရွေးပါ။",
        "loginerror": "Login ဝင်ခြင်း အမှား",
+       "createacct-error": "အကောင့်ဖန်တီးမှု အမှား",
        "createaccounterror": "ဤအကောင့်ကို မဖန်တီးနိုင်ပါ - $1",
        "noname": "တရားဝင် အသုံးပြုသူအမည်ကို မသတ်မှတ်ရသေးပါ။",
        "loginsuccesstitle": "Login ဝင်​ခြင်း အောင်မြင်သည်။",
        "pt-login": "အကောင့်ဝင်ရန်",
        "pt-login-button": "အကောင့်ဝင်ရန်",
        "pt-createaccount": "အကောင့် ဖန်တီးရန်",
-       "pt-userlogout": "ထွက်ရန်",
+       "pt-userlogout": "á\80¡á\80\80á\80±á\80¬á\80\84á\80·á\80ºá\80\91á\80½á\80\80á\80ºá\80\9bá\80\94á\80º",
        "changepassword": "စကားဝှက် ပြောင်းရန်",
        "resetpass_announce": "လော့ဂ်အင်ဝင်ရောက်ခြင်း ပြီးမြောက်ရန် စကားဝှက်အသစ် ပေးရမည် ဖြစ်သည်။",
        "resetpass_header": "အကောင့်စကားဝှက်ပြောင်းရန်",
        "resetpass-submit-loggedin": "စကားဝှက်ပြောင်းရန်",
        "resetpass-submit-cancel": "မလုပ်တော့ပါ",
        "resetpass-temp-password": "ယာယီစကားဝှက် -",
-       "passwordreset": "á\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်ရန်",
+       "passwordreset": "á\80\85á\80\80á\80¬á\80¸á\80\9dá\80¾á\80\80á\80ºá\80¡á\80\9eá\80\85á\80º á\80\95á\80¼á\80¯á\80\9cá\80¯á\80\95်ရန်",
        "passwordreset-username": "အသုံးပြုသူအမည် :",
        "passwordreset-email": "အီးမေး လိပ်စာ :",
        "changeemail": "အီးမေးလိပ်စာ ပြင်ဆင် သို့ ဖယ်ရှားရန်",
        "noarticletext-nopermission": "ဤစာမျက်နှာတွင် ယခုလက်ရှိတွင် မည်သည့်စာသားမှ မရှိပါ။\nသင်သည် အခြားစာမျက်နှာများတွင် [[Special:Search/{{PAGENAME}}|ဤစာမျက်နှာ၏ ခေါင်းစဉ်ကို ရှာနိုင်သည်]]၊ သို့မဟုတ် <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} ဆက်စပ်ရာ Logs များကို ရှာနိုင်သည်]</span>။ သို့သော် ဤစာမျက်နှာကို ဖန်တီးရန် သင့်တွင် အခွင့်အရေး မရှိပါ။",
        "note": "'''မှတ်ချက် -'''",
        "previewnote": "<strong>ဤသည်မှာ နမူနာ ကြည့်နေခြင်းသာဖြစ်ကြောင်း မမေ့ပါနှင့်။</strong>\nသင်ပြောင်းလဲထားသည်များကို မသိမ်းရသေးပါ။",
+       "continue-editing": "တည်းဖြတ်ဧရိယာသို့ သွားရန်",
        "editing": "$1 ကို တည်းဖြတ်နေသည်",
        "editingsection": "$1 (အပိုင်း) ကို ပြင်ဆင်နေသည်။",
        "editingcomment": "$1 (အပိုင်းသစ်) ကို ပြင်ဆင်နေသည်။",
        "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|á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸]]) á\81\8f $1 á\80¡á\80¬á\80¸ á\80\94á\80±á\80¬á\80\80á\80ºá\80\95á\80¼á\80\94á\80ºá\80\95á\80¼á\80\84်လိုက်သည်",
+       "undo-summary": "[[Special:Contributions/$2|$2]] ([[User talk:$2|á\80\86á\80½á\80±á\80¸á\80\94á\80½á\80±á\80¸]]) á\81\8f $1 á\80\95á\80¼á\80\84á\80ºá\80\86á\80\84á\80ºá\80\81á\80»á\80\80á\80º $1 á\80\80á\80­á\80¯ á\80\95á\80¼á\80\94á\80ºá\80\9cá\80\8aá\80ºá\80\95á\80\9aá\80ºá\80\96á\80»á\80\80်လိုက်သည်",
        "viewpagelogs": "ဤစာမျက်နှာအတွက် မှတ်တမ်းများကို ကြည့်ရန်",
        "nohistory": "ဤစာမျက်နှာတွင် တည်းဖြတ်မှု ရာဇဝင်မရှိပါ",
        "currentrev": "နောက်ဆုံးမူ",
        "mergelog": "ပေါင်းလိုက်သော မှတ်တမ်း",
        "revertmerge": "ပြန်ခွဲထုတ်ရန်",
        "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|ခု|ခု}} ပြရန်",
        "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": "အများဆုံး $1 {{PLURAL:$1|ရက်|ရက်}}",
        "prefs-watchlist-edits": "ချဲ့ထားသော စောင့်ကြည့်စာရင်းတွင် ပြရန် အပြောင်းအလဲတို့၏ အများဆုံး အရေအတွက်",
        "gender-female": "သူမသည် ဝီကီစာမျက်နှာများကို တည်းဖြတ်သည်",
        "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-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": "အသုံးပြုသူအုပ်စုကို စီမံရန်",
        "userrights-user-editname": "အသုံးပြုသူအမည်တစ်ခုကို ထည့်ပါ -",
-       "editusergroup": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80\90á\80¼်ရန်",
+       "editusergroup": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90်ရန်",
        "editinguser": "{{GENDER:$1|အသုံးပြုသူ}} <strong>[[User:$1|$1]]</strong> $2 ၏ အသုံးပြုအခွင့်အရေးများကို ပြောင်းလဲခြင်း",
-       "userrights-editusergroup": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80\90á\80¼်ရန်",
+       "userrights-editusergroup": "á\80¡á\80\9eá\80¯á\80¶á\80¸á\80\95á\80¼á\80¯á\80\9eá\80°á\80¡á\80¯á\80\95á\80ºá\80\85á\80¯á\80\80á\80­á\80¯ á\80\90á\80\8aá\80ºá\80¸á\80\96á\80¼á\80\90်ရန်",
        "saveusergroups": "အသုံးပြုသူအုပ်စုကို သိမ်းရန်",
        "userrights-groupsmember": "အဖွဲ့ဝင်",
        "userrights-reason": "အ​ကြောင်း​ပြ​ချက်:",
        "action-viewmywatchlist": "သင့် စောင့်ကြည့်စာရင်းကို ကြည့်ရန်",
        "nchanges": "ပြောင်းလဲချက် $1 {{PLURAL:$1|ခု|ခု}}",
        "enhancedrc-history": "ရာဇဝင်",
-       "recentchanges": "လတ်​တ​လောအ​ပြောင်း​အ​လဲ​",
-       "recentchanges-legend": "လတ်တလောအပြောင်းအလဲများအတွက် ရွေးချယ်စရာများ",
+       "recentchanges": "လတ်တလော အပြောင်းအလဲများ",
+       "recentchanges-legend": "လတ်တလော အပြောင်းအလဲများအတွက် ရွေးချယ်စရာများ",
        "recentchanges-summary": "ဤစာမျက်နှာတွင် ဝီကီ၏ လတ်တလောပြောင်းလဲမှုများကို နောက်ကြောင်းခံလိုက်ရန်",
        "recentchanges-feed-description": "ဤ feed ထဲတွင် ဝီကီ၏ လတ်တလောပြောင်းလဲမှုများကို နောက်ကြောင်းခံလိုက်ရန်",
        "recentchanges-label-newpage": "ဤတည်းဖြတ်မှုသည် စာမျက်နှာအသစ်ကို ဖန်တီးခဲ့သည်။",
        "recentchanges-label-unpatrolled": "ဤတည်းဖြတ်မှုကို မစောင့်ကြပ်မစစ်ဆေးရသေးပါ",
        "recentchanges-label-plusminus": "စာမျက်နှာ အရွယ်အစားမှာ အောက်ပါ ဘိုက်ပမာဏ ပြောင်းလဲသွားခဲ့သည်",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} ([[Special:NewPages|စာမျက်နှာသစ်များ စာရင်း]]ကိုလည်း ကြည့်ရန်)",
-       "recentchanges-submit": "ပြ",
+       "recentchanges-submit": "ပြသရန်",
        "rcnotefrom": "အောက်ပါတို့မှာ <strong>$3၊ $4</strong> မှစ၍ {{PLURAL:$5|ပြောင်းလဲမှု|ပြောင်းလဲမှုများ}} ဖြစ်သည်  (<strong>$1</strong> အထိ ပြထား)။",
        "rclistfrom": "$3 $2 မှစသော အပြောင်းအလဲအသစ်များကို ပြရန်",
        "rcshowhideminor": "အရေးမကြီးသော ပြင်ဆင်မှု $1ရန်",
-       "rcshowhideminor-show": "ပြ",
+       "rcshowhideminor-show": "ပြသရန်",
        "rcshowhideminor-hide": "ဝှက်",
-       "rcshowhidebots": "ဘော့ $1ရန်",
+       "rcshowhidebots": "ဘော့များ $1ရန်",
        "rcshowhidebots-show": "ပြ",
-       "rcshowhidebots-hide": "ဝှက်",
-       "rcshowhideliu": "$1 မှတ်ပုံတင်ထားသော အသုံးပြုသူများ",
-       "rcshowhideliu-show": "ပြ",
+       "rcshowhidebots-hide": "ဝှက်ရန်",
+       "rcshowhideliu": "မှတ်ပုံတင်ထားသော အသုံးပြုသူများ $1",
+       "rcshowhideliu-show": "ပြသရန်",
        "rcshowhideliu-hide": "ဝှက်",
        "rcshowhideanons": "အမည်မသိ အသုံးပြုသူ $1ရန်",
-       "rcshowhideanons-show": "ပြ",
+       "rcshowhideanons-show": "ပြသရန်",
        "rcshowhideanons-hide": "ဝှက်",
        "rcshowhidepatr": "စောင့်ြကပ်တည်းဖြတ်မှု $1ရန်",
-       "rcshowhidepatr-show": "ပြ",
-       "rcshowhidepatr-hide": "ဝှက်",
-       "rcshowhidemine": "ကျွန်ုပ်တည်းဖြတ်ထားသည်များအား $1ရန်",
-       "rcshowhidemine-show": "ပြ",
+       "rcshowhidepatr-show": "ပြသရန်",
+       "rcshowhidepatr-hide": "ဝှက်ရန်",
+       "rcshowhidemine": "ကျွန်ုပ်တည်းဖြတ်ထားသည်များ $1",
+       "rcshowhidemine-show": "ပြသရန်",
        "rcshowhidemine-hide": "ဝှက်",
-       "rcshowhidecategorization-show": "ပြ",
-       "rcshowhidecategorization-hide": "ဝှက်",
+       "rcshowhidecategorization-show": "ပြသရန်",
+       "rcshowhidecategorization-hide": "ဝှက်ရန်",
        "rclinks": "$2 ရက်အတွင်းမှ နောက်ဆုံးပြင်ဆင်ချက် $1 ခုကို ပြရန်</br> $3",
        "diff": "ကွဲပြားမှု",
        "hist": "မှတ်တမ်း",
        "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-added-to-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာ $2 ခု}}ကို ကဏ္ဍထဲသို့ ပေါင်းထည့်ခဲ့သည်",
        "recentchanges-page-removed-from-category": "ကဏ္ဍထဲမှ [[:$1]] ကို ဖယ်ရှားခဲ့သည်",
-       "recentchanges-page-removed-from-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာများ $2 ခု}}ကို ကဏ္ဍထဲမှ ဖယ်ရှားခဲ့သည်",
+       "recentchanges-page-removed-from-category-bundled": "[[:$1]] နှင့် {{PLURAL:$2|စာမျက်နှာ တစ်ခု|စာမျက်နှာ $2 ခု}}ကို ကဏ္ဍထဲမှ ဖယ်ရှားခဲ့သည်",
        "upload": "ဖိုင်​တင်​ရန်​",
        "uploadbtn": "ဖိုင်​တင်​ရန်​",
        "reuploaddesc": "Upload တင်နေခြင်းကို ဖျက်သိမ်းပြီး upload တင်သည့် ပုံစံသို့ ပြန်သွားရန်",
        "license-header": "လိုင်စင်သတ်မှတ်ခြင်း",
        "nolicense": "ဘာမှရွေးချယ်မထားပါ",
        "license-nopreview": "(နမူနာ မရနိုင်ပါ)",
-       "upload_source_url": "(သင်ရွေးချယ်ထားသော တရားဝင်၍ အများပြည်သူ ရယူသုံးစွဲနိုင်သော URL လင့်တစ်ခု)",
+       "upload_source_url": "တရားဝင်၍ အများပြည်သူ သုံးစွဲခွင့်ရှိသော URL လင့်တစ်ခုမှ သင်ရွေးချယ်ထားသည့် File",
        "upload_source_file": "(သင်ရွေးချယ်ထားသော သင့်ကွန်ပျူတာမှ ဖိုင်တစ်ခု)",
        "listfiles_search_for": "မီဒီယာအမည်ကို ရှာရန် -",
        "imgfile": "ဖိုင်",
        "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": "တည်းဖြတ်ထားဖူးသော အသုံးပြုသူများကိုသာ ဖော်ပြရန်",
        "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": "အများနှင့်ဆိုင်သောမှတ်တမ်းအားလုံး",
        "allpages": "စာမျက်နှာအားလုံး",
        "nextpage": "နောက်ထပ်စာမျက်နှာ ($1)",
        "allarticles": "စာမျက်နှာအားလုံး",
        "allinnamespace": "စာမျက်နှာအားလုံး (အမည်ညွှန်း $1)",
        "allpagessubmit": "သွား​ပါ​",
+       "allpages-hide-redirects": "ပြန်ညွှန်းများအား ဝှက်ရန်",
        "categories": "အမျိုးအစားများ",
+       "categories-submit": "ပြသရန်",
        "categoriesfrom": "ဤမှစသော အမျိုးအစားများကို ပြရန် -",
        "special-categories-sort-count": "အနည်းအများအလိုက်စီရန်",
        "special-categories-sort-abc": "အက္ခရာစဉ်အလိုက်စီရန်",
        "unwatchthispage": "စောင့်ကြည့်ခြင်းကို ရပ်တန့်ရန်",
        "notanarticle": "မာတိကာစာမျက်နှာတစ်ခု မဟုတ်",
        "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|ဆွေးနွေး]]) သာလျှင် ဆောင်ရွက်ထားသူ ဖြစ်သည်",
+       "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-level-sysop": "စီမံခန့်ခွဲသူများသာ",
        "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": "{{GENDER:$1|အသုံးပြုသူ}}၏ ဆောင်ရွက်ချက်များ",
        "contributions-title": "$1 အတွက် အသုံးပြုသူ၏ ဆောင်ရွက်ချက်များ",
        "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": "ပံ့ပိုး",
        "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-pt-createaccount": "အကောင့်တစ်ခုကို ဖန်တီးပြီး ဝင်ရောက်ရန် သင့်အား တိုက်တွန်းပါသည်။ သို့သော် မဖြစ်မနေ မဟုတ်ပါ။",
        "tooltip-ca-talk": "မာတိကာ စာမျက်နှာအတွက် ဆွေးနွေးချက်များ",
        "tooltip-ca-edit": "ဤစာမျက်နှာကို ပြင်ရန်",
        "tooltip-ca-addsection": "အပိုင်းသစ်တစ်ခု စရန်",
        "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": "ဤစာမျက်နှာ၏ ပရင့်ထုတ်နိုင်သောမူ",
        "exif-bitspersample": "အစိတ်အပိုင်းတစ်ခုတွင်ပါဝင်သော အပိုင်းငယ်များ",
        "exif-xresolution": "အလျားလိုက် ပုံရိပ်ပြတ်သားမှု",
        "exif-yresolution": "ဒေါင်လိုက် ပုံရိပ်ပြတ်သားမှု",
+       "exif-datetime": "ဖိုင်အပြောင်းအလဲ ရက်စွဲနှင့် အချိန်",
        "exif-imagedescription": "ပုံခေါင်းစဉ်",
        "exif-make": "ကင်မရာ ထုတ်လုပ်သူ",
        "exif-model": "ကင်မရာ မော်ဒယ်",
        "htmlform-submit": "ထည့်သွင်းရန်",
        "htmlform-reset": "ပြောင်းလဲထားသည်များ မလုပ်တော့ရန်",
        "htmlform-selectorother-other": "အခြား",
+       "logentry-delete-delete": "$3 စာမျက်နှာကို $1 က {{GENDER:$2|ဖျက်ပစ်ခဲ့သည်}}",
        "revdelete-restricted": "အက်ဒမင်များသို့ ကန့်သတ်ချက်များ သက်ရောက်ရန်",
        "revdelete-unrestricted": "အက်ဒမင်များအတွက် ကန့်သတ်ချက်များကို ဖယ်ရှားရန်",
        "logentry-newusers-create": "အသုံးပြုသူအကောင့် $1 ကို {{GENDER:$2|ဖန်တီးခဲ့သည်}}",
+       "logentry-upload-upload": "$1 သည် $3 ကို {{GENDER:$2|upload တင်ခဲ့သည်}}",
        "rightsnone": "(ဘာမှမရှိ)",
        "revdelete-summary": "အကျဉ်းချုပ်ကို တည်းဖြတ်ရန်",
        "searchsuggest-search": "ရှာဖွေရန်",
index 466e5cb..25b8777 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",
        "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",
        "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",
        "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",
        "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.",
        "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",
        "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."
 }
index 006c20f..d86263f 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",
        "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",
        "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",
        "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",
index 04ff6de..eb98058 100644 (file)
        "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": "प्रयोगकर्ता अधिकार लग",
        "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..5cf2382 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.",
        "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",
        "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",
        "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."
 }
index f88f127..4e33bf9 100644 (file)
        "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",
        "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 chcesz 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": "Utwórz konta",
+       "grant-createeditmovepage": "Tworzenie, edycja i przenoszenie stron",
+       "grant-delete": "Usuwanie stron, usuwanie określonych wersji stron i usuwanie 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": "Edytuj listę obserwowanych",
+       "grant-editpage": "Edytowanie istniejących stron",
+       "grant-editprotected": "Edycja stron zabezpieczonych",
+       "grant-highvolume": "Masowe edytowanie",
+       "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",
        "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-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",
        "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",
index c20186c..d0c6d8e 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.",
        "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-msg": "وېشنيزه موندنه",
        "trackingcategories-name": "پيغام نوم",
        "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": "تر اوسه پورې دا مخ نشته",
index b7556e0..8b260c8 100644 (file)
        "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": "Fazer alterações nas 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-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-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ários",
index 7075c2d..f53f001 100644 (file)
        "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-label-create": "Criar",
+       "botpasswords-label-resetpassword": "Redefinir palavra-passe",
        "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",
        "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-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 utilizadores",
        "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|criado(a)}} 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):",
        "listgrouprights-namespaceprotection-header": "Restrições do domínio",
        "listgrouprights-namespaceprotection-namespace": "Domínio",
        "listgrouprights-namespaceprotection-restrictedto": "Direito(s) do utilizador para editar",
+       "listgrants-rights": "Conceder",
        "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",
        "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:",
        "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",
        "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",
        "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.",
        "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",
index b9f133d..37e1a5a 100644 (file)
        "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}}",
        "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}}",
        "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}}",
        "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}}",
        "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.",
+       "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]]",
        "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 3e70b6e..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",
        "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}}”",
        "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.",
        "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 1ab11ef..e3557d1 100644 (file)
        "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": "Изменить пароль",
        "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": "обход блокировок по 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": "переименование этой страницы",
        "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": "Отслеживающая категория",
        "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» уже заблокирован.",
        "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": "Список служебных страниц",
        "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": "Поиск одинаковых файлов по хэш-коду.",
        "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": "Могут быть отключены куки. Убедитесь, что у вас включены куки и начните заново."
 }
index 0101f66..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": "प्रयोक्तृ-अधिकार-सूचिका",
        "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": "एतादृशयोग्यताभिः समं परिवर्तनानि न दृष्टानि ।",
index cc82198..10a5ff5 100644 (file)
        "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",
index 82e7e41..daf3396 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",
        "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",
        "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",
        "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",
        "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."
 }
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 5fe178e..6a65cb1 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": "Дневник корисничких права",
        "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": "по датуму",
index a877573..bfcab5b 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",
index a66f943..43d32ce 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",
        "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",
        "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",
        "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",
        "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.",
        "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."
 }
index 42e3441..a7055be 100644 (file)
        "right-blockemail": "ఈమెయిలు పంపకుండా సభ్యుని నిరోధించు",
        "right-hideuser": "ప్రజలకు కనబడకుండా చేసి, సభ్యనామాన్ని నిరోధించు",
        "right-ipblock-exempt": "ఐపీ నిరోధాలు, ఆటో నిరోధాలు, శ్రేణి నిరోధాలను తప్పించు",
-       "right-proxyunbannable": "ప్రాక్సీల ఆటోమాటిక్ నిరోధాన్ని తప్పించు",
        "right-unblockself": "స్వీయ అనిరోధం",
        "right-protect": "సంరక్షణ స్థాయిలను మార్చు, కాస్కేడ్-రక్షిత పేజీలలో దిద్దుబాటు చెయ్యి",
        "right-editprotected": "\"{{int:protect-level-sysop}}\" గా సంక్షించబడిన పేజీలను సరిదిద్దు",
index c91db68..cbcbb49 100644 (file)
        "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": "เปลี่ยนรหัสผ่าน",
        "right-blockemail": "บล็อกผู้ใช้มิให้ส่งอีเมล",
        "right-hideuser": "บล็อกชื่อผู้ใช้ ซ่อนไม่ให้สาธารณะเห็น",
        "right-ipblock-exempt": "เลี่ยงการบล็อกเลขที่อยู่ไอพี บล็อกอัตโนมัติ และบล็อกช่วง",
-       "right-proxyunbannable": "เลี่ยงการบล็อกอัตโนมัติของพร็อกซี",
        "right-unblockself": "ปลดบล็อกตนเอง",
        "right-protect": "เปลี่ยนระดับการล็อกและแก้ไขหน้าที่ถูกล็อกแบบต่อเรียง",
        "right-editprotected": "แก้ไขหน้าที่ถูกล็อกในฐานะ \"{{int:protect-level-sysop}}\"",
index 01bd4f7..7280d1f 100644 (file)
@@ -81,7 +81,8 @@
                        "Captantrips",
                        "Diyapazon",
                        "Matma Rex",
-                       "HakanIST"
+                       "HakanIST",
+                       "Imabadplayer"
                ]
        },
        "tog-underline": "Bağlantıların altını çiz:",
        "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 oturum kapatmak mümkün degil.",
        "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ısı.",
+       "botpasswords-no-central-id": "Bot şifresini kullanmak için, merkezi bir hesabla giriş yapmalisiniz.",
+       "botpasswords-existing": "Mevcut bot şifreleri.",
+       "botpasswords-createnew": "Yeni bir bot şifresi olustur",
+       "botpasswords-editexisting": "Mevcut bir bot şifresini duzenle",
+       "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ın $1 olarak eklenmesi. Zaten eklenmiş mıydı?",
+       "botpasswords-update-failed": "Bot ismini $1 olarak güncelleme başarısız oldu. Silinmiş mıydı?",
+       "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 kaldir",
        "prefs-setemail": "E-posta adresini ayarlayın",
        "prefs-email": "E-posta seçenekleri",
        "prefs-rendering": "Görünüm",
        "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ı",
        "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",
        "special-characters-title-emdash": "uzun çizgi",
        "special-characters-title-minus": "Eksi işareti",
        "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."
 }
index 20f9da2..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": "Төркемчәләр",
        "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:Тасвирлама",
        "nstab-special": "Махсус бит",
        "nstab-project": "Проект бите",
        "nstab-image": "Файл",
-       "nstab-mediawiki": "Хәбәр",
+       "nstab-mediawiki": "Хат",
        "nstab-template": "Калып",
        "nstab-help": "Ярдәм",
        "nstab-category": "Төркем",
        "eauthentsent": "Күрсәтелгән электрон почта адресына үзгәртүләрне раслау өчен хат җибәрелде. Киләчәктәдә хатлар кабул итү өчен, раслауны үтегез.",
        "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-none": "(юк)",
        "changeemail-password": "«{{SITENAME}}» проекты өчен серсүзегез:",
        "changeemail-submit": "E-mail адресын үзгәртү",
+       "resettokens": "Токеннарны ташлау",
        "resettokens-tokens": "Токеннар:",
        "resettokens-token-label": "$1 (агымдагы мәгънә: $2)",
        "bold_sample": "Калын язылыш",
        "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-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": "Файл эчендәгеләрне качыр",
        "searchprofile-everything-tooltip": "Барлык битләрдән эзләү",
        "searchprofile-advanced-tooltip": "Бирелгән исемнәр мәйданында эзләү",
        "search-result-size": "$1 ({{PLURAL:$2|$2 сүз}})",
-       "search-result-category-size": "{{PLURAL:$1|1=1 әгъза|$1 әгъза}} ({{PLURAL:$2|1=1 асттөркем|$2 асттөркем}}, {{PLURAL:$3|1=1 файл|$3 файл}})",
+       "search-result-category-size": "$1 {{PLURAL:$1|әгъза}} ($2 {{PLURAL:$2|астөркем}}, $3 {{PLURAL:$3|файл}})",
        "search-redirect": "(юнәлтү $1)",
        "search-section": "($1 бүлеге)",
        "search-category": "($1 категориясе)",
        "right-suppressredirect": "Элекке исемнән юнәлтү ясамыйча исемне алмаштыру",
        "right-upload": "файлларны йөкләү",
        "right-reupload": "Булган файллар өстеннән язарга",
-       "right-writeapi": "язма өчен API куллану",
+       "right-writeapi": "Язма өчен API куллану",
        "right-delete": "битләрне бетерү",
        "right-editinterface": "Кулланучы интерфейсын үзгәртү",
        "newuserlogpage": "Кулланучыларны теркәү көндәлеге",
        "recentchanges-label-bot": "Бу үзгәртү бот белән эшләнгән",
        "recentchanges-label-unpatrolled": "Үзгәртүне әлегә тикшермәгәннәр",
        "recentchanges-label-plusminus": "Битнең зурлыгы шуның кадәрле байтка үзгәрде",
-       "recentchanges-legend-heading": "'''Ð\9bегенда:&nbsp;'''",
+       "recentchanges-legend-heading": "'''Ð\90ңлаÑ\82ма:&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",
        "rcshowhidebots-show": "Күрсәт",
        "rcshowhidebots-hide": "Яшер",
        "rcshowhideliu": "теркәлгән кулланучыларны $1",
-       "rcshowhideliu-show": "күрсәт",
+       "rcshowhideliu-show": "Ð\9aүрсәт",
        "rcshowhideliu-hide": "Яшер",
        "rcshowhideanons": "кермәгән кулланучыларны $1",
        "rcshowhideanons-show": "Күрсәтү",
        "rcshowhideanons-hide": "Яшер",
        "rcshowhidepatr": "тикшерелгән үзгәртүләрне $1",
+       "rcshowhidepatr-show": "Күрсәтү",
        "rcshowhidepatr-hide": "яшер",
        "rcshowhidemine": "минем үзгәртүләремне $1",
        "rcshowhidemine-show": "Күрсәтү",
        "rcshowhidemine-hide": "Яшер",
-       "rclinks": "Соңгы $2 көн эчендә соңгы $1 үзгәртүне күрсәт<br />$3",
+       "rcshowhidecategorization-show": "Күрсәт",
+       "rclinks": "Соңгы $2 көн эчендә ясалган $1 үзгәртүне күрсәт<br />$3",
        "diff": "аерма",
        "hist": "тарих",
        "hide": "Яшер",
        "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.",
        "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": "әлифба буенча тәртипләү",
        "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": "Эш башкарылмаган",
        "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-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": "Барлык махсус битләр исемлеге",
        "confirm_purge_button": "OK",
        "confirm-purge-top": "Бу битнең кэшы чистартылсынмы?",
        "confirm-purge-bottom": "Кэшны чистартудан соң аның соңгы юрамасы күрсәтеләчәк.",
+       "pipe-separator": "&#32;|&#32;",
        "imgmultipageprev": "← алдагы бит",
        "imgmultipagenext": "алдагы бит →",
        "imgmultigo": "Күчү!",
index c2d96e3..5f49198 100644 (file)
@@ -63,7 +63,8 @@
                        "Капитан Джон Шепард",
                        "Translatemyname",
                        "Dars",
-                       "Mix Gerder"
+                       "Mix Gerder",
+                       "E.belykh"
                ]
        },
        "tog-underline": "Підкреслювання посилань:",
        "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": "Журнал прав користувача",
        "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": "Відстежувана категорія",
        "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": "Причина:",
        "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> атрибута не може бути пуста.",
index c446107..17cfff1 100644 (file)
        "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 سےنئی تبدیلیاں دکھانا شروع کریں",
        "statistics-edits-average": "فی صفحہ اوسط ترامیم",
        "statistics-users": "مندرج [[خاص:فہرست صارفین، صارف فہرست|صارفین]]",
        "statistics-users-active": "متحرک صارفین",
+       "pageswithprop-submit": "ٹھیک",
        "doubleredirects": "دوہرے متبادل ربط",
        "brokenredirects": "نامکمل متبادل ربط",
        "brokenredirects-edit": "ترمیم کریں",
        "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": "درآمد صفحات",
        "monthsall": "تمام",
        "deletedwhileediting": "انتباہ: آپ کے ترمیم شروع کرنے کے بعد یہ صفحہ حذف کیا جا چکا ہے!",
        "confirm_purge_button": "جی!",
+       "semicolon-separator": "؛&#32;",
+       "imgmultipageprev": "← پچھلا",
+       "imgmultipagenext": "اگلا →",
+       "imgmultigo": "جائیں!",
+       "imgmultigoto": "$1 صفحہ پر جائیں",
        "table_pager_next": "اگلا صفحہ",
        "table_pager_prev": "پچھلا صفحہ",
        "table_pager_first": "پہلا صفحہ",
index c28646c..28329b6 100644 (file)
        "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/JS 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",
        "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-rights": "Cấp phép",
        "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",
index 14f9d09..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",
        "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.",
        "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.",
        "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-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",
        "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",
index 151a632..ec65b9a 100644 (file)
        "print": "打印",
        "view": "望",
        "view-foreign": "登$1上看",
-       "edit": "编",
+       "edit": "编",
        "edit-local": "编辑本地说明",
        "create": "建",
        "create-local": "添加本地说明",
        "redirectedfrom": "(从$1转戳到箇里)",
        "redirectpagesub": "轉戳頁",
        "redirectto": "重定向到:",
-       "lastmodifiedat": "箇页此垡来$1 $2改进。",
+       "lastmodifiedat": "箇只页面阿末趟修订来拉$1 $2。",
        "viewcount": "箇頁望過$1垡。",
        "protectedpage": "畀保护个页面",
        "jumpto": "蹦到:",
        "extlink_tip": "外部链接(前头记牢加 http://)",
        "headline_sample": "标题文本",
        "headline_tip": "2级标题文字",
-       "nowiki_sample": "箇里插入非格式文本",
+       "nowiki_sample": "箇里插入非格式文本",
        "nowiki_tip": "弗管wiki格式",
        "image_tip": "嵌入文件",
        "media_tip": "文件链接",
        "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": "用户权限日志",
        "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-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-minoredit": "标作小编写",
        "tooltip-save": "保存侬个修改",
        "tooltip-preview": "望望相你侬个修改,保存之前用!",
-       "tooltip-diff": "æ\98¾ç¤ºä½ ä¾¬å¯¹æ\96\87æ\9c¬ä¸ªä¿®æ\94¹",
+       "tooltip-diff": "显示侬对文本个修改",
        "tooltip-compareselectedversions": "查看本页面两只选定个修订版个差异。",
        "tooltip-watch": "拿箇页加到侬个关注表里",
        "tooltip-rollback": "揿一记“回转”就回退到上一位贡献者个编辑状态",
index c2a6910..687b7d9 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": "ჯოხო",
        "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 653f7d2..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 אריבער ווייטערפירונג",
index 14b08ca..804d55b 100644 (file)
        "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=更改}}",
        "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": "验证数据库出错或您被禁止更新您的外部账号。",
        "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": "更改密码",
        "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": "绕过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": "移动本页",
        "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": "追踪分类",
        "block-log-flags-hiddenname": "隐藏用户名",
        "range_block_disabled": "管理员执行段封禁的权限已被禁用。",
        "ipb_expiry_invalid": "无效的终止时间。",
+       "ipb_expiry_old": "终止时间已过去。",
        "ipb_expiry_temp": "隐藏用户名的封禁必须是永久性的。",
        "ipb_hide_invalid": "无法封禁此账户;它拥有多于$1次编辑。",
        "ipb_already_blocked": "“$1”已被封禁。",
        "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": "所有特殊页面的列表",
        "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": "要清除此页面的缓存吗?",
        "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)值搜索重复文件。",
        "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,并重试。"
 }
index 5230c4a..23b7ca2 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|偏好設定]] 更新您在 {{SITENAME}} 的個人化設定。",
        "yourname": "使用者名稱:",
        "remembermypassword": "在瀏覽器上記住我的登入資訊 (上限 $1 {{PLURAL:$1|天}})",
        "userlogin-remembermypassword": "記住我的登入狀態",
        "userlogin-signwithsecure": "使用安全連線",
+       "cannotloginnow-title": "現在無法登入",
+       "cannotloginnow-text": "使用 $1 時無法登入。",
        "yourdomainname": "您的網域:",
        "password-change-forbidden": "您不可變更此 Wiki 上的密碼。",
        "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-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以下提供此頁面的刪除與移動日誌做為參考。",
        "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-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": "移動此頁面",
        "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\"。",
        "log-title-wildcard": "搜尋以此欄位文字為字首的標題",
        "showhideselectedlogentries": "顯示/隱藏已選擇的日誌項目",
        "log-edit-tags": "編輯已選擇日誌項目的標籤",
+       "checkbox-all": "全部",
        "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": "追蹤分類",
        "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-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": "依日期",
        "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> 屬性不能為空。",
        "mediastatistics": "媒體統計資訊",
        "mediastatistics-summary": "已上傳檔案類型的統計資訊,此報表僅統計檔案的最新版本,不包含舊的或已刪除的版本。",
        "mediastatistics-nbytes": "{{PLURAL:$1|$1 位元組|$1 位元組}} ($2; $3%)",
-       "mediastatistics-bytespertype": "此章節總檔案大小:$1 位元組。",
-       "mediastatistics-allbytes": "所有檔案的總檔案大小:$1 位元組。",
+       "mediastatistics-bytespertype": "此章節總檔案大小:$1 位元組 ($2; $3%)。",
+       "mediastatistics-allbytes": "所有檔案的總檔案大小:$1 位元組 ($2)。",
        "mediastatistics-table-mimetype": "MIME 類型",
        "mediastatistics-table-extensions": "可用的副檔名",
        "mediastatistics-table-count": "檔案數量",
        "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 功能並重新啟動。"
 }
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 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( '重新導向', '重定向' ),
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 b26eaa5..14d8420 100644 (file)
@@ -38,13 +38,22 @@ class ImportTextFiles extends Maintenance {
                $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->addArg( 'titles', 'Titles of article to 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.
@@ -59,7 +68,7 @@ class ImportTextFiles extends Maintenance {
                };
 
                $count = count( $files );
-               $this->output( "Creating $count pages...\n" );
+               $this->output( "Importing $count pages...\n" );
 
                if ( $userName === false ) {
                        $user = User::newSystemUser( 'Maintenance script', array( 'steal' => true ) );
@@ -81,46 +90,103 @@ class ImportTextFiles extends Maintenance {
                $skipCount = 0;
 
                foreach ( $files as $file => $text ) {
-                       $pageName = pathinfo( $file, PATHINFO_FILENAME );
+                       $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;
                        }
 
-                       if ( $title->exists() ) {
-                               $actualTitle = $title->getPrefixedText();
-                               $this->output( "Title $pageName already exists. 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( $text );
+                       $rev->setText( rtrim( $text ) );
                        $rev->setTitle( $title );
                        $rev->setUserObj( $user );
                        $rev->setComment( $summary );
-                       if ( $useTimestamp ) {
-                               $rev->setTimestamp( wfTimestamp( TS_UNIX, filemtime( $file ) ) );
-                       } else {
-                               $rev->setTimestamp( wfTimestampNow() );
+                       $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 ) {
-                               $this->output( "Successfully created $actualTitle\n" );
+                               $action = $exists ? 'updated' : 'created';
+                               $this->output( "Successfully $action $actualTitle\n" );
                                $successCount++;
                        } else {
-                               $actualTitle = $title->getPrefixedText();
-                               $this->output( "Failed to create $actualTitle\n" );
+                               $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 successfully created, $skipCount skipped.\n" );
+
+               $this->output( "Done! $successCount succeeded, $skipCount skipped.\n" );
                if ( $exit ) {
                        $this->error( "Import failed with $failCount failed pages.\n", $exit );
                }
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¾子里
 朴子里
 翁子里
 田子里
 櫃裡
 片裡
 電影裡
+廣播裡
+電視裡
 裏白 #植物常用名
 烏蘇里 #分詞用
 首發
 王后
 王侯后
 母后
+字母後
+聲母後
 武后
 歌后
 影后
 鮮于
 朝鮮於
 于寶軒
+于承惠
 于震
 於震前
 於震後
 電子製表
 製毒
 製販
+譯製
 遏制 #以下分詞用
 管制
 抑制
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 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 bbcf8b5..cd9810c 100644 (file)
@@ -832,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(
@@ -1026,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',
@@ -1345,6 +1342,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',
@@ -1718,6 +1723,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(
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 457e8c1..f6c407a 100644 (file)
 }
 
 .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 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;
+               }
        }
 }
 
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 ) );
index 92064a6..29322f4 100644 (file)
                        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.
index 4806097..6813ea6 100644 (file)
                        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' )
index 1cd9101..84b33fd 100644 (file)
                        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 {
                                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 }
                                );
                        }
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..d6e0157 100644 (file)
@@ -29,3 +29,7 @@
        margin-right: 5%;
        width: 39%;
 }
+
+.oo-ui-fieldLayout .oo-ui-fieldLayout.oo-ui-fieldLayout-align-inline {
+  margin-bottom: 0;
+}
index d444923..671f38f 100644 (file)
                 */
                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 1c48515..f4ddfb2 100644 (file)
@@ -49,6 +49,7 @@ $wgAutoloadClasses += array(
 
        # tests/phpunit/includes
        'TestingAccessWrapper' => "$testDir/phpunit/includes/TestingAccessWrapper.php",
+       'TestLogger' => "$testDir/phpunit/includes/TestLogger.php",
 
        # tests/phpunit/includes/api
        'ApiFormatTestBase' => "$testDir/phpunit/includes/api/format/ApiFormatTestBase.php",
@@ -94,6 +95,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 +123,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 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/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 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(
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 002e86f..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
         */
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..c18b821
--- /dev/null
@@ -0,0 +1,353 @@
+<?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 ) );
+       }
+
+       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..dc217cd
--- /dev/null
@@ -0,0 +1,1683 @@
+<?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 );
+               $this->assertInstanceOf( 'MediaWiki\\Session\\Session', $session );
+               $this->assertSame( $id, $session->getId() );
+
+               $id = $manager->generateSessionId();
+               $this->assertNull( $manager->getSessionById( $id, true ) );
+
+               // Known but unloadable session ID
+               $this->logger->setCollect( true );
+               $id = $manager->generateSessionId();
+               $this->store->setRawSession( $id, array( 'metadata' => array(
+                       'provider' => 'DummySessionProvider',
+                       'userId' => 0,
+                       'userName' => null,
+                       'userToken' => null,
+               ) ) );
+
+               try {
+                       $manager->getSessionById( $id );
+                       $this->fail( 'Expected exception not thrown' );
+               } catch ( \UnexpectedValueException $ex ) {
+                       $this->assertSame(
+                               'Can neither load the session nor create an empty session',
+                               $ex->getMessage()
+                       );
+               }
+
+               $this->assertNull( $manager->getSessionById( $id, true ) );
+               $this->logger->setCollect( false );
+
+               // Known session ID
+               $this->store->setSession( $id, array() );
+               $session = $manager->getSessionById( $id );
+               $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 )->getSessionId() );
+
+               $manager->changeBackendId( $backend );
+               $this->assertSame( $sessionId, $session->getSessionId() );
+               $this->assertNotEquals( $id, (string)$sessionId );
+               $id = (string)$sessionId;
+
+               $this->assertSame( $sessionId, $manager->getSessionById( $id )->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 );
+               $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() );
+       }
+
+}
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 );
-       }
 }
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;
+       }
+}
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 09dc931..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(
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" );