Merge "Do not hardcode square brackets in "rollback link""
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Sun, 21 Jul 2019 19:41:24 +0000 (19:41 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Sun, 21 Jul 2019 19:41:24 +0000 (19:41 +0000)
239 files changed:
RELEASE-NOTES-1.34
autoload.php
docs/hooks.txt
includes/DefaultSettings.php
includes/DevelopmentSettings.php
includes/EditPage.php
includes/FeedUtils.php
includes/GlobalFunctions.php
includes/Linker.php
includes/Permissions/PermissionManager.php
includes/Revision/RenderedRevision.php
includes/ServiceWiring.php
includes/Storage/PageEditStash.php
includes/Title.php
includes/TitleArrayFromResult.php
includes/actions/InfoAction.php
includes/actions/RollbackAction.php
includes/actions/pagers/HistoryPager.php
includes/api/ApiCSPReport.php
includes/api/ApiEditPage.php
includes/api/ApiParse.php
includes/api/ApiQueryDeletedrevs.php
includes/cache/MessageCache.php
includes/changes/CategoryMembershipChange.php
includes/changes/ChangesFeed.php
includes/changes/ChangesList.php
includes/changes/EnhancedChangesList.php
includes/changes/RCCacheEntryFactory.php
includes/changetags/ChangeTagsLogItem.php
includes/content/ContentHandler.php
includes/deferred/DeferredUpdates.php
includes/diff/DifferenceEngine.php
includes/export/WikiExporter.php
includes/export/XmlDumpWriter.php
includes/filerepo/file/LocalFileDeleteBatch.php
includes/htmlform/fields/HTMLNamespacesMultiselectField.php
includes/import/ImportableUploadRevisionImporter.php
includes/import/WikiRevision.php
includes/installer/Installer.php
includes/installer/WebInstaller.php
includes/installer/WebInstallerWelcome.php
includes/installer/i18n/ar.json
includes/installer/i18n/ast.json
includes/installer/i18n/ba.json
includes/installer/i18n/be-tarask.json
includes/installer/i18n/bg.json
includes/installer/i18n/br.json
includes/installer/i18n/ca.json
includes/installer/i18n/ce.json
includes/installer/i18n/cs.json
includes/installer/i18n/de-ch.json
includes/installer/i18n/de.json
includes/installer/i18n/dty.json
includes/installer/i18n/el.json
includes/installer/i18n/en-gb.json
includes/installer/i18n/en.json
includes/installer/i18n/es.json
includes/installer/i18n/eu.json
includes/installer/i18n/fa.json
includes/installer/i18n/fi.json
includes/installer/i18n/fr.json
includes/installer/i18n/gl.json
includes/installer/i18n/gsw.json
includes/installer/i18n/he.json
includes/installer/i18n/hrx.json
includes/installer/i18n/hu.json
includes/installer/i18n/ia.json
includes/installer/i18n/id.json
includes/installer/i18n/io.json
includes/installer/i18n/is.json
includes/installer/i18n/it.json
includes/installer/i18n/ja.json
includes/installer/i18n/ko.json
includes/installer/i18n/ksh.json
includes/installer/i18n/lij.json
includes/installer/i18n/lki.json
includes/installer/i18n/mg.json
includes/installer/i18n/mk.json
includes/installer/i18n/mr.json
includes/installer/i18n/ms.json
includes/installer/i18n/nan.json
includes/installer/i18n/nap.json
includes/installer/i18n/nb.json
includes/installer/i18n/nl-informal.json
includes/installer/i18n/nl.json
includes/installer/i18n/pl.json
includes/installer/i18n/pms.json
includes/installer/i18n/pt-br.json
includes/installer/i18n/pt.json
includes/installer/i18n/qqq.json
includes/installer/i18n/ro.json
includes/installer/i18n/ru.json
includes/installer/i18n/sco.json
includes/installer/i18n/sh.json
includes/installer/i18n/sv.json
includes/installer/i18n/te.json
includes/installer/i18n/th.json
includes/installer/i18n/tl.json
includes/installer/i18n/tr.json
includes/installer/i18n/uk.json
includes/installer/i18n/vi.json
includes/installer/i18n/zh-hans.json
includes/installer/i18n/zh-hant.json
includes/libs/objectcache/APCBagOStuff.php
includes/libs/objectcache/APCUBagOStuff.php
includes/libs/objectcache/BagOStuff.php
includes/libs/objectcache/CachedBagOStuff.php
includes/libs/objectcache/EmptyBagOStuff.php
includes/libs/objectcache/HashBagOStuff.php
includes/libs/objectcache/MediumSpecificBagOStuff.php [new file with mode: 0644]
includes/libs/objectcache/MemcachedBagOStuff.php
includes/libs/objectcache/MemcachedPeclBagOStuff.php
includes/libs/objectcache/MemcachedPhpBagOStuff.php
includes/libs/objectcache/MultiWriteBagOStuff.php
includes/libs/objectcache/RESTBagOStuff.php
includes/libs/objectcache/RedisBagOStuff.php
includes/libs/objectcache/ReplicatedBagOStuff.php
includes/libs/objectcache/WANObjectCache.php
includes/libs/objectcache/WinCacheBagOStuff.php
includes/libs/redis/RedisConnectionPool.php
includes/logging/DeleteLogFormatter.php
includes/objectcache/SqlBagOStuff.php
includes/page/Article.php
includes/page/WikiPage.php
includes/parser/CoreParserFunctions.php
includes/parser/Parser.php
includes/parser/ParserOutput.php
includes/resourceloader/MessageBlobStore.php
includes/resourceloader/ResourceLoaderFileModule.php
includes/resourceloader/ResourceLoaderWikiModule.php
includes/revisiondelete/RevDelArchivedFileItem.php
includes/revisiondelete/RevDelFileItem.php
includes/revisiondelete/RevDelList.php
includes/revisiondelete/RevDelLogItem.php
includes/revisiondelete/RevDelLogList.php
includes/revisiondelete/RevDelRevisionItem.php
includes/revisiondelete/RevDelRevisionList.php
includes/revisiondelete/RevisionDeleteUser.php
includes/revisiondelete/RevisionDeleter.php
includes/revisionlist/RevisionItem.php
includes/specialpage/AuthManagerSpecialPage.php
includes/specials/SpecialFewestrevisions.php
includes/specials/SpecialMergeHistory.php
includes/specials/SpecialMute.php
includes/specials/SpecialRevisionDelete.php
includes/specials/SpecialUndelete.php
includes/specials/pagers/ContribsPager.php
includes/specials/pagers/DeletedContribsPager.php
includes/upload/UploadBase.php
includes/upload/UploadFromChunks.php
includes/watcheditem/WatchedItemQueryService.php
languages/LanguageConverter.php
languages/i18n/ar.json
languages/i18n/ast.json
languages/i18n/bcc.json
languages/i18n/be-tarask.json
languages/i18n/be.json
languages/i18n/cs.json
languages/i18n/de.json
languages/i18n/en.json
languages/i18n/eo.json
languages/i18n/es-formal.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/gor.json
languages/i18n/he.json
languages/i18n/hu.json
languages/i18n/ia.json
languages/i18n/it.json
languages/i18n/ja.json
languages/i18n/km.json
languages/i18n/ko.json
languages/i18n/mk.json
languages/i18n/ms.json
languages/i18n/my.json
languages/i18n/nb.json
languages/i18n/nl.json
languages/i18n/nn.json
languages/i18n/pl.json
languages/i18n/pt-br.json
languages/i18n/pt.json
languages/i18n/qqq.json
languages/i18n/ru.json
languages/i18n/rue.json
languages/i18n/sdc.json
languages/i18n/sh.json
languages/i18n/sl.json
languages/i18n/sq.json
languages/i18n/sr-ec.json
languages/i18n/sty.json
languages/i18n/sv.json
languages/i18n/tg-cyrl.json
languages/i18n/th.json
languages/i18n/tr.json
languages/i18n/tt-cyrl.json
languages/i18n/uk.json
languages/i18n/zh-hans.json
languages/i18n/zh-hant.json
maintenance/cleanupSpam.php
maintenance/doMaintenance.php
maintenance/fixDefaultJsonContentPages.php
maintenance/getText.php
maintenance/mctest.php
maintenance/namespaceDupes.php
maintenance/populateRevisionLength.php
maintenance/preprocessDump.php
maintenance/refreshLinks.php
maintenance/storage/checkStorage.php
maintenance/userDupes.inc
maintenance/view.php
resources/Resources.php
resources/src/mediawiki.api/index.js
resources/src/mediawiki.special.block.js
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt
tests/phpunit/MediaWikiUnitTestCase.php
tests/phpunit/README
tests/phpunit/TODO [deleted file]
tests/phpunit/includes/GlobalFunctions/GlobalTest.php
tests/phpunit/includes/Permissions/PermissionManagerTest.php
tests/phpunit/includes/RevisionDbTestBase.php
tests/phpunit/includes/api/ApiCSPReportTest.php [new file with mode: 0644]
tests/phpunit/includes/block/BlockManagerTest.php
tests/phpunit/includes/cache/MessageCacheTest.php
tests/phpunit/includes/libs/objectcache/BagOStuffTest.php
tests/phpunit/includes/libs/objectcache/WANObjectCacheTest.php
tests/phpunit/includes/resourceloader/ResourceLoaderFilePathTest.php [deleted file]
tests/phpunit/includes/session/TestBagOStuff.php
tests/phpunit/includes/specials/SpecialMuteTest.php
tests/phpunit/unit/includes/SiteConfigurationTest.php
tests/phpunit/unit/includes/resourceloader/ResourceLoaderFilePathTest.php [new file with mode: 0644]
tests/selenium/wdio-mediawiki/CHANGELOG.md
tests/selenium/wdio-mediawiki/README.md
tests/selenium/wdio-mediawiki/package.json

index 60d5d96..57d635e 100644 (file)
@@ -36,6 +36,9 @@ For notes on 1.33.x and older releases, see HISTORY.
 * $wgEnableSpecialMute (T218265) - This configuration controls whether
   Special:Mute is available and whether to include a link to it on emails
   originating from Special:Email.
+* editmyuserjsredirect user right – users without this right now cannot edit JS
+  redirects in their userspace unless the target of the redirect is also in
+  their userspace. By default, this right is given to everyone.
 
 ==== Changed configuration ====
 * $wgUseCdn, $wgCdnServers, $wgCdnServersNoPurge, and $wgCdnMaxAge – These four
@@ -77,6 +80,8 @@ For notes on 1.33.x and older releases, see HISTORY.
   of headers in private wikis.
 * Language::formatTimePeriod now supports the new 'avoidhours' option to output
   strings like "5 days ago" instead of "5 days 13 hours ago".
+* (T220163) Added SpecialMuteModifyFormFields hook to allow extensions
+  to add fields to Special:Mute.
 
 === External library changes in 1.34 ===
 
@@ -298,6 +303,13 @@ because of Phabricator reports.
 * The parameter $lang of the functions generateTOC and tocList in Linker and
   DummyLinker must be in type Language when present. Other types are
   deprecated since 1.33.
+* The static properties mw.Api.errors and mw.Api.warnings, deprecated in 1.29,
+  have been removed.
+* The UploadVerification hook, deprecated in 1.28, has been removed. Instead,
+  use the UploadVerifyFile hook.
+* UploadBase:: and UploadFromChunks::stashFileGetKey() and stashSession(),
+  deprecated in 1.28, have been removed. Instead, please use the getFileKey()
+  method on the response from doStashFile().
 * …
 
 === Deprecations in 1.34 ===
index 9f9f1a6..5410bb8 100644 (file)
@@ -968,6 +968,7 @@ $wgAutoloadLocalClasses = [
        'MediaWiki\\Widget\\TitlesMultiselectWidget' => __DIR__ . '/includes/widget/TitlesMultiselectWidget.php',
        'MediaWiki\\Widget\\UserInputWidget' => __DIR__ . '/includes/widget/UserInputWidget.php',
        'MediaWiki\\Widget\\UsersMultiselectWidget' => __DIR__ . '/includes/widget/UsersMultiselectWidget.php',
+       'MediumSpecificBagOStuff' => __DIR__ . '/includes/libs/objectcache/MediumSpecificBagOStuff.php',
        'MemcLockManager' => __DIR__ . '/includes/libs/lockmanager/MemcLockManager.php',
        'MemcachedBagOStuff' => __DIR__ . '/includes/libs/objectcache/MemcachedBagOStuff.php',
        'MemcachedClient' => __DIR__ . '/includes/libs/objectcache/MemcachedClient.php',
index 36e0891..8e274ed 100644 (file)
@@ -3200,6 +3200,10 @@ $request: WebRequest object for getting the value provided by the current user
 &$oldTitle: old title (object)
 &$newTitle: new title (object)
 
+'SpecialMuteModifyFormFields': Add more fields to Special:Mute
+$sp: SpecialPage object, for context
+&$fields: Current HTMLForm fields descriptors
+
 'SpecialNewpagesConditions': Called when building sql query for
 Special:NewPages.
 &$special: NewPagesPager object (subclass of ReverseChronologicalPager)
@@ -3565,14 +3569,6 @@ $props: (array|null) File properties, as returned by
   MessageSpecifier instance (you might want to use ApiMessage to provide machine
   -readable details for the API).
 
-'UploadVerification': DEPRECATED since 1.28! Use UploadVerifyFile instead.
-Additional chances to reject an uploaded file.
-$saveName: (string) destination file name
-$tempName: (string) filesystem path to the temporary file for checks
-&$error: (string) output: message key for message to show if upload canceled by
-  returning false. May also be an array, where the first element is the message
-  key and the remaining elements are used as parameters to the message.
-
 'UploadVerifyFile': extra file verification, based on MIME type, etc. Preferred
 in most cases over UploadVerification.
 $upload: (object) an instance of UploadBase, with all info about the upload
index 3bfc8f8..107c546 100644 (file)
@@ -5173,6 +5173,7 @@ $wgGroupPermissions['user']['minoredit'] = true;
 $wgGroupPermissions['user']['editmyusercss'] = true;
 $wgGroupPermissions['user']['editmyuserjson'] = true;
 $wgGroupPermissions['user']['editmyuserjs'] = true;
+$wgGroupPermissions['user']['editmyuserjsredirect'] = true;
 $wgGroupPermissions['user']['purge'] = true;
 $wgGroupPermissions['user']['sendemail'] = true;
 $wgGroupPermissions['user']['applychangetags'] = true;
index d2f26b3..103ff45 100644 (file)
@@ -57,3 +57,6 @@ unset( $logDir );
 // Disable rate-limiting to allow integration tests to run unthrottled
 // in CI and for devs locally (T225796)
 $wgRateLimits = [];
+
+// Disable legacy javascript globals in CI and for devs (T72470)
+$wgLegacyJavaScriptGlobals = false;
index d27ef9c..f288327 100644 (file)
@@ -25,6 +25,7 @@ use MediaWiki\EditPage\TextboxBuilder;
 use MediaWiki\EditPage\TextConflictHelper;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\ScopedCallback;
 
 /**
@@ -1222,8 +1223,8 @@ class EditPage {
                                # the revisions exist and they were not deleted.
                                # Otherwise, $content will be left as-is.
                                if ( !is_null( $undorev ) && !is_null( $oldrev ) &&
-                                       !$undorev->isDeleted( Revision::DELETED_TEXT ) &&
-                                       !$oldrev->isDeleted( Revision::DELETED_TEXT )
+                                       !$undorev->isDeleted( RevisionRecord::DELETED_TEXT ) &&
+                                       !$oldrev->isDeleted( RevisionRecord::DELETED_TEXT )
                                ) {
                                        if ( WikiPage::hasDifferencesOutsideMainSlot( $undorev, $oldrev )
                                                || !$this->isSupportedContentModel( $oldrev->getContentModel() )
@@ -1245,7 +1246,7 @@ class EditPage {
                                        }
 
                                        if ( $undoMsg === null ) {
-                                               $oldContent = $this->page->getContent( Revision::RAW );
+                                               $oldContent = $this->page->getContent( RevisionRecord::RAW );
                                                $popts = ParserOptions::newFromUserAndLang(
                                                        $user, MediaWikiServices::getInstance()->getContentLanguage() );
                                                $newContent = $content->preSaveTransform( $this->mTitle, $user, $popts );
@@ -1371,7 +1372,7 @@ class EditPage {
                        $handler = ContentHandler::getForModelID( $this->contentModel );
                        return $handler->makeEmptyContent();
                }
-               $content = $revision->getContent( Revision::FOR_THIS_USER, $user );
+               $content = $revision->getContent( RevisionRecord::FOR_THIS_USER, $user );
                return $content;
        }
 
@@ -1405,7 +1406,7 @@ class EditPage {
         */
        protected function getCurrentContent() {
                $rev = $this->page->getRevision();
-               $content = $rev ? $rev->getContent( Revision::RAW ) : null;
+               $content = $rev ? $rev->getContent( RevisionRecord::RAW ) : null;
 
                if ( $content === false || $content === null ) {
                        $handler = ContentHandler::getForModelID( $this->contentModel );
@@ -1496,7 +1497,7 @@ class EditPage {
                }
 
                $parserOptions = ParserOptions::newFromUser( $user );
-               $content = $page->getContent( Revision::RAW );
+               $content = $page->getContent( RevisionRecord::RAW );
 
                if ( !$content ) {
                        // TODO: somehow show a warning to the user!
@@ -3139,12 +3140,12 @@ ERROR;
                                if ( $revision ) {
                                        // Let sysop know that this will make private content public if saved
 
-                                       if ( !$revision->userCan( Revision::DELETED_TEXT, $user ) ) {
+                                       if ( !$revision->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                                                $out->wrapWikiMsg(
                                                        "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                                        'rev-deleted-text-permission'
                                                );
-                                       } elseif ( $revision->isDeleted( Revision::DELETED_TEXT ) ) {
+                                       } elseif ( $revision->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                                                $out->wrapWikiMsg(
                                                        "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                                        'rev-deleted-text-view'
index 59efc98..8efae4f 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Feed
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Helper functions for feeds
  *
@@ -68,7 +70,7 @@ class FeedUtils {
                return self::formatDiffRow( $titleObj,
                        $row->rc_last_oldid, $row->rc_this_oldid,
                        $timestamp,
-                       $row->rc_deleted & Revision::DELETED_COMMENT
+                       $row->rc_deleted & RevisionRecord::DELETED_COMMENT
                                ? wfMessage( 'rev-deleted-comment' )->escaped()
                                : CommentStore::getStore()->getComment( 'rc_comment', $row )->text,
                        $actiontext
index c6c386c..7b4b502 100644 (file)
@@ -2521,6 +2521,7 @@ function wfForeignMemcKey( $db, $prefix, ...$args ) {
  * @return string
  */
 function wfGlobalCacheKey( ...$args ) {
+       wfDeprecated( __METHOD__, '1.30' );
        return ObjectCache::getLocalClusterInstance()->makeGlobalKey( ...$args );
 }
 
index f20795d..db3e2f5 100644 (file)
@@ -21,6 +21,7 @@
  */
 use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Some internal bits split of from Skin.php. These functions are used
@@ -1093,15 +1094,15 @@ class Linker {
         * @return string HTML fragment
         */
        public static function revUserLink( $rev, $isPublic = false ) {
-               if ( $rev->isDeleted( Revision::DELETED_USER ) && $isPublic ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) && $isPublic ) {
                        $link = wfMessage( 'rev-deleted-user' )->escaped();
-               } elseif ( $rev->userCan( Revision::DELETED_USER ) ) {
-                       $link = self::userLink( $rev->getUser( Revision::FOR_THIS_USER ),
-                               $rev->getUserText( Revision::FOR_THIS_USER ) );
+               } elseif ( $rev->userCan( RevisionRecord::DELETED_USER ) ) {
+                       $link = self::userLink( $rev->getUser( RevisionRecord::FOR_THIS_USER ),
+                               $rev->getUserText( RevisionRecord::FOR_THIS_USER ) );
                } else {
                        $link = wfMessage( 'rev-deleted-user' )->escaped();
                }
-               if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
                        return '<span class="history-deleted">' . $link . '</span>';
                }
                return $link;
@@ -1116,11 +1117,11 @@ class Linker {
         * @return string HTML
         */
        public static function revUserTools( $rev, $isPublic = false, $useParentheses = true ) {
-               if ( $rev->userCan( Revision::DELETED_USER ) &&
-                       ( !$rev->isDeleted( Revision::DELETED_USER ) || !$isPublic )
+               if ( $rev->userCan( RevisionRecord::DELETED_USER ) &&
+                       ( !$rev->isDeleted( RevisionRecord::DELETED_USER ) || !$isPublic )
                ) {
-                       $userId = $rev->getUser( Revision::FOR_THIS_USER );
-                       $userText = $rev->getUserText( Revision::FOR_THIS_USER );
+                       $userId = $rev->getUser( RevisionRecord::FOR_THIS_USER );
+                       $userText = $rev->getUserText( RevisionRecord::FOR_THIS_USER );
                        if ( $userId || (string)$userText !== '' ) {
                                $link = self::userLink( $userId, $userText )
                                        . self::userToolLinks( $userId, $userText, false, 0, null,
@@ -1132,7 +1133,7 @@ class Linker {
                        $link = wfMessage( 'rev-deleted-user' )->escaped();
                }
 
-               if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
                        return ' <span class="history-deleted mw-userlink">' . $link . '</span>';
                }
                return $link;
@@ -1571,18 +1572,18 @@ class Linker {
        public static function revComment( Revision $rev, $local = false, $isPublic = false,
                $useParentheses = true
        ) {
-               if ( $rev->getComment( Revision::RAW ) == "" ) {
+               if ( $rev->getComment( RevisionRecord::RAW ) == "" ) {
                        return "";
                }
-               if ( $rev->isDeleted( Revision::DELETED_COMMENT ) && $isPublic ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) && $isPublic ) {
                        $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
-               } elseif ( $rev->userCan( Revision::DELETED_COMMENT ) ) {
-                       $block = self::commentBlock( $rev->getComment( Revision::FOR_THIS_USER ),
+               } elseif ( $rev->userCan( RevisionRecord::DELETED_COMMENT ) ) {
+                       $block = self::commentBlock( $rev->getComment( RevisionRecord::FOR_THIS_USER ),
                                $rev->getTitle(), $local, null, $useParentheses );
                } else {
                        $block = " <span class=\"comment\">" . wfMessage( 'rev-deleted-comment' )->escaped() . "</span>";
                }
-               if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
                        return " <span class=\"history-deleted comment\">$block</span>";
                }
                return $block;
@@ -1875,10 +1876,10 @@ class Linker {
                $editCount = 0;
                $moreRevs = false;
                foreach ( $res as $row ) {
-                       if ( $rev->getUserText( Revision::RAW ) != $row->rev_user_text ) {
+                       if ( $rev->getUserText( RevisionRecord::RAW ) != $row->rev_user_text ) {
                                if ( $verify &&
-                                       ( $row->rev_deleted & Revision::DELETED_TEXT
-                                               || $row->rev_deleted & Revision::DELETED_USER
+                                       ( $row->rev_deleted & RevisionRecord::DELETED_TEXT
+                                               || $row->rev_deleted & RevisionRecord::DELETED_USER
                                ) ) {
                                        // If the user or the text of the revision we might rollback
                                        // to is deleted in some way we can't rollback. Similar to
@@ -2107,7 +2108,7 @@ class Linker {
                        return '';
                }
 
-               if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
+               if ( !$rev->userCan( RevisionRecord::DELETED_RESTRICTED, $user ) ) {
                        return self::revDeleteLinkDisabled( $canHide ); // revision was hidden from sysops
                }
                $prefixedDbKey = MediaWikiServices::getInstance()->getTitleFormatter()->
@@ -2130,7 +2131,7 @@ class Linker {
                        ];
                }
                return self::revDeleteLink( $query,
-                       $rev->isDeleted( Revision::DELETED_RESTRICTED ), $canHide );
+                       $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ), $canHide );
        }
 
        /**
index 98a5b17..a04b29c 100644 (file)
@@ -23,6 +23,8 @@ use Action;
 use Exception;
 use Hooks;
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\Revision\RevisionLookup;
+use MediaWiki\Revision\RevisionRecord;
 use MediaWiki\Session\SessionManager;
 use MediaWiki\Special\SpecialPageFactory;
 use MediaWiki\User\UserIdentity;
@@ -55,6 +57,9 @@ class PermissionManager {
        /** @var SpecialPageFactory */
        private $specialPageFactory;
 
+       /** @var RevisionLookup */
+       private $revisionLookup;
+
        /** @var string[] List of pages names anonymous user may see */
        private $whitelistRead;
 
@@ -130,6 +135,7 @@ class PermissionManager {
                'editmyusercss',
                'editmyuserjson',
                'editmyuserjs',
+               'editmyuserjsredirect',
                'editmywatchlist',
                'editsemiprotected',
                'editsitecss',
@@ -184,6 +190,7 @@ class PermissionManager {
 
        /**
         * @param SpecialPageFactory $specialPageFactory
+        * @param RevisionLookup $revisionLookup
         * @param string[] $whitelistRead
         * @param string[] $whitelistReadRegexp
         * @param bool $emailConfirmToEdit
@@ -195,6 +202,7 @@ class PermissionManager {
         */
        public function __construct(
                SpecialPageFactory $specialPageFactory,
+               RevisionLookup $revisionLookup,
                $whitelistRead,
                $whitelistReadRegexp,
                $emailConfirmToEdit,
@@ -205,6 +213,7 @@ class PermissionManager {
                NamespaceInfo $nsInfo
        ) {
                $this->specialPageFactory = $specialPageFactory;
+               $this->revisionLookup = $revisionLookup;
                $this->whitelistRead = $whitelistRead;
                $this->whitelistReadRegexp = $whitelistReadRegexp;
                $this->emailConfirmToEdit = $emailConfirmToEdit;
@@ -1134,6 +1143,20 @@ class PermissionManager {
                                && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' )
                        ) {
                                $errors[] = [ 'mycustomjsprotected', $action ];
+                       } elseif (
+                               $page->isUserJsConfigPage()
+                               && !$user->isAllowedAny( 'edituserjs', 'editmyuserjsredirect' )
+                       ) {
+                               // T207750 - do not allow users to edit a redirect if they couldn't edit the target
+                               $rev = $this->revisionLookup->getRevisionByTitle( $page );
+                               $content = $rev ? $rev->getContent( 'main', RevisionRecord::RAW ) : null;
+                               $target = $content ? $content->getUltimateRedirectTarget() : null;
+                               if ( $target && (
+                                               !$target->inNamespace( NS_USER )
+                                               || !preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $target->getText() )
+                               ) ) {
+                                       $errors[] = [ 'mycustomjsredirectprotected', $action ];
+                               }
                        }
                } else {
                        // Users need editmyuser* to edit their own CSS/JSON/JS subpages, except for
@@ -1409,24 +1432,20 @@ class PermissionManager {
         * to make bot-flagged actions through certain special pages.
         * Returns a "scope guard" variable; whenever that variable goes out of scope or is consumed
         * via ScopedCallback::consume(), the temporary rights are revoked.
+        *
+        * @since 1.34
+        *
         * @param UserIdentity $user
         * @param string|string[] $rights
         * @return ScopedCallback
         */
        public function addTemporaryUserRights( UserIdentity $user, $rights ) {
-               $nextKey = count( $this->temporaryUserRights[$user->getId()] ?? [] );
-               $this->temporaryUserRights[$user->getId()][$nextKey] = (array)$rights;
-               return new ScopedCallback( [ $this, 'revokeTemporaryUserRights' ], [ $user->getId(), $nextKey ] );
-       }
-
-       /**
-        * Revoke rights added by addTemporaryUserRights().
-        * @param int $userId
-        * @param int $rightsGroupKey Key in self::$temporaryUserRights
-        * @internal For use by addTemporaryUserRights() only.
-        */
-       public function revokeTemporaryUserRights( $userId, $rightsGroupKey ) {
-               unset( $this->temporaryUserRights[$userId][$rightsGroupKey] );
+               $userId = $user->getId();
+               $nextKey = count( $this->temporaryUserRights[$userId] ?? [] );
+               $this->temporaryUserRights[$userId][$nextKey] = (array)$rights;
+               return new ScopedCallback( function () use ( $userId, $nextKey ) {
+                       unset( $this->temporaryUserRights[$userId][$nextKey] );
+               } );
        }
 
        /**
index 4acb9c0..cf1cc94 100644 (file)
@@ -430,6 +430,16 @@ class RenderedRevision implements SlotRenderingProvider {
                                "$method: Prepared output has vary-revision-exists..."
                        );
                        return true;
+               } elseif (
+                       $out->getFlag( 'vary-revision-sha1' ) &&
+                       $out->getRevisionUsedSha1Base36() !== $this->revision->getSha1()
+               ) {
+                       // If a self-transclusion used the proposed page text, it must match the final
+                       // page content after PST transformations and automatically merged edit conflicts
+                       $this->saveParseLogger->info(
+                               "$method: Prepared output has vary-revision-sha1 with wrong SHA-1..."
+                       );
+                       return true;
                } else {
                        // NOTE: In the original fix for T135261, the output was discarded if 'vary-user' was
                        // set for a null-edit. The reason was that the original rendering in that case was
index 339c34c..1bb848f 100644 (file)
@@ -465,6 +465,7 @@ return [
                $config = $services->getMainConfig();
                return new PermissionManager(
                        $services->getSpecialPageFactory(),
+                       $services->getRevisionLookup(),
                        $config->get( 'WhitelistRead' ),
                        $config->get( 'WhitelistReadRegexp' ),
                        $config->get( 'EmailConfirmToEdit' ),
index 6caca29..4671d99 100644 (file)
@@ -269,23 +269,28 @@ class PageEditStash {
 
                if ( $editInfo->output->getFlag( 'vary-revision' ) ) {
                        // This can be used for the initial parse, e.g. for filters or doEditContent(),
-                       // but a second parse will be triggered in doEditUpdates(). This is not optimal.
+                       // but a second parse will be triggered in doEditUpdates() no matter what
                        $logger->info(
-                               "Cache for key '{key}' has vary_revision; post-insertion parse inevitable.",
-                               $context
-                       );
-               } elseif ( $editInfo->output->getFlag( 'vary-revision-id' ) ) {
-                       // Similar to the above if we didn't guess the ID correctly.
-                       $logger->debug(
-                               "Cache for key '{key}' has vary_revision_id; post-insertion parse possible.",
-                               $context
-                       );
-               } elseif ( $editInfo->output->getFlag( 'vary-revision-timestamp' ) ) {
-                       // Similar to the above if we didn't guess the timestamp correctly.
-                       $logger->debug(
-                               "Cache for key '{key}' has vary_revision_timestamp; post-insertion parse possible.",
+                               "Cache for key '{key}' has 'vary-revision'; post-insertion parse inevitable.",
                                $context
                        );
+               } else {
+                       static $flagsMaybeReparse = [
+                               // Similar to the above if we didn't guess the ID correctly
+                               'vary-revision-id',
+                               // Similar to the above if we didn't guess the timestamp correctly
+                               'vary-revision-timestamp',
+                               // Similar to the above if we didn't guess the content correctly
+                               'vary-revision-sha1'
+                       ];
+                       foreach ( $flagsMaybeReparse as $flag ) {
+                               if ( $editInfo->output->getFlag( $flag ) ) {
+                                       $logger->debug(
+                                               "Cache for key '{key}' has $flag; post-insertion parse possible.",
+                                               $context
+                                       );
+                               }
+                       }
                }
 
                return $editInfo;
index 95ccd9a..12d6641 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use MediaWiki\Permissions\PermissionManager;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Assert\Assert;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
@@ -3947,17 +3948,18 @@ class Title implements LinkTarget, IDBAccessObject {
                if ( $old->getId() === $new->getId() ) {
                        return ( $old_cmp === '>' && $new_cmp === '<' ) ?
                                [] :
-                               [ $old->getUserText( Revision::RAW ) ];
+                               [ $old->getUserText( RevisionRecord::RAW ) ];
                } elseif ( $old->getId() === $new->getParentId() ) {
                        if ( $old_cmp === '>=' && $new_cmp === '<=' ) {
-                               $authors[] = $old->getUserText( Revision::RAW );
-                               if ( $old->getUserText( Revision::RAW ) != $new->getUserText( Revision::RAW ) ) {
-                                       $authors[] = $new->getUserText( Revision::RAW );
+                               $authors[] = $oldUserText = $old->getUserText( RevisionRecord::RAW );
+                               $newUserText = $new->getUserText( RevisionRecord::RAW );
+                               if ( $oldUserText != $newUserText ) {
+                                       $authors[] = $newUserText;
                                }
                        } elseif ( $old_cmp === '>=' ) {
-                               $authors[] = $old->getUserText( Revision::RAW );
+                               $authors[] = $old->getUserText( RevisionRecord::RAW );
                        } elseif ( $new_cmp === '<=' ) {
-                               $authors[] = $new->getUserText( Revision::RAW );
+                               $authors[] = $new->getUserText( RevisionRecord::RAW );
                        }
                        return $authors;
                }
index ee60f7b..80fdf9d 100644 (file)
@@ -41,7 +41,7 @@ class TitleArrayFromResult extends TitleArray implements Countable {
        }
 
        /**
-        * @param bool|IResultWrapper $row
+        * @param bool|stdClass $row
         * @return void
         */
        protected function setCurrent( $row ) {
index f8ba08c..279c13b 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\Database;
 
 /**
@@ -543,7 +544,7 @@ class InfoAction extends FormlessAction {
                $batch = new LinkBatch;
 
                if ( $firstRev ) {
-                       $firstRevUser = $firstRev->getUserText( Revision::FOR_THIS_USER );
+                       $firstRevUser = $firstRev->getUserText( RevisionRecord::FOR_THIS_USER );
                        if ( $firstRevUser !== '' ) {
                                $firstRevUserTitle = Title::makeTitle( NS_USER, $firstRevUser );
                                $batch->addObj( $firstRevUserTitle );
@@ -552,7 +553,7 @@ class InfoAction extends FormlessAction {
                }
 
                if ( $lastRev ) {
-                       $lastRevUser = $lastRev->getUserText( Revision::FOR_THIS_USER );
+                       $lastRevUser = $lastRev->getUserText( RevisionRecord::FOR_THIS_USER );
                        if ( $lastRevUser !== '' ) {
                                $lastRevUserTitle = Title::makeTitle( NS_USER, $lastRevUser );
                                $batch->addObj( $lastRevUserTitle );
index e2fc265..519da61 100644 (file)
@@ -20,6 +20,8 @@
  * @ingroup Actions
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * User interface for the rollback action
  *
@@ -167,8 +169,8 @@ class RollbackAction extends FormAction {
                $this->getOutput()->addHTML(
                        $this->msg( 'rollback-success' )
                                ->rawParams( $old, $new )
-                               ->params( $current->getUserText( Revision::FOR_THIS_USER, $user ) )
-                               ->params( $target->getUserText( Revision::FOR_THIS_USER, $user ) )
+                               ->params( $current->getUserText( RevisionRecord::FOR_THIS_USER, $user ) )
+                               ->params( $target->getUserText( RevisionRecord::FOR_THIS_USER, $user ) )
                                ->parseAsBlock()
                );
 
index 99c57e1..c5c090d 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Revision\RevisionRecord;
 
 /**
  * @ingroup Pager
@@ -338,7 +339,9 @@ class HistoryPager extends ReverseChronologicalPager {
                        $this->preventClickjacking();
                        // If revision was hidden from sysops and we don't need the checkbox
                        // for anything else, disable it
-                       if ( !$this->showTagEditUI && !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
+                       if ( !$this->showTagEditUI
+                               && !$rev->userCan( RevisionRecord::DELETED_RESTRICTED, $user )
+                       ) {
                                $del = Xml::check( 'deleterevisions', false, [ 'disabled' => 'disabled' ] );
                        // Otherwise, enable the checkbox...
                        } else {
@@ -348,14 +351,14 @@ class HistoryPager extends ReverseChronologicalPager {
                // User can only view deleted revisions...
                } elseif ( $rev->getVisibility() && $user->isAllowed( 'deletedhistory' ) ) {
                        // If revision was hidden from sysops, disable the link
-                       if ( !$rev->userCan( Revision::DELETED_RESTRICTED, $user ) ) {
+                       if ( !$rev->userCan( RevisionRecord::DELETED_RESTRICTED, $user ) ) {
                                $del = Linker::revDeleteLinkDisabled( false );
                        // Otherwise, show the link...
                        } else {
                                $query = [ 'type' => 'revision',
                                        'target' => $this->getTitle()->getPrefixedDBkey(), 'ids' => $rev->getId() ];
                                $del .= Linker::revDeleteLink( $query,
-                                       $rev->isDeleted( Revision::DELETED_RESTRICTED ), false );
+                                       $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ), false );
                        }
                }
                if ( $del ) {
@@ -409,8 +412,8 @@ class HistoryPager extends ReverseChronologicalPager {
                                }
                        }
 
-                       if ( !$rev->isDeleted( Revision::DELETED_TEXT )
-                               && !$prevRev->isDeleted( Revision::DELETED_TEXT )
+                       if ( !$rev->isDeleted( RevisionRecord::DELETED_TEXT )
+                               && !$prevRev->isDeleted( RevisionRecord::DELETED_TEXT )
                        ) {
                                # Create undo tooltip for the first (=latest) line only
                                $undoTooltip = $latest
@@ -491,7 +494,9 @@ class HistoryPager extends ReverseChronologicalPager {
        function curLink( $rev ) {
                $cur = $this->historyPage->message['cur'];
                $latest = $this->getWikiPage()->getLatest();
-               if ( $latest === $rev->getId() || !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+               if ( $latest === $rev->getId()
+                       || !$rev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
+               ) {
                        return $cur;
                } else {
                        return MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
@@ -539,8 +544,8 @@ class HistoryPager extends ReverseChronologicalPager {
 
                $nextRev = new Revision( $next, 0, $this->getTitle() );
 
-               if ( !$prevRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
-                       || !$nextRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
+               if ( !$prevRev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
+                       || !$nextRev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
                ) {
                        return $last;
                }
@@ -579,7 +584,7 @@ class HistoryPager extends ReverseChronologicalPager {
                                $checkmark = [ 'checked' => 'checked' ];
                        } else {
                                # Check visibility of old revisions
-                               if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+                               if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
                                        $radio['disabled'] = 'disabled';
                                        $checkmark = []; // We will check the next possible one
                                } elseif ( !$this->oldIdChecked ) {
index f53d2b9..be2da34 100644 (file)
@@ -54,7 +54,7 @@ class ApiCSPReport extends ApiBase {
                        // XXX Is it ok to put untrusted data into log??
                        'csp-report' => $report,
                        'method' => __METHOD__,
-                       'user_id' => $this->getUser()->getId() || 'logged-out',
+                       'user_id' => $this->getUser()->getId() ?: 'logged-out',
                        'user-agent' => $userAgent,
                        'source' => $this->getParameter( 'source' ),
                ] );
index 96aea04..3f63a00 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * A module that allows for editing and creating pages.
  *
@@ -52,7 +54,7 @@ class ApiEditPage extends ApiBase {
                                $oldTitle = $titleObj;
 
                                $titles = Revision::newFromTitle( $oldTitle, false, Revision::READ_LATEST )
-                                       ->getContent( Revision::FOR_THIS_USER, $user )
+                                       ->getContent( RevisionRecord::FOR_THIS_USER, $user )
                                        ->getRedirectChain();
                                // array_shift( $titles );
 
@@ -193,14 +195,14 @@ class ApiEditPage extends ApiBase {
                                $undoafterRev = Revision::newFromId( $params['undoafter'] );
                        }
                        $undoRev = Revision::newFromId( $params['undo'] );
-                       if ( is_null( $undoRev ) || $undoRev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       if ( is_null( $undoRev ) || $undoRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                                $this->dieWithError( [ 'apierror-nosuchrevid', $params['undo'] ] );
                        }
 
                        if ( $params['undoafter'] == 0 ) {
                                $undoafterRev = $undoRev->getPrevious();
                        }
-                       if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       if ( is_null( $undoafterRev ) || $undoafterRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                                $this->dieWithError( [ 'apierror-nosuchrevid', $params['undoafter'] ] );
                        }
 
index 84fff96..a7390e6 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * @ingroup API
@@ -105,7 +106,7 @@ class ApiParse extends ApiBase {
                                }
 
                                $this->checkTitleUserPermissions( $rev->getTitle(), 'read' );
-                               if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+                               if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
                                        $this->dieWithError(
                                                [ 'apierror-permissiondenied', $this->msg( 'action-deletedtext' ) ]
                                        );
@@ -562,23 +563,23 @@ class ApiParse extends ApiBase {
                WikiPage $page, $popts, $suppressCache, $pageId, $rev, $getContent
        ) {
                $revId = $rev ? $rev->getId() : null;
-               $isDeleted = $rev && $rev->isDeleted( Revision::DELETED_TEXT );
+               $isDeleted = $rev && $rev->isDeleted( RevisionRecord::DELETED_TEXT );
 
                if ( $getContent || $this->section !== false || $isDeleted ) {
                        if ( $rev ) {
-                               $this->content = $rev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
+                               $this->content = $rev->getContent( RevisionRecord::FOR_THIS_USER, $this->getUser() );
                                if ( !$this->content ) {
                                        $this->dieWithError( [ 'apierror-missingcontent-revid', $revId ] );
                                }
                        } else {
-                               $this->content = $page->getContent( Revision::FOR_THIS_USER, $this->getUser() );
+                               $this->content = $page->getContent( RevisionRecord::FOR_THIS_USER, $this->getUser() );
                                if ( !$this->content ) {
                                        $this->dieWithError( [ 'apierror-missingcontent-pageid', $page->getId() ] );
                                }
                        }
                        $this->contentIsDeleted = $isDeleted;
                        $this->contentIsSuppressed = $rev &&
-                               $rev->isDeleted( Revision::DELETED_TEXT | Revision::DELETED_RESTRICTED );
+                               $rev->isDeleted( RevisionRecord::DELETED_TEXT | RevisionRecord::DELETED_RESTRICTED );
                }
 
                if ( $this->section !== false ) {
index 4fbc157..a6366f2 100644 (file)
@@ -22,6 +22,7 @@
 
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Storage\NameTableAccessException;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Query module to enumerate all deleted revisions.
@@ -197,9 +198,9 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                        // (shouldn't be able to get here without 'deletedhistory', but
                        // check it again just in case)
                        if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                               $bitmask = Revision::DELETED_USER;
+                               $bitmask = RevisionRecord::DELETED_USER;
                        } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                               $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                               $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                        } else {
                                $bitmask = 0;
                        }
@@ -288,11 +289,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                                $rev['parentid'] = (int)$row->ar_parent_id;
                        }
                        if ( $fld_user || $fld_userid ) {
-                               if ( $row->ar_deleted & Revision::DELETED_USER ) {
+                               if ( $row->ar_deleted & RevisionRecord::DELETED_USER ) {
                                        $rev['userhidden'] = true;
                                        $anyHidden = true;
                                }
-                               if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_USER, $user ) ) {
+                               if ( Revision::userCanBitfield( $row->ar_deleted, RevisionRecord::DELETED_USER, $user ) ) {
                                        if ( $fld_user ) {
                                                $rev['user'] = $row->ar_user_text;
                                        }
@@ -303,11 +304,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                        }
 
                        if ( $fld_comment || $fld_parsedcomment ) {
-                               if ( $row->ar_deleted & Revision::DELETED_COMMENT ) {
+                               if ( $row->ar_deleted & RevisionRecord::DELETED_COMMENT ) {
                                        $rev['commenthidden'] = true;
                                        $anyHidden = true;
                                }
-                               if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_COMMENT, $user ) ) {
+                               if ( Revision::userCanBitfield( $row->ar_deleted, RevisionRecord::DELETED_COMMENT, $user ) ) {
                                        $comment = $commentStore->getComment( 'ar_comment', $row )->text;
                                        if ( $fld_comment ) {
                                                $rev['comment'] = $comment;
@@ -326,11 +327,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                                $rev['len'] = $row->ar_len;
                        }
                        if ( $fld_sha1 ) {
-                               if ( $row->ar_deleted & Revision::DELETED_TEXT ) {
+                               if ( $row->ar_deleted & RevisionRecord::DELETED_TEXT ) {
                                        $rev['sha1hidden'] = true;
                                        $anyHidden = true;
                                }
-                               if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_TEXT, $user ) ) {
+                               if ( Revision::userCanBitfield( $row->ar_deleted, RevisionRecord::DELETED_TEXT, $user ) ) {
                                        if ( $row->ar_sha1 != '' ) {
                                                $rev['sha1'] = Wikimedia\base_convert( $row->ar_sha1, 36, 16, 40 );
                                        } else {
@@ -339,11 +340,11 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                                }
                        }
                        if ( $fld_content ) {
-                               if ( $row->ar_deleted & Revision::DELETED_TEXT ) {
+                               if ( $row->ar_deleted & RevisionRecord::DELETED_TEXT ) {
                                        $rev['texthidden'] = true;
                                        $anyHidden = true;
                                }
-                               if ( Revision::userCanBitfield( $row->ar_deleted, Revision::DELETED_TEXT, $user ) ) {
+                               if ( Revision::userCanBitfield( $row->ar_deleted, RevisionRecord::DELETED_TEXT, $user ) ) {
                                        ApiResult::setContentValue( $rev, 'text', Revision::getRevisionText( $row, 'ar_' ) );
                                }
                        }
@@ -358,7 +359,7 @@ class ApiQueryDeletedrevs extends ApiQueryBase {
                                }
                        }
 
-                       if ( $anyHidden && ( $row->ar_deleted & Revision::DELETED_RESTRICTED ) ) {
+                       if ( $anyHidden && ( $row->ar_deleted & RevisionRecord::DELETED_RESTRICTED ) ) {
                                $rev['suppressed'] = true;
                        }
 
index b0716b1..a8bcfc6 100644 (file)
@@ -525,9 +525,8 @@ class MessageCache {
                        __METHOD__ . "($code)-big"
                );
                foreach ( $res as $row ) {
-                       $name = $this->contLang->lcfirst( $row->page_title );
                        // Include entries/stubs for all keys in $mostused in adaptive mode
-                       if ( $wgAdaptiveMessageCache || $this->isMainCacheable( $name, $overridable ) ) {
+                       if ( $wgAdaptiveMessageCache || $this->isMainCacheable( $row->page_title, $overridable ) ) {
                                $cache[$row->page_title] = '!TOO BIG';
                        }
                        // At least include revision ID so page changes are reflected in the hash
@@ -549,9 +548,8 @@ class MessageCache {
                        $revQuery['joins']
                );
                foreach ( $res as $row ) {
-                       $name = $this->contLang->lcfirst( $row->page_title );
                        // Include entries/stubs for all keys in $mostused in adaptive mode
-                       if ( $wgAdaptiveMessageCache || $this->isMainCacheable( $name, $overridable ) ) {
+                       if ( $wgAdaptiveMessageCache || $this->isMainCacheable( $row->page_title, $overridable ) ) {
                                try {
                                        $rev = $revisionStore->newRevisionFromRow( $row );
                                        $content = $rev->getContent( MediaWiki\Revision\SlotRecord::MAIN );
@@ -592,14 +590,17 @@ class MessageCache {
        }
 
        /**
-        * @param string $name Message name with lowercase first letter
+        * @param string $name Message name (possibly with /code suffix)
         * @param array $overridable Map of (key => unused) for software-defined messages
         * @return bool
         */
        private function isMainCacheable( $name, array $overridable ) {
+               // Convert first letter to lowercase, and strip /code suffix
+               $name = $this->contLang->lcfirst( $name );
+               $msg = preg_replace( '/\/[a-z0-9-]{2,}$/', '', $name );
                // Include common conversion table pages. This also avoids problems with
                // Installer::parse() bailing out due to disallowed DB queries (T207979).
-               return ( isset( $overridable[$name] ) || strpos( $name, 'conversiontable/' ) === 0 );
+               return ( isset( $overridable[$msg] ) || strpos( $name, 'conversiontable/' ) === 0 );
        }
 
        /**
@@ -1069,8 +1070,7 @@ class MessageCache {
                        );
                } else {
                        // Message page either does not exist or does not override a software message
-                       $name = $this->contLang->lcfirst( $title );
-                       if ( !$this->isMainCacheable( $name, $this->overridable ) ) {
+                       if ( !$this->isMainCacheable( $title, $this->overridable ) ) {
                                // Message page does not override any software-defined message. A custom
                                // message might be defined to have content or settings specific to the wiki.
                                // Load the message page, utilizing the individual message cache as needed.
index f1e61bb..2ef9c9f 100644 (file)
@@ -1,4 +1,7 @@
 <?php
+
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Helper class for category membership changes
  *
@@ -158,7 +161,7 @@ class CategoryMembershipChange {
                $revision,
                $added
        ) {
-               $deleted = $revision ? $revision->getVisibility() & Revision::SUPPRESSED_USER : 0;
+               $deleted = $revision ? $revision->getVisibility() & RevisionRecord::SUPPRESSED_USER : 0;
                $newRevId = $revision ? $revision->getId() : 0;
 
                /**
@@ -214,9 +217,9 @@ class CategoryMembershipChange {
         */
        private function getUser() {
                if ( $this->revision ) {
-                       $userId = $this->revision->getUser( Revision::RAW );
+                       $userId = $this->revision->getUser( RevisionRecord::RAW );
                        if ( $userId === 0 ) {
-                               return User::newFromName( $this->revision->getUserText( Revision::RAW ), false );
+                               return User::newFromName( $this->revision->getUserText( RevisionRecord::RAW ), false );
                        } else {
                                return User::newFromId( $userId );
                        }
index 69c709c..79092ee 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Feed to Special:RecentChanges and Special:RecentChangesLinked.
@@ -117,7 +118,7 @@ class ChangesFeed {
                                FeedUtils::formatDiff( $obj ),
                                $url,
                                $obj->rc_timestamp,
-                               ( $obj->rc_deleted & Revision::DELETED_USER )
+                               ( $obj->rc_deleted & RevisionRecord::DELETED_USER )
                                        ? wfMessage( 'rev-deleted-user' )->escaped() : $obj->rc_user_text,
                                $talkpage
                        );
index ec9a061..aa6d9c9 100644 (file)
@@ -23,6 +23,7 @@
  */
 use MediaWiki\Linker\LinkRenderer;
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IResultWrapper;
 
 class ChangesList extends ContextSource {
@@ -409,7 +410,7 @@ class ChangesList extends ContextSource {
        public static function revDateLink( Revision $rev, User $user, Language $lang, $title = null ) {
                $ts = $rev->getTimestamp();
                $date = $lang->userTimeAndDate( $ts, $user );
-               if ( $rev->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( $rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                        $link = MediaWikiServices::getInstance()->getLinkRenderer()->makeKnownLink(
                                $title ?? $rev->getTitle(),
                                $date,
@@ -419,7 +420,7 @@ class ChangesList extends ContextSource {
                } else {
                        $link = htmlspecialchars( $date );
                }
-               if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $link = "<span class=\"history-deleted mw-changeslist-date\">$link</span>";
                }
                return $link;
@@ -468,7 +469,7 @@ class ChangesList extends ContextSource {
                        $rc->mAttribs['rc_type'] == RC_CATEGORIZE
                ) {
                        $diffLink = $this->message['diff'];
-               } elseif ( !self::userCan( $rc, Revision::DELETED_TEXT, $this->getUser() ) ) {
+               } elseif ( !self::userCan( $rc, RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
                        $diffLink = $this->message['diff'];
                } else {
                        $query = [
@@ -524,7 +525,7 @@ class ChangesList extends ContextSource {
                        [ 'class' => 'mw-changeslist-title' ],
                        $params
                );
-               if ( $this->isDeleted( $rc, Revision::DELETED_TEXT ) ) {
+               if ( $this->isDeleted( $rc, RevisionRecord::DELETED_TEXT ) ) {
                        $articlelink = '<span class="history-deleted">' . $articlelink . '</span>';
                }
                # To allow for boldening pages watched by this user
@@ -576,7 +577,7 @@ class ChangesList extends ContextSource {
         * @param RecentChange &$rc
         */
        public function insertUserRelatedLinks( &$s, &$rc ) {
-               if ( $this->isDeleted( $rc, Revision::DELETED_USER ) ) {
+               if ( $this->isDeleted( $rc, RevisionRecord::DELETED_USER ) ) {
                        $s .= ' <span class="history-deleted">' .
                                $this->msg( 'rev-deleted-user' )->escaped() . '</span>';
                } else {
@@ -613,7 +614,7 @@ class ChangesList extends ContextSource {
         * @return string
         */
        public function insertComment( $rc ) {
-               if ( $this->isDeleted( $rc, Revision::DELETED_COMMENT ) ) {
+               if ( $this->isDeleted( $rc, RevisionRecord::DELETED_COMMENT ) ) {
                        return ' <span class="history-deleted">' .
                                $this->msg( 'rev-deleted-comment' )->escaped() . '</span>';
                } else {
index 8f32ba2..62cf39e 100644 (file)
@@ -1,4 +1,7 @@
 <?php
+
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Generates a list of changes using an Enhanced system (uses javascript).
  *
@@ -409,7 +412,7 @@ class EnhancedChangesList extends ChangesList {
                if ( $type == RC_LOG ) {
                        $link = htmlspecialchars( $rcObj->timestamp );
                        # Revision link
-               } elseif ( !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() ) ) {
+               } elseif ( !ChangesList::userCan( $rcObj, RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
                        $link = Html::element( 'span', [ 'class' => 'history-deleted' ], $rcObj->timestamp );
                } else {
                        $link = $this->linkRenderer->makeKnownLink(
@@ -418,7 +421,7 @@ class EnhancedChangesList extends ChangesList {
                                [],
                                $params
                        );
-                       if ( static::isDeleted( $rcObj, Revision::DELETED_TEXT ) ) {
+                       if ( static::isDeleted( $rcObj, RevisionRecord::DELETED_TEXT ) ) {
                                $link = '<span class="history-deleted">' . $link . '</span> ';
                        }
                }
@@ -552,7 +555,7 @@ class EnhancedChangesList extends ChangesList {
                        if (
                                $isnew ||
                                $rcObj->mAttribs['rc_type'] == RC_CATEGORIZE ||
-                               !ChangesList::userCan( $rcObj, Revision::DELETED_TEXT, $this->getUser() )
+                               !ChangesList::userCan( $rcObj, RevisionRecord::DELETED_TEXT, $this->getUser() )
                        ) {
                                $links['total-changes'] = Html::rawElement( 'span', [], $nchanges[$n] );
                        } else {
index 2d60ca2..d448eae 100644 (file)
@@ -20,6 +20,7 @@
  * @file
  */
 use MediaWiki\Linker\LinkRenderer;
+use MediaWiki\Storage\RevisionRecord;
 
 class RCCacheEntryFactory {
 
@@ -79,7 +80,7 @@ class RCCacheEntryFactory {
                // Make user links
                $cacheEntry->userlink = $this->getUserLink( $cacheEntry );
 
-               if ( !ChangesList::isDeleted( $cacheEntry, Revision::DELETED_USER ) ) {
+               if ( !ChangesList::isDeleted( $cacheEntry, RevisionRecord::DELETED_USER ) ) {
                        $cacheEntry->usertalklink = Linker::userToolLinks(
                                $cacheEntry->mAttribs['rc_user'],
                                $cacheEntry->mAttribs['rc_user_text'],
@@ -104,7 +105,7 @@ class RCCacheEntryFactory {
         * @return bool
         */
        private function showDiffLinks( RecentChange $cacheEntry, User $user ) {
-               return ChangesList::userCan( $cacheEntry, Revision::DELETED_TEXT, $user );
+               return ChangesList::userCan( $cacheEntry, RevisionRecord::DELETED_TEXT, $user );
        }
 
        /**
@@ -281,7 +282,7 @@ class RCCacheEntryFactory {
         * @return string
         */
        private function getUserLink( RecentChange $cacheEntry ) {
-               if ( ChangesList::isDeleted( $cacheEntry, Revision::DELETED_USER ) ) {
+               if ( ChangesList::isDeleted( $cacheEntry, RevisionRecord::DELETED_USER ) ) {
                        $userLink = ' <span class="history-deleted">' .
                                $this->context->msg( 'rev-deleted-user' )->escaped() . '</span>';
                } else {
index 2bac909..1827aab 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Item class for a logging table row with its associated change tags.
@@ -49,7 +50,9 @@ class ChangeTagsLogItem extends RevisionItemBase {
        }
 
        public function canView() {
-               return LogEventsList::userCan( $this->row, Revision::SUPPRESSED_ALL, $this->list->getUser() );
+               return LogEventsList::userCan(
+                       $this->row, RevisionRecord::SUPPRESSED_ALL, $this->list->getUser()
+               );
        }
 
        public function canViewContent() {
index 65a7b7d..48dfc70 100644 (file)
@@ -26,6 +26,7 @@
  * @author Daniel Kinzler
  */
 
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Assert\Assert;
 use MediaWiki\Logger\LoggerFactory;
 use MediaWiki\MediaWikiServices;
@@ -1025,7 +1026,7 @@ abstract class ContentHandler {
                        [ 'rev_user_text' => $revQuery['fields']['rev_user_text'] ],
                        [
                                'rev_page' => $title->getArticleID(),
-                               $dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0'
+                               $dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0'
                        ],
                        __METHOD__,
                        [ 'LIMIT' => 20 ],
index c754cff..d43ffbc 100644 (file)
@@ -28,6 +28,7 @@ use MediaWiki\MediaWikiServices;
 use Wikimedia\Rdbms\LBFactory;
 use Wikimedia\Rdbms\ILBFactory;
 use Wikimedia\Rdbms\LoadBalancer;
+use Wikimedia\Rdbms\DBTransactionError;
 
 /**
  * Class for managing the deferred updates
@@ -352,28 +353,30 @@ class DeferredUpdates {
         * @since 1.34
         */
        public static function attemptUpdate( DeferrableUpdate $update, ILBFactory $lbFactory ) {
+               $ticket = $lbFactory->getEmptyTransactionTicket( __METHOD__ );
+               if ( !$ticket || $lbFactory->hasTransactionRound() ) {
+                       throw new DBTransactionError( null, "A database transaction round is pending." );
+               }
+
                if ( $update instanceof DataUpdate ) {
-                       $update->setTransactionTicket( $lbFactory->getEmptyTransactionTicket( __METHOD__ ) );
+                       $update->setTransactionTicket( $ticket );
                }
 
-               if (
+               $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+               $useExplicitTrxRound = !(
                        $update instanceof TransactionRoundAwareUpdate &&
                        $update->getTransactionRoundRequirement() == $update::TRX_ROUND_ABSENT
-               ) {
-                       $fnameTrxOwner = null;
+               );
+               // Flush any pending changes left over from an implicit transaction round
+               if ( $useExplicitTrxRound ) {
+                       $lbFactory->beginMasterChanges( $fnameTrxOwner ); // new explicit round
                } else {
-                       $fnameTrxOwner = get_class( $update ) . '::doUpdate';
+                       $lbFactory->commitMasterChanges( $fnameTrxOwner ); // new implicit round
                }
-
-               if ( $fnameTrxOwner !== null ) {
-                       $lbFactory->beginMasterChanges( $fnameTrxOwner );
-               }
-
+               // Run the update after any stale master view snapshots have been flushed
                $update->doUpdate();
-
-               if ( $fnameTrxOwner !== null ) {
-                       $lbFactory->commitMasterChanges( $fnameTrxOwner );
-               }
+               // Commit any pending changes from the explicit or implicit transaction round
+               $lbFactory->commitMasterChanges( $fnameTrxOwner );
        }
 
        /**
index 86d1a43..54cd9ca 100644 (file)
@@ -508,7 +508,7 @@ class DifferenceEngine extends ContextSource {
 
                # Check if one of the revisions is deleted/suppressed
                $deleted = $suppressed = false;
-               $allowed = $this->mNewRev->userCan( Revision::DELETED_TEXT, $user );
+               $allowed = $this->mNewRev->userCan( RevisionRecord::DELETED_TEXT, $user );
 
                $revisionTools = [];
 
@@ -554,8 +554,8 @@ class DifferenceEngine extends ContextSource {
                                        }
                                }
 
-                               if ( !$this->mOldRev->isDeleted( Revision::DELETED_TEXT ) &&
-                                       !$this->mNewRev->isDeleted( Revision::DELETED_TEXT )
+                               if ( !$this->mOldRev->isDeleted( RevisionRecord::DELETED_TEXT ) &&
+                                       !$this->mNewRev->isDeleted( RevisionRecord::DELETED_TEXT )
                                ) {
                                        $undoLink = Html::element( 'a', [
                                                        'href' => $this->mNewPage->getLocalURL( [
@@ -605,15 +605,15 @@ class DifferenceEngine extends ContextSource {
                        Hooks::run( 'DifferenceEngineOldHeader', [ $this, &$oldHeader, $prevlink, $oldminor,
                                $diffOnly, $ldel, $this->unhide ] );
 
-                       if ( $this->mOldRev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       if ( $this->mOldRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                                $deleted = true; // old revisions text is hidden
-                               if ( $this->mOldRev->isDeleted( Revision::DELETED_RESTRICTED ) ) {
+                               if ( $this->mOldRev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
                                        $suppressed = true; // also suppressed
                                }
                        }
 
                        # Check if this user can see the revisions
-                       if ( !$this->mOldRev->userCan( Revision::DELETED_TEXT, $user ) ) {
+                       if ( !$this->mOldRev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                                $allowed = false;
                        }
                }
@@ -675,9 +675,9 @@ class DifferenceEngine extends ContextSource {
                Hooks::run( 'DifferenceEngineNewHeader', [ $this, &$newHeader, $formattedRevisionTools,
                        $nextlink, $rollback, $newminor, $diffOnly, $rdel, $this->unhide ] );
 
-               if ( $this->mNewRev->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( $this->mNewRev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $deleted = true; // new revisions text is hidden
-                       if ( $this->mNewRev->isDeleted( Revision::DELETED_RESTRICTED ) ) {
+                       if ( $this->mNewRev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ) {
                                $suppressed = true; // also suppressed
                        }
                }
@@ -1011,11 +1011,11 @@ class DifferenceEngine extends ContextSource {
                        if ( !$this->loadRevisionData() ) {
                                return false;
                        } elseif ( $this->mOldRev &&
-                               !$this->mOldRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
+                               !$this->mOldRev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
                        ) {
                                return false;
                        } elseif ( $this->mNewRev &&
-                               !$this->mNewRev->userCan( Revision::DELETED_TEXT, $this->getUser() )
+                               !$this->mNewRev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() )
                        ) {
                                return false;
                        }
@@ -1465,7 +1465,7 @@ class DifferenceEngine extends ContextSource {
                        $users = $this->mNewPage->getAuthorsBetween( $oldRev, $newRev, $limit );
                        $numUsers = count( $users );
 
-                       if ( $numUsers == 1 && $users[0] == $newRev->getUserText( Revision::RAW ) ) {
+                       if ( $numUsers == 1 && $users[0] == $newRev->getUserText( RevisionRecord::RAW ) ) {
                                $numUsers = 0; // special case to say "by the same user" instead of "by one other user"
                        }
 
@@ -1530,7 +1530,7 @@ class DifferenceEngine extends ContextSource {
                $header = Linker::linkKnown( $title, $header, [],
                        [ 'oldid' => $rev->getId() ] );
 
-               if ( $rev->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( $rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                        $editQuery = [ 'action' => 'edit' ];
                        if ( !$rev->isCurrent() ) {
                                $editQuery['oldid'] = $rev->getId();
@@ -1545,7 +1545,7 @@ class DifferenceEngine extends ContextSource {
                                [ 'class' => 'mw-diff-edit' ],
                                $editLink
                        );
-                       if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+                       if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                                $header = Html::rawElement(
                                        'span',
                                        [ 'class' => 'history-deleted' ],
@@ -1869,13 +1869,17 @@ class DifferenceEngine extends ContextSource {
                }
 
                if ( $this->mOldRev ) {
-                       $this->mOldContent = $this->mOldRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
+                       $this->mOldContent = $this->mOldRev->getContent(
+                               RevisionRecord::FOR_THIS_USER, $this->getUser()
+                       );
                        if ( $this->mOldContent === null ) {
                                return false;
                        }
                }
 
-               $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
+               $this->mNewContent = $this->mNewRev->getContent(
+                       RevisionRecord::FOR_THIS_USER, $this->getUser()
+               );
                Hooks::run( 'DifferenceEngineLoadTextAfterNewContentIsLoaded', [ $this ] );
                if ( $this->mNewContent === null ) {
                        return false;
@@ -1900,7 +1904,9 @@ class DifferenceEngine extends ContextSource {
                        return false;
                }
 
-               $this->mNewContent = $this->mNewRev->getContent( Revision::FOR_THIS_USER, $this->getUser() );
+               $this->mNewContent = $this->mNewRev->getContent(
+                       RevisionRecord::FOR_THIS_USER, $this->getUser()
+               );
 
                Hooks::run( 'DifferenceEngineAfterLoadNewText', [ $this ] );
 
index 8b0ed00..fe6dadf 100644 (file)
@@ -28,6 +28,7 @@
  */
 
 use MediaWiki\MediaWikiServices as MediaWikiServicesAlias;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\IDatabase;
 
@@ -253,7 +254,7 @@ class WikiExporter {
                                'rev_user' => $revQuery['fields']['rev_user'],
                        ],
                        [
-                               $this->db->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0',
+                               $this->db->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER ) . ' = 0',
                                $cond,
                        ],
                        __METHOD__,
index f71b0d5..7ee6d85 100644 (file)
@@ -320,7 +320,7 @@ class XmlDumpWriter {
 
                $out .= $this->writeTimestamp( $rev->getTimestamp() );
 
-               if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
                        $out .= "      " . Xml::element( 'contributor', [ 'deleted' => 'deleted' ] ) . "\n";
                } else {
                        // empty values get written out as uid 0, see T224221
@@ -334,7 +334,7 @@ class XmlDumpWriter {
                if ( $rev->isMinor() ) {
                        $out .= "      <minor/>\n";
                }
-               if ( $rev->isDeleted( Revision::DELETED_COMMENT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_COMMENT ) ) {
                        $out .= "      " . Xml::element( 'comment', [ 'deleted' => 'deleted' ] ) . "\n";
                } else {
                        if ( $rev->getComment()->text != '' ) {
@@ -344,14 +344,14 @@ class XmlDumpWriter {
                        }
                }
 
-               $contentMode = $rev->isDeleted( Revision::DELETED_TEXT ) ? self::WRITE_STUB_DELETED
+               $contentMode = $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ? self::WRITE_STUB_DELETED
                        : $this->contentMode;
 
                foreach ( $rev->getSlots()->getSlots() as $slot ) {
                        $out .= $this->writeSlot( $slot, $contentMode );
                }
 
-               if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $out .= "      <sha1/>\n";
                } else {
                        $out .= "      " . Xml::element( 'sha1', null, strval( $rev->getSha1() ) ) . "\n";
index d447945..61faa09 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Helper class for file deletion
@@ -195,7 +196,7 @@ class LocalFileDeleteBatch {
 
                // Bitfields to further suppress the content
                if ( $this->suppress ) {
-                       $bitfield = Revision::SUPPRESSED_ALL;
+                       $bitfield = RevisionRecord::SUPPRESSED_ALL;
                } else {
                        $bitfield = 'oi_deleted';
                }
index cbcd3ba..860a351 100644 (file)
@@ -28,7 +28,7 @@ class HTMLNamespacesMultiselectField extends HTMLSelectNamespace {
        }
 
        public function validate( $value, $alldata ) {
-               if ( !$this->mParams['exists'] ) {
+               if ( !$this->mParams['exists'] || $value === '' ) {
                        return true;
                }
 
index 40c9417..4be13b0 100644 (file)
@@ -139,9 +139,9 @@ class ImportableUploadRevisionImporter implements UploadRevisionImporter {
 
        /**
         * @deprecated DO NOT CALL ME.
-        * This method was introduced when factoring UploadImporter out of WikiRevision.
-        * It only has 1 use by the deprecated downloadSource method in WikiRevision.
-        * Do not use this in new code.
+        * This method was introduced when factoring (Importable)UploadRevisionImporter out of
+        * WikiRevision. It only has 1 use by the deprecated downloadSource method in WikiRevision.
+        * Do not use this in new code, it will be made private soon.
         *
         * @param ImportableUploadRevision $wikiRevision
         *
index c006874..cae9542 100644 (file)
@@ -636,7 +636,7 @@ class WikiRevision implements ImportableUploadRevision, ImportableOldRevision {
 
        /**
         * @since 1.12.2
-        * @deprecated in 1.31. Use UploadImporter::import
+        * @deprecated in 1.31. Use UploadRevisionImporter::import
         * @return bool
         */
        public function importUpload() {
@@ -647,7 +647,7 @@ class WikiRevision implements ImportableUploadRevision, ImportableOldRevision {
 
        /**
         * @since 1.12.2
-        * @deprecated in 1.31. Use UploadImporter::downloadSource
+        * @deprecated in 1.31. No replacement
         * @return bool|string
         */
        public function downloadSource() {
index 33d4fcc..f6a5c41 100644 (file)
@@ -1764,7 +1764,9 @@ abstract class Installer {
        public static function overrideConfig() {
                // Use PHP's built-in session handling, since MediaWiki's
                // SessionHandler can't work before we have an object cache set up.
-               define( 'MW_NO_SESSION_HANDLER', 1 );
+               if ( !defined( 'MW_NO_SESSION_HANDLER' ) ) {
+                       define( 'MW_NO_SESSION_HANDLER', 1 );
+               }
 
                // Don't access the database
                $GLOBALS['wgUseDatabaseMessages'] = false;
index 33c06fe..db26c0b 100644 (file)
@@ -145,10 +145,6 @@ class WebInstaller extends Installer {
                parent::__construct();
                $this->output = new WebInstallerOutput( $this );
                $this->request = $request;
-
-               // Add parser hooks
-               $parser = MediaWikiServices::getInstance()->getParser();
-               $parser->setHook( 'doclink', [ $this, 'docLink' ] );
        }
 
        /**
@@ -1090,7 +1086,7 @@ class WebInstaller extends Installer {
        }
 
        /**
-        * Helper for Installer::docLink()
+        * Helper for WebInstallerOutput
         *
         * @internal For use by WebInstallerOutput
         * @param string $page
@@ -1106,21 +1102,6 @@ class WebInstaller extends Installer {
                return $this->getUrl( $query );
        }
 
-       /**
-        * Extension tag hook for a documentation link.
-        *
-        * @param string $linkText
-        * @param string[] $attribs
-        * @param Parser $parser Unused
-        *
-        * @return string
-        */
-       public function docLink( $linkText, $attribs, $parser ) {
-               $url = $this->getDocUrl( $attribs['href'] );
-
-               return Html::element( 'a', [ 'href' => $url ], $linkText );
-       }
-
        /**
         * Helper for sidebar links.
         *
index 28e8784..662a40d 100644 (file)
@@ -33,8 +33,12 @@ class WebInstallerWelcome extends WebInstallerPage {
                if ( $status->isGood() ) {
                        $this->parent->output->addHTML( '<span class="success-message">' .
                                wfMessage( 'config-env-good' )->escaped() . '</span>' );
-                       $this->parent->output->addWikiTextAsInterface( wfMessage( 'config-copyright',
-                               SpecialVersion::getCopyrightAndAuthorList() )->plain() );
+                       $this->parent->output->addWikiTextAsInterface(
+                               wfMessage( 'config-welcome-section-copyright',
+                                       SpecialVersion::getCopyrightAndAuthorList(),
+                                       wfExpandUrl( $this->parent->getDocUrl( 'Copying' ) )
+                               )->plain()
+                       );
                        $this->startForm();
                        $this->endForm();
                } else {
index 60e78b4..c901ab0 100644 (file)
@@ -51,7 +51,7 @@
        "config-help-restart": "هل تريد إزالة البيانات المحفوظة التي قد قمت بإدخالها وإعادة تشغيل عملية التثبيت؟",
        "config-restart": "نعم، إعادة التشغيل",
        "config-welcome": "=== التحقق من البيئة ===\nسوف يتم الآن التحقق من أن البيئة مناسبة لتنصيب ميديا ويكي.\nتذكر تضمين هذه المعلومات اذا اردت طلب المساعدة عن كيفية إكمال التنصيب.",
-       "config-copyright": "=== حقوق النسخ والشروط ===\n\n$1\n\nهذا البرنامج هو برنامج حر؛ يمكنك إعادة توزيعه و/أو تعديله تحت شروط رخصة جنو العامة على أن هذا البرنامج قد نُشر من قِبل مؤسسة البرمجيات الحرة؛ إما النسخة 2 من الرخصة، أو أي نسخة أخرى بعدها (من اختيارك).\n\nتم توزيع هذا البرنامج على أمل ان يكون مفيدًا ولكن <strong>دون أية ضمانات</strong>؛ دون حتى أية ضمانات مفهومة ضمنيًا أو رواجات أو أية أسباب محددة.\nانظر رخصة جنو العامة لمزيد من المعلومات.\n\nمن المفترض أنك استملت <doclink href=Copying> نسخة عن رخصة جنو العامة </doclink> مع هذا البرنامج؛ إذا لم تقعل اكتب رسالة إلى مؤسسة البرمجيات الحرة المحدودة، شارع 51 فرانكلين الطابق الخامس، بوسطن  MA 02110-1301 الولايات المتخدة أو [https://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-welcome-section-copyright": "=== حقوق النسخ والشروط ===\n\n$1\n\nهذا البرنامج هو برنامج حر؛ يمكنك إعادة توزيعه و/أو تعديله تحت شروط رخصة جنو العامة على أن هذا البرنامج قد نُشر من قِبل مؤسسة البرمجيات الحرة؛ إما النسخة 2 من الرخصة، أو أي نسخة أخرى بعدها (من اختيارك).\n\nتم توزيع هذا البرنامج على أمل ان يكون مفيدًا ولكن <strong>دون أية ضمانات</strong>؛ دون حتى أية ضمانات مفهومة ضمنيًا أو رواجات أو أية أسباب محددة.\nانظر رخصة جنو العامة لمزيد من المعلومات.\n\nمن المفترض أنك استملت [$2  نسخة عن رخصة جنو العامة ] مع هذا البرنامج؛ إذا لم تقعل اكتب رسالة إلى مؤسسة البرمجيات الحرة المحدودة، شارع 51 فرانكلين الطابق الخامس، بوسطن  MA 02110-1301 الولايات المتخدة أو [https://www.gnu.org/copyleft/gpl.html read it online].",
        "config-sidebar": "* [https://www.mediawiki.org موقع ميدياويكي]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents دليل المستخدم]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents دليل الإداري]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ الأسئلة المتكررة]",
        "config-sidebar-readme": "اقرأني",
        "config-sidebar-relnotes": "ملاحظات الإصدار",
index 512f032..12c6375 100644 (file)
@@ -44,7 +44,7 @@
        "config-help-restart": "¿Quies llimpiar tolos datos guardaos qu'introduxesti y reaniciar el procesu d'instalación?",
        "config-restart": "Sí, reanicialu",
        "config-welcome": "=== Comprobaciones del entornu ===\nAgora van facese unes comprobaciones básiques para ver si l'entornu ye afayadizu pa la instalación de MediaWiki.\nAlcuérdese d'incluir esta información si necesita encontu pa completar la instalación.",
-       "config-copyright": "=== Drechos d'autor y condiciones d'usu ===\n\n$1\n\nEsti programa ye software llibre; puedes redistribuilu y/o camudalu baxo les condiciones de la llicencia pública xeneral GNU tal como la publica la Free Software Foundation; versión 2 o (como prefieras) cualquier versión posterior.\n\nEsti programa distribúese cola esperanza de que pueda ser útil, pero <strong>ensin garantía denguna</strong>; nin siquiera la garantía implícita de <strong>comercialidá</strong> o \n<strong>adautación a un fin determináu</strong>.\nVer la Llicencia pública xeneral GNU pa más detalles.\n\nHabríes de tener recibío <doclink href=Copying>una copia de la llicencia pública xeneral GNU</doclink> xunto con esti programa; sinón, escribi a la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lléila en llinia].",
+       "config-welcome-section-copyright": "=== Drechos d'autor y condiciones d'usu ===\n\n$1\n\nEsti programa ye software llibre; puedes redistribuilu y/o camudalu baxo les condiciones de la llicencia pública xeneral GNU tal como la publica la Free Software Foundation; versión 2 o (como prefieras) cualquier versión posterior.\n\nEsti programa distribúese cola esperanza de que pueda ser útil, pero <strong>ensin garantía denguna</strong>; nin siquiera la garantía implícita de <strong>comercialidá</strong> o \n<strong>adautación a un fin determináu</strong>.\nVer la Llicencia pública xeneral GNU pa más detalles.\n\nHabríes de tener recibío [$2 una copia de la llicencia pública xeneral GNU] xunto con esti programa; sinón, escribi a la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lléila en llinia].",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/gl Páxina principal de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guía del usuariu]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guía del alministrador]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Entrugues frecuentes]\n----\n* <doclink href=Readme>Lléame</doclink>\n* <doclink href=ReleaseNotes>Notes de llanzamientu</doclink>\n* <doclink href=Copying>Copia</doclink>\n* <doclink href=UpgradeDoc>Anovamientu</doclink>",
        "config-env-good": "Comprobóse l'entornu.\nPues instalar MediaWiki.",
        "config-env-bad": "Comprobóse l'entornu.\nNun pues instalar MediaWiki.",
index e0d2bc9..e437503 100644 (file)
@@ -51,7 +51,7 @@
        "config-help-restart": "Һеҙ үҙегеҙ индергән һәм  һаҡланған әлеге мәғлүмәттәрҙе юйып, урынлаштырыуҙың яңы процессын ебәрергә теләйһегеҙме?",
        "config-restart": "Эйе, яңынан башларға",
        "config-welcome": "=== Даирәне тикшереү ===",
-       "config-copyright": "=== Авторлыҡ хоҡуҡтары һәм шарттар ===\n\n$1\n\nMediaWiki - ирекле программа тьәминәте. Һеҙ уны ирекле программа тьәминәте фонды баҫып сығарған GNU General Public License лицензия талаптарына ярашлы рәүештә тарата һәм/йәки үҙгәртә алаһығыҙ;икенсе версияһына йәки ниндәйҙә булһа һуңғы версияһына ярашлы рәүештә.\nMediaWiki - файҙалы булыу өмөтө менән таратыла, ләкин <strong> бер ниндәй ҙә гарантияларһыҙ</strong>, хатта күҙ уңында тотолған гарантияларһыҙ <strong> коммерция ҡимәтенән тыш </strong> йәки </strong> ниндәй ҙә булһа маҡсатҡа яраҡһыҙ </strong>. Ҡара. тулыраҡ мәғлүмәт алыу өсөн GNU General Public License лицезияһы. \nҺеҙ <doclink href=Copying> копияһын GNU General Public License</doclink>ошо программа менән бергә алырға тейеш инегеҙ, әгәр алмаһағыҙ, Free Software Foundation, Inc. ошо адрес буйынса яҙығыҙ:51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA йәки  [https://www.gnu.org/copyleft/gpl.html уны онлайнда уҡығыҙ].",
+       "config-welcome-section-copyright": "=== Авторлыҡ хоҡуҡтары һәм шарттар ===\n\n$1\n\nMediaWiki - ирекле программа тьәминәте. Һеҙ уны ирекле программа тьәминәте фонды баҫып сығарған GNU General Public License лицензия талаптарына ярашлы рәүештә тарата һәм/йәки үҙгәртә алаһығыҙ;икенсе версияһына йәки ниндәйҙә булһа һуңғы версияһына ярашлы рәүештә.\nMediaWiki - файҙалы булыу өмөтө менән таратыла, ләкин <strong> бер ниндәй ҙә гарантияларһыҙ</strong>, хатта күҙ уңында тотолған гарантияларһыҙ <strong> коммерция ҡимәтенән тыш </strong> йәки </strong> ниндәй ҙә булһа маҡсатҡа яраҡһыҙ </strong>. Ҡара. тулыраҡ мәғлүмәт алыу өсөн GNU General Public License лицезияһы. \nҺеҙ [$2  копияһын GNU General Public License]ошо программа менән бергә алырға тейеш инегеҙ, әгәр алмаһағыҙ, Free Software Foundation, Inc. ошо адрес буйынса яҙығыҙ:51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA йәки  [https://www.gnu.org/copyleft/gpl.html уны онлайнда уҡығыҙ].",
        "config-sidebar": "* [https://www.mediawiki.org Сайт MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/ru Ҡулланыусылар өсөн белешмә]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/ru Администраторҙар өсөн белешмә]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/ru FAQ]\n----\n* <doclink href=Readme>Readme-файл</doclink>\n* <doclink href=ReleaseNotes>Сығарылыш тураһында мәғлүмәт</doclink>\n* <doclink href=Copying>Лицензия</doclink>\n* <doclink href=UpgradeDoc>Яңыртыуҙар</doclink>",
        "config-env-good": "Мөхитте тикшереү уңышлы тамамланды. MediaWiki урынлаштырырға мөмкин.",
        "config-env-bad": "Мөхит тикшерелде. Һеҙ MediaWiki урынлаштыра алмайһығыҙ.",
index 136f3a2..7e0e6de 100644 (file)
@@ -47,7 +47,7 @@
        "config-help-restart": "Ці жадаеце выдаліць усе ўведзеныя зьвесткі і пачаць працэс усталяваньня зноў?",
        "config-restart": "Так, пачаць зноў",
        "config-welcome": "== Праверка асяродзьдзя ==\nЗараз будуць праведзеныя праверкі для запэўніваньня, што гэтае асяродзьдзе адпаведнае для ўсталяваньня MediaWiki.\nНе забудзьце далучыць гэтую інфармацыю, калі вам спатрэбіцца дапамога для завяршэньня ўсталяваньня.",
-       "config-copyright": "== Аўтарскае права і ўмовы ==\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but '''without any warranty'''; without even the implied warranty of '''merchantability''' or '''fitness for a particular purpose'''.\nSee the GNU General Public License for more details.\n\nYou should have received <doclink href=Copying>a copy of the GNU General Public License</doclink> along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. or [https://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-welcome-section-copyright": "== Аўтарскае права і ўмовы ==\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but '''without any warranty'''; without even the implied warranty of '''merchantability''' or '''fitness for a particular purpose'''.\nSee the GNU General Public License for more details.\n\nYou should have received [$2 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. or [https://www.gnu.org/copyleft/gpl.html read it online].",
        "config-sidebar": "* [https://www.mediawiki.org Хатняя старонка MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Даведка для ўдзельнікаў]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Даведка для адміністратараў]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Адказы на частыя пытаньні]",
        "config-sidebar-readme": "Прачытай мяне",
        "config-sidebar-relnotes": "Заўвагі да выпуску",
index 8074a14..24dd4f0 100644 (file)
        "config-help-restart": "Необходимо е потвърждение за изтриване на всички въведени и съхранени данни и започване отначало на процеса по инсталация.",
        "config-restart": "Да, започване отначало",
        "config-welcome": "=== Проверка на условията ===\nЩе бъдат извършени основни проверки, които да установят дали условията са подходящи за инсталиране на МедияУики.\nАко е необходима помощ по време на инсталацията, резултатите от направените проверки трябва също да бъдат предоставени.",
-       "config-copyright": "=== Авторски права и условия ===\n\n$1\n\nТази програма е свободен софтуер, който може да се променя и/или разпространява според Общия публичен лиценз на GNU, както е публикуван от Free Software Foundation във версия на Лиценза 2 или по-късна версия.\n\nТази програма се разпространява с надеждата, че ще е полезна, но <strong>без каквито и да е гаранции</strong>; без дори косвена гаранция за <strong>продаваемост</strong>  или <strong>пригодност за конкретна употреба</strong> .\nЗа повече подробности се препоръчва преглеждането на Общия публичен лиценз на GNU.\n\nКъм програмата трябва да е приложено <doclink href=Copying>копие на Общия публичен лиценз на GNU</doclink>; ако не, можете да пишете на Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, или да [https://www.gnu.org/copyleft/gpl.html го прочетете онлайн].",
-       "config-sidebar": "* [https://www.mediawiki.org Сайт на МедияУики]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Наръчник на потребителя]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Наръчник на администратора]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ЧЗВ]\n----\n* <doclink href=Readme>Документация</doclink>\n* <doclink href=ReleaseNotes>Бележки за версията</doclink>\n* <doclink href=Copying>Авторски права</doclink>\n* <doclink href=UpgradeDoc>Обновяване</doclink>",
+       "config-welcome-section-copyright": "=== Авторски права и условия ===\n\n$1\n\nТази програма е свободен софтуер, който може да се променя и/или разпространява според Общия публичен лиценз на GNU, както е публикуван от Free Software Foundation във версия на Лиценза 2 или по-късна версия.\n\nТази програма се разпространява с надеждата, че ще е полезна, но <strong>без каквито и да е гаранции</strong>; без дори косвена гаранция за <strong>продаваемост</strong>  или <strong>пригодност за конкретна употреба</strong> .\nЗа повече подробности се препоръчва преглеждането на Общия публичен лиценз на GNU.\n\nКъм програмата трябва да е приложено [$2 копие на Общия публичен лиценз на GNU]; ако не, можете да пишете на Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, или да [https://www.gnu.org/copyleft/gpl.html го прочетете онлайн].",
+       "config-sidebar": "* [https://www.mediawiki.org Сайт на МедияУики]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Наръчник на потребителя]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Наръчник на администратора]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ЧЗВ]",
        "config-env-good": "Средата беше проверена.\nИнсталирането на МедияУики е възможно.",
        "config-env-bad": "Средата беше проверена.\nНе е възможна инсталация на МедияУики.",
        "config-env-php": "PHP $1 е инсталирано.",
        "config-env-hhvm": "HHVM $1 е инсталиран.",
-       "config-unicode-using-intl": "Използване на разширението [https://pecl.php.net/intl intl PECL] за нормализация на Уникод.",
-       "config-unicode-pure-php-warning": "<strong>Внимание:</strong> [https://pecl.php.net/intl Разширението intl PECL] не е налично за справяне с нормализацията на Уникод, превключване към по-бавното изпълнение на чист PHP.\nАко сайтът е с голям трафик, препоръчително е да се запознаете с [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations нормализацията на Уникод].",
+       "config-unicode-using-intl": "Използване на разширението [https://php.net/manual/en/book.intl.php intl PECL] за нормализация на Уникод.",
+       "config-unicode-pure-php-warning": "<strong>Внимание:</strong> [https://php.net/manual/en/book.intl.php Разширението intl PECL] не е налично за справяне с нормализацията на Уникод, превключване към по-бавното изпълнение на чист PHP.\nАко сайтът е с голям трафик, препоръчително е да се запознаете с [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations нормализацията на Уникод].",
        "config-unicode-update-warning": "<strong>Предупреждение</strong>: Инсталираната версия на Обвивката за нормализация на Unicode използва по-старата версия на библиотеката на [http://site.icu-project.org/ проекта ICU].\nНеобходимо е да [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations инсталирате по-нова версия], в случай че сте загрижени за използването на Unicode.",
        "config-no-db": "Не може да бъде открит подходящ драйвер за база данни! Необходимо е да инсталирате драйвер за база данни за PHP.\n{{PLURAL:$2|Поддържа се следния тип|Поддържат се следните типове}} бази от данни: $1.\n\nАко сами сте компилирали PHP, преконфигурирайте го с включен клиент за база данни, например чрез използване на <code>./configure --with-mysqli</code>.\nАко сте инсталирали PHP от пакет за Debian или Ubuntu, необходимо е също така да инсталирате и модула <code>php-mysql</code>.",
-       "config-outdated-sqlite": "<strong>Внимание:</strong> имате инсталиран SQLite  $1, а минималната допустима версия е $2. SQLite ще бъде недостъпна за ползване.",
+       "config-outdated-sqlite": "<strong>Внимание:</strong> имате инсталиран SQLite $2, а минималната допустима версия е $1. SQLite ще бъде недостъпна за ползване.",
        "config-no-fts3": "<strong>Предупреждение:</strong> SQLite е компилирана без [//sqlite.org/fts3.html модула FTS3], затова възможностите за търсене няма да са достъпни.",
        "config-pcre-old": "<strong>Фатална грешка:</strong> Изисква се PCRE версия $1 или по-нова.\nИзпълнимият файл на PHP е свързан с PCRE версия $2.\n[https://www.mediawiki.org/wiki/Manual:Errors_and_symptoms/Повече информация за PCRE].",
        "config-pcre-no-utf8": "<strong>Фатално:</strong> Модулът PCRE на PHP изглежда е компилиран без поддръжка на PCRE_UTF8.\nЗа да функционира правилно, МедияУики изисква поддръжка на UTF-8.",
index 91f9bdb..93bec19 100644 (file)
@@ -46,7 +46,7 @@
        "config-help-restart": "Ha c'hoant hoc'h eus da ziverkañ an holl roadennoù hoc'h eus ebarzhet ha da adlañsañ an argerzh staliañ ?",
        "config-restart": "Ya, adloc'hañ anezhañ",
        "config-welcome": "=== Gwiriadennoù a denn d'an endro ===\nRekis eo un nebeud gwiriadennoù diazez da welet hag azas eo an endro evit gallout staliañ MediaWiki.\nHo pet soñj merkañ disoc'hoù ar gwiriadennoù-se m'ho pez ezhomm skoazell e-pad ar staliadenn.",
-       "config-copyright": "=== Gwiriañ aozer ha Termenoù implijout ===\n\n$1\n\nUr meziant frank eo ar programm-mañ; gallout a rit skignañ anezhañ ha/pe kemmañ anezhañ dindan termenoù ar GNU Aotre-implijout Foran Hollek evel m'emañ embannet gant Diazezadur ar Meziantoù Frank; pe diouzh stumm 2 an aotre-implijout, pe (evel mar karit) diouzh ne vern pe stumm nevesoc'h.\n\nIngalet eo ar programm gant ar spi e vo talvoudus met n'eus '''tamm gwarant ebet'''; hep zoken gwarant empleg ar '''varc'hadusted''' pe an '''azaster ouzh ur pal bennak'''. Gwelet ar GNU Aotre-Implijout Foran Hollek evit muioc'h a ditouroù.\n\nSañset oc'h bezañ resevet <doclink href=Copying>un eilskrid eus ar GNU Aotre-implijout Foran Hollek</doclink> a-gevret gant ar programm-mañ; ma n'hoc'h eus ket, skrivit da Diazezadur ar Meziantoù Frank/Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, SUA pe [https://www.gnu.org/licenses/old-licenses/gpl-2.0.html lennit anezhañ enlinenn].",
+       "config-welcome-section-copyright": "=== Gwiriañ aozer ha Termenoù implijout ===\n\n$1\n\nUr meziant frank eo ar programm-mañ; gallout a rit skignañ anezhañ ha/pe kemmañ anezhañ dindan termenoù ar GNU Aotre-implijout Foran Hollek evel m'emañ embannet gant Diazezadur ar Meziantoù Frank; pe diouzh stumm 2 an aotre-implijout, pe (evel mar karit) diouzh ne vern pe stumm nevesoc'h.\n\nIngalet eo ar programm gant ar spi e vo talvoudus met n'eus '''tamm gwarant ebet'''; hep zoken gwarant empleg ar '''varc'hadusted''' pe an '''azaster ouzh ur pal bennak'''. Gwelet ar GNU Aotre-Implijout Foran Hollek evit muioc'h a ditouroù.\n\nSañset oc'h bezañ resevet [$2 un eilskrid eus ar GNU Aotre-implijout Foran Hollek] a-gevret gant ar programm-mañ; ma n'hoc'h eus ket, skrivit da Diazezadur ar Meziantoù Frank/Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, SUA pe [https://www.gnu.org/licenses/old-licenses/gpl-2.0.html lennit anezhañ enlinenn].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki degemer]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Sturlevr an implijerien]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Sturlevr ar verourien]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAG]\n----\n* <doclink href=Readme>Lennit-me</doclink>\n* <doclink href=ReleaseNotes>Notennoù embann</doclink>\n* <doclink href=Copying>Oc'h eilañ</doclink>\n* <doclink href=UpgradeDoc>O hizivaat</doclink>",
        "config-env-good": "Gwiriet eo bet an endro.\nGallout a rit staliañ MediaWiki.",
        "config-env-bad": "Gwiriet eo bet an endro.\nNe c'hallit ket staliañ MediaWiki.",
index 44cd45e..2a358e7 100644 (file)
@@ -51,7 +51,7 @@
        "config-help-restart": "Voleu esborrar totes les dades que heu introduït i tornar a començar el procés d'instal·lació?",
        "config-restart": "Sí, torna a començar",
        "config-welcome": "=== Comprovacions de l'entorn ===\nS'efectuaran comprovacions bàsiques per veure si l'entorn és adequat per a la instal·lació del MediaWiki.\nRecordeu d'incloure aquesta informació si heu de demanar ajuda sobre com completar la instal·lació.",
-       "config-copyright": "=== Drets d'autor i condicions ===\n\n$1\n\nAquest programa és de programari lliure; podeu redistribuir-lo i/o modificar-lo sota les condicions de la Llicència Pública General GNU com es publicada per la Free Software Foundation; qualsevol versió 2 de la llicència, o (opcionalment) qualsevol versió posterior.\n\nAquest programa és distribueix amb l'esperança que serà útil, però <strong>sense cap garantia</strong>; sense ni tan sols la garantia implícita de <strong>\ncomerciabilitat</strong> o <strong>idoneïtat per a un propòsit particular</strong>.\nConsulteu la Llicència Pública General GNU, per a més detalls.\n\nHauríeu d'haver rebut <doclink href=\"Copying\">una còpia de la Llicència Pública General GNU</doclink> amb aquest programa; si no, escriviu a la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA o [https://www.gnu.org/copyleft/gpl.html per llegir-lo en línia].",
+       "config-welcome-section-copyright": "=== Drets d'autor i condicions ===\n\n$1\n\nAquest programa és de programari lliure; podeu redistribuir-lo i/o modificar-lo sota les condicions de la Llicència Pública General GNU com es publicada per la Free Software Foundation; qualsevol versió 2 de la llicència, o (opcionalment) qualsevol versió posterior.\n\nAquest programa és distribueix amb l'esperança que serà útil, però <strong>sense cap garantia</strong>; sense ni tan sols la garantia implícita de <strong>\ncomerciabilitat</strong> o <strong>idoneïtat per a un propòsit particular</strong>.\nConsulteu la Llicència Pública General GNU, per a més detalls.\n\nHauríeu d'haver rebut [$2 una còpia de la Llicència Pública General GNU] amb aquest programa; si no, escriviu a la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA o [https://www.gnu.org/copyleft/gpl.html per llegir-lo en línia].",
        "config-sidebar": "* [https://www.mediawiki.org la Pàgina d'inici]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guia de l'usuari]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guia de l'administrador]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ PMF]\n----\n* <doclink href=Readme>Llegeix-me</doclink>\n* <doclink href=ReleaseNotes>Notes de la versió</doclink>\n* <doclink href=Còpia>Còpia</doclink>\n* <doclink href=UpgradeDoc>Actualització</doclink>",
        "config-env-good": "S'ha comprovat l'entorn.\nPodeu instal·lar el MediaWiki.",
        "config-env-bad": "S'ha comprovat l'entorn.\nNo podeu instal·lar el MediaWiki.",
index be482e9..baba3e2 100644 (file)
@@ -27,7 +27,7 @@
        "config-page-copying": "Лицензи",
        "config-page-upgradedoc": "Карлаяккхар",
        "config-page-existingwiki": "Йолуш йолу вики",
-       "config-copyright": "=== Авторан бакъонаш а хьал а ===\n\n$1\nMediaWiki ю маьрша программин латораг, шу йиш ю фондас арахецна йолу GNU General Public License лицензица и яржо я хийца а.\n\nMediaWiki яржош ю и шуна пайдане хир яц те аьлла, амма цхьа юкъарахилар доцуш. Хь. кхин. лицензи мадарра GNU General Public License .\n\nШоьга кхача езаш яра [{{SERVER}}{{SCRIPTPATH}}/COPYING копи GNU General Public License] хӀокху программица, кхаьчна яцахь язъе Free Software Foundation, Inc., адрес тӀе: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA я [https://www.gnu.org/licenses/old-licenses/gpl-2.0.html еша и онлайнехь].",
+       "config-welcome-section-copyright": "=== Авторан бакъонаш а хьал а ===\n\n$1\nMediaWiki ю маьрша программин латораг, шу йиш ю фондас арахецна йолу GNU General Public License лицензица и яржо я хийца а.\n\nMediaWiki яржош ю и шуна пайдане хир яц те аьлла, амма цхьа юкъарахилар доцуш. Хь. кхин. лицензи мадарра GNU General Public License .\n\nШоьга кхача езаш яра [$2 копи GNU General Public License] хӀокху программица, кхаьчна яцахь язъе Free Software Foundation, Inc., адрес тӀе: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA я [https://www.gnu.org/licenses/old-licenses/gpl-2.0.html еша и онлайнехь].",
        "config-no-fts3": "'''Тергам бе''': SQLite гулйина хуттург йоцуш [//sqlite.org/fts3.html FTS3] — лахар болхбеш хир дац оцу бухца.",
        "config-no-cli-uri": "'''ДӀахьедар''': <code>--scriptpath</code> параметр язйина яц, иза Ӏадйитаран кепаца лелош ю: <code>$1</code> .",
        "config-db-name": "Хаамийн базан цӀе:",
index ad412d7..7c72530 100644 (file)
@@ -53,7 +53,7 @@
        "config-help-restart": "Chcete smazat všechny údaje, které jste zadali, a spustit proces instalace znovu od začátku?",
        "config-restart": "Ano, restartovat",
        "config-welcome": "=== Kontrola prostředí ===\nNyní se provedou základní kontroly, aby se zjistilo, zda je toto prostředí použitelné k instalaci MediaWiki.\nPokud budete potřebovat k dokončení instalace pomoc, nezapomeňte sdělit výsledky těchto testů.",
-       "config-copyright": "=== Licence a podmínky ===\n$1\n\nTento program je svobodný software; můžete jej šířit nebo modifikovat podle podmínek GNU General Public License, vydávané Free Software Foundation; buď verze 2 této licence anebo (podle vašeho uvážení) kterékoli pozdější verze.\n\nTento program je distribuován v naději, že bude užitečný, avšak '''bez jakékoli záruky'''; neposkytují se ani odvozené záruky '''prodejnosti''' anebo '''vhodnosti pro určitý účel'''.\nPodrobnosti se dočtete v textu GNU General Public License.\n\n<doclink href=Copying>Kopii GNU General Public License</doclink> jste měli obdržet spolu s tímto programem; pokud ne, napište na Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA nebo [https://www.gnu.org/copyleft/gpl.html si ji přečtěte online].",
+       "config-welcome-section-copyright": "=== Licence a podmínky ===\n$1\n\nTento program je svobodný software; můžete jej šířit nebo modifikovat podle podmínek GNU General Public License, vydávané Free Software Foundation; buď verze 2 této licence anebo (podle vašeho uvážení) kterékoli pozdější verze.\n\nTento program je distribuován v naději, že bude užitečný, avšak '''bez jakékoli záruky'''; neposkytují se ani odvozené záruky '''prodejnosti''' anebo '''vhodnosti pro určitý účel'''.\nPodrobnosti se dočtete v textu GNU General Public License.\n\n[$2 Kopii GNU General Public License] jste měli obdržet spolu s tímto programem; pokud ne, napište na Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA nebo [https://www.gnu.org/copyleft/gpl.html si ji přečtěte online].",
        "config-sidebar": "* [https://www.mediawiki.org Oficiální web MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Uživatelská příručka]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administrátorská příručka]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]",
        "config-sidebar-readme": "Čti mě",
        "config-sidebar-relnotes": "Poznámky k vydání",
index d307c8d..f10530e 100644 (file)
@@ -5,7 +5,7 @@
                        "Das Schäfchen"
                ]
        },
-       "config-copyright": "=== Lizenz und Nutzungsbedingungen ===\n\n$1\n\nDieses Programm ist freie Software, d. h. es kann, gemäss den Bedingungen der von der Free Software Foundation veröffentlichten ''GNU General Public License'', weiterverteilt und/oder modifiziert werden. Dabei kann die Version 2, oder nach eigenem Ermessen, jede neuere Version der Lizenz verwendet werden.\n\nDieses Programm wird in der Hoffnung verteilt, dass es nützlich sein wird, allerdings '''ohne jegliche Garantie''' und sogar ohne die implizierte Garantie einer '''Marktgängigkeit''' oder '''Eignung für einen bestimmten Zweck'''. Hierzu sind weitere Hinweise in der ''GNU General Public License'' enthalten.\n\nEine <doclink href=Copying>Kopie der GNU General Public License</doclink> sollte zusammen mit diesem Programm verteilt worden sein. Sofern dies nicht der Fall war, kann eine Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich angefordert oder auf deren Website [https://www.gnu.org/copyleft/gpl.html online gelesen] werden.",
+       "config-welcome-section-copyright": "=== Lizenz und Nutzungsbedingungen ===\n\n$1\n\nDieses Programm ist freie Software, d. h. es kann, gemäss den Bedingungen der von der Free Software Foundation veröffentlichten ''GNU General Public License'', weiterverteilt und/oder modifiziert werden. Dabei kann die Version 2, oder nach eigenem Ermessen, jede neuere Version der Lizenz verwendet werden.\n\nDieses Programm wird in der Hoffnung verteilt, dass es nützlich sein wird, allerdings '''ohne jegliche Garantie''' und sogar ohne die implizierte Garantie einer '''Marktgängigkeit''' oder '''Eignung für einen bestimmten Zweck'''. Hierzu sind weitere Hinweise in der ''GNU General Public License'' enthalten.\n\nEine [$2 Kopie der GNU General Public License] sollte zusammen mit diesem Programm verteilt worden sein. Sofern dies nicht der Fall war, kann eine Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich angefordert oder auf deren Website [https://www.gnu.org/copyleft/gpl.html online gelesen] werden.",
        "config-unicode-pure-php-warning": "'''Warnung:''' Die [https://pecl.php.net/intl PECL-Erweiterung intl] ist für die Unicode-Normalisierung nicht verfügbar, so dass stattdessen die langsame pure-PHP-Implementierung genutzt wird.\nSofern eine Website mit grosser Benutzeranzahl betrieben wird, sollten weitere Informationen auf der Webseite [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode-Normalisierung (en)] gelesen werden.",
        "config-uploads-not-safe": "'''Warnung:''' Das Standardverzeichnis für hochgeladene Dateien <code>$1</code> ist für die willkürliche Ausführung von Skripten anfällig.\nObwohl MediaWiki die hochgeladenen Dateien auf Sicherheitsrisiken überprüft, wird dennoch dringend empfohlen, diese [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Security#Upload_security Sicherheitslücke] zu schliessen, bevor das Hochladen von Dateien aktiviert wird.",
        "config-license-help": "Viele öffentliche Wikis publizieren alle Beiträge unter einer [https://freedomdefined.org/Definition/De freien Lizenz.]\nDies trägt dazu bei, ein Gefühl von Gemeinschaft zu schaffen, und ermutigt zu längerfristiger Mitarbeit.\nHingegen ist im Allgemeinen eine freie Lizenz auf geschlossenen Wikis nicht notwendig.\n\nSofern man Texte aus der Wikipedia verwenden möchte und umgekehrt, sollte die ''Creative-Commons''-Lizenz „Namensnennung – Weitergabe unter gleichen Bedingungen“ gewählt werden.\n\nDie Wikipedia nutzte vormals die GNU-Lizenz für freie Dokumentation (GFDL).\nDie GFDL ist eine gültige Lizenz, die allerdings schwer zu verstehen ist.\nEs ist zudem schwierig, gemäss dieser Lizenz lizenzierte Inhalte wiederzuverwenden."
index 6bf9921..b2c16c5 100644 (file)
@@ -56,7 +56,7 @@
        "config-help-restart": "Sollen alle bereits eingegebenen Daten gelöscht und der Installationsvorgang erneut gestartet werden?",
        "config-restart": "Ja, erneut starten",
        "config-welcome": "=== Prüfung der Installationsumgebung ===\nDie Basisprüfungen werden jetzt durchgeführt, um festzustellen, ob die Installationsumgebung für MediaWiki geeignet ist.\nNotiere diese Informationen und gib sie an, sofern du Hilfe beim Installieren benötigst.",
-       "config-copyright": "=== Lizenz und Nutzungsbedingungen ===\n\n$1\n\nDieses Programm ist freie Software, d. h. es kann, gemäß den Bedingungen der von der Free Software Foundation veröffentlichten ''GNU General Public License'', weiterverteilt und/oder modifiziert werden. Dabei kann die Version 2, oder nach eigenem Ermessen, jede neuere Version der Lizenz verwendet werden.\n\nDieses Programm wird in der Hoffnung verteilt, dass es nützlich sein wird, allerdings '''ohne jegliche Garantie''' und sogar ohne die implizierte Garantie einer '''Marktgängigkeit''' oder '''Eignung für einen bestimmten Zweck'''. Hierzu sind weitere Hinweise in der ''GNU General Public License'' enthalten.\n\nEine <doclink href=Copying>Kopie der GNU General Public License</doclink> sollte zusammen mit diesem Programm verteilt worden sein. Sofern dies nicht der Fall war, kann eine Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich angefordert oder auf deren Website [https://www.gnu.org/copyleft/gpl.html online gelesen] werden.",
+       "config-welcome-section-copyright": "=== Lizenz und Nutzungsbedingungen ===\n\n$1\n\nDieses Programm ist freie Software, d. h. es kann, gemäß den Bedingungen der von der Free Software Foundation veröffentlichten ''GNU General Public License'', weiterverteilt und/oder modifiziert werden. Dabei kann die Version 2, oder nach eigenem Ermessen, jede neuere Version der Lizenz verwendet werden.\n\nDieses Programm wird in der Hoffnung verteilt, dass es nützlich sein wird, allerdings '''ohne jegliche Garantie''' und sogar ohne die implizierte Garantie einer '''Marktgängigkeit''' oder '''Eignung für einen bestimmten Zweck'''. Hierzu sind weitere Hinweise in der ''GNU General Public License'' enthalten.\n\nEine [$2 Kopie der GNU General Public License] sollte zusammen mit diesem Programm verteilt worden sein. Sofern dies nicht der Fall war, kann eine Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich angefordert oder auf deren Website [https://www.gnu.org/copyleft/gpl.html online gelesen] werden.",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/de Website von MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/de Benutzer­anleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/de Administratoren­anleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/de Häufig gestellte Fragen]\n----\n* <doclink href=Readme>Lies mich</doclink>\n* <doclink href=ReleaseNotes>Versions­informationen</doclink>\n* <doclink href=Copying>Lizenz­bestimmungen</doclink>\n* <doclink href=UpgradeDoc>Aktualisierung</doclink>",
        "config-env-good": "Die Installationsumgebung wurde geprüft.\nMediaWiki kann installiert werden.",
        "config-env-bad": "Die Installationsumgebung wurde geprüft.\nMediaWiki kann nicht installiert werden.",
index 896a453..c5b07fa 100644 (file)
@@ -41,7 +41,7 @@
        "config-help-restart": "Do you want to clear all saved data that you have entered and restart the installation process?",
        "config-restart": "हुन्छ, पुनः सुचारू गद्दे",
        "config-welcome": "=== Environmental checks ===\nBasic checks will now be performed to see if this environment is suitable for MediaWiki installation.\nRemember to include this information if you seek support on how to complete the installation.",
-       "config-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but <strong>without any warranty</strong>; without even the implied warranty of <strong>merchantability</strong> or <strong>fitness for a particular purpose</strong>.\nSee the GNU General Public License for more details.\n\nYou should have received <doclink href=Copying>a copy of the GNU General Public License</doclink> along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, or [https://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-welcome-section-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but <strong>without any warranty</strong>; without even the implied warranty of <strong>merchantability</strong> or <strong>fitness for a particular purpose</strong>.\nSee the GNU General Public License for more details.\n\nYou should have received [$2 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, or [https://www.gnu.org/copyleft/gpl.html read it online].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki home]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administrator's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>Read me</doclink>\n* <doclink href=ReleaseNotes>Release notes</doclink>\n* <doclink href=Copying>Copying</doclink>\n* <doclink href=UpgradeDoc>Upgrading</doclink>",
        "config-env-good": "The environment has been checked.\nYou can install MediaWiki.",
        "config-env-bad": "The environment has been checked.\nYou cannot install MediaWiki.",
index b7a4e06..21a0e7d 100644 (file)
@@ -52,7 +52,7 @@
        "config-help-restart": "Θέλετε να καταργήσετε όλα τα αποθηκευμένα δεδομένα που έχετε εισαγάγει και να επανεκκινήσετε τη διαδικασία εγκατάστασης;",
        "config-restart": "Ναι, επανεκκίνηση",
        "config-welcome": "=== Έλεγχοι του περιβάλλοντος ===\nΤώρα θα γίνουν βασικοί έλεγχοι για να δούμε αν αυτό το περιβάλλον είναι κατάλληλο για την εγκατάσταση του MediaWiki.\nΘυμηθείτε να συμπεριλάβετε αυτές τις πληροφορίες εάν αναζητήσετε υποστήριξη για να ολοκληρώσετε την εγκατάσταση.",
-       "config-copyright": "=== Πνευματικά δικαιώματα και όροι ===\n\n$1\n\nΑυτό το πρόγραμμα είναι ελεύθερο λογισμικό· μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό τους όρους της Γενικής Άδειας Δημόσιας Χρήσης GNU, όπως αυτή δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού· είτε της έκδοσης 2 της Άδειας, είτε (κατ' επιλογήν σας) οποιασδήποτε μεταγενέστερης έκδοσης.\n\nΑυτό το πρόγραμμα διανέμεται με την ελπίδα ότι θα είναι χρήσιμο, αλλά <strong>χωρίς καμία εγγύηση</strong>· χωρίς καν τη σιωπηρή εγγύηση της <strong>εμπορευσιμότητας</strong> ή <strong>καταλληλότητας για συγκεκριμένο σκοπό</strong>.\nΔείτε τη Γενική Άδεια Δημόσιας Χρήσης GNU για περισσότερες λεπτομέρειες.\n\nΘα πρέπει να έχετε παραλάβει <doclink href=\"Copying\">ένα αντίγραφο της Γενικής Άδειας Δημόσιας Χρήσης GNU</doclink> μαζί με αυτό το πρόγραμμα· αν όχι, στείλτε ένα γράμμα στο Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, ή [https://www.gnu.org/copyleft/gpl.html διαβάστε το διαδικτυακά].",
+       "config-welcome-section-copyright": "=== Πνευματικά δικαιώματα και όροι ===\n\n$1\n\nΑυτό το πρόγραμμα είναι ελεύθερο λογισμικό· μπορείτε να το αναδιανείμετε ή/και να το τροποποιήσετε υπό τους όρους της Γενικής Άδειας Δημόσιας Χρήσης GNU, όπως αυτή δημοσιεύεται από το Ίδρυμα Ελεύθερου Λογισμικού· είτε της έκδοσης 2 της Άδειας, είτε (κατ' επιλογήν σας) οποιασδήποτε μεταγενέστερης έκδοσης.\n\nΑυτό το πρόγραμμα διανέμεται με την ελπίδα ότι θα είναι χρήσιμο, αλλά <strong>χωρίς καμία εγγύηση</strong>· χωρίς καν τη σιωπηρή εγγύηση της <strong>εμπορευσιμότητας</strong> ή <strong>καταλληλότητας για συγκεκριμένο σκοπό</strong>.\nΔείτε τη Γενική Άδεια Δημόσιας Χρήσης GNU για περισσότερες λεπτομέρειες.\n\nΘα πρέπει να έχετε παραλάβει [$2 ένα αντίγραφο της Γενικής Άδειας Δημόσιας Χρήσης GNU] μαζί με αυτό το πρόγραμμα· αν όχι, στείλτε ένα γράμμα στο Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, ή [https://www.gnu.org/copyleft/gpl.html διαβάστε το διαδικτυακά].",
        "config-sidebar": "* [https://www.mediawiki.org Αρχική MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Οδηγός Χρήστη]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Οδηγός Διαχειριστή]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Συχνές ερωτήσεις]\n----\n* <doclink href=\"Readme\">Διαβάστε με</doclink>\n* <doclink href=\"ReleaseNotes\">Σημειώσεις έκδοσης</doclink>\n* <doclink href=\"Copying\">Αντιγραφή</doclink>\n* <doclink href=\"UpgradeDoc\">Αναβάθμιση</doclink>",
        "config-env-good": "Το περιβάλλον έχει ελεγχθεί.\nΜπορείτε να εγκαταστήσετε το MediaWiki.",
        "config-env-bad": "Το περιβάλλον έχει ελεγχθεί.\nΔεν μπορείτε να εγκαταστήσετε το MediaWiki.",
index 7476c89..7f81a88 100644 (file)
@@ -8,7 +8,7 @@
        "config-desc": "The installer for MediaWiki",
        "config-title": "MediaWiki $1 installation",
        "config-information": "Information",
-       "config-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation; either version 2 of the Licence, or (at your option) any later version.\n\nThis 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'''.\nSee the GNU General Public Licence for more details.\n\nYou should have received <doclink href=Copying>a copy of the GNU General Public Licence</doclink> along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. or [https://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-welcome-section-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public Licence as published by the Free Software Foundation; either version 2 of the Licence, or (at your option) any later version.\n\nThis 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'''.\nSee the GNU General Public Licence for more details.\n\nYou should have received [$2 a copy of the GNU General Public Licence] along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. or [https://www.gnu.org/copyleft/gpl.html read it online].",
        "config-unicode-using-intl": "Using the [https://pecl.php.net/intl intl PECL extension] for Unicode normalisation.",
        "config-unicode-pure-php-warning": "'''Warning:''' The [https://pecl.php.net/intl intl PECL extension] is not available to handle Unicode normalisation, falling back to slow pure-PHP implementation.\nIf you run a high-traffic site, you should read a little on [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicode normalisation].",
        "config-unicode-update-warning": "'''Warning:''' The installed version of the Unicode normalisation wrapper uses an older version of [http://site.icu-project.org/ the ICU project's] library.\nYou should [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations upgrade] if you are at all concerned about using Unicode.",
index 5f5e452..52d8d3d 100644 (file)
@@ -39,7 +39,7 @@
        "config-help-restart": "Do you want to clear all saved data that you have entered and restart the installation process?",
        "config-restart": "Yes, restart it",
        "config-welcome": "=== Environmental checks ===\nBasic checks will now be performed to see if this environment is suitable for MediaWiki installation.\nRemember to include this information if you seek support on how to complete the installation.",
-       "config-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but <strong>without any warranty</strong>; without even the implied warranty of <strong>merchantability</strong> or <strong>fitness for a particular purpose</strong>.\nSee the GNU General Public License for more details.\n\nYou should have received <doclink href=Copying>a copy of the GNU General Public License</doclink> along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, or [https://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-welcome-section-copyright": "=== Copyright and Terms ===\n\n$1\n\nThis program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful, but <strong>without any warranty</strong>; without even the implied warranty of <strong>merchantability</strong> or <strong>fitness for a particular purpose</strong>.\nSee the GNU General Public License for more details.\n\nYou should have received [$2 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, or [https://www.gnu.org/copyleft/gpl.html read it online].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki home]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administrator's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]",
        "config-sidebar-readme": "Read me",
        "config-sidebar-relnotes": "Release notes",
index 196e2d4..c1f393a 100644 (file)
@@ -81,7 +81,7 @@
        "config-help-restart": "¿Deseas borrar todos los datos guardados que has escrito y reiniciar el proceso de instalación?",
        "config-restart": "Sí, reiniciarlo",
        "config-welcome": "=== Comprobación del entorno ===\nAhora se van a realizar comprobaciones básicas para ver si el entorno es adecuado para la instalación de MediaWiki.\nRecuerda suministrar los resultados de tales comprobaciones si necesitas ayuda para completar la instalación.",
-       "config-copyright": "=== Derechos de autor y Términos de uso ===\n\n$1\n\nEste programa es software libre; puedes redistribuirlo y/o modificarlo en los términos de la Licencia Pública General de GNU, tal como aparece publicada por la Fundación para el Software Libre, tanto la versión 2 de la Licencia, como cualquier versión posterior (según prefieras).\n\nEste programa es distribuido con la esperanza de que sea útil, pero <strong>sin ninguna garantía</strong>; inclusive, sin la garantía implícita de la <strong>posibilidad de ser comercializado</strong> o de <strong>idoneidad para cualquier finalidad específica</strong>.\nConsulta la Licencia Pública General de GNU para más detalles.\n\nEn conjunto con este programa debes haber recibido <doclink href=Copying>una copia de la Licencia Pública General de GNU</doclink>; caso contrario, pídela por escrito a la Fundación para el Software Libre, Inc., 51 Franklin Street, Fifth Floor, Boston, ME La 02110-1301, USA o [https://www.gnu.org/copyleft/gpl.html léela en Internet].",
+       "config-welcome-section-copyright": "=== Derechos de autor y Términos de uso ===\n\n$1\n\nEste programa es software libre; puedes redistribuirlo y/o modificarlo en los términos de la Licencia Pública General de GNU, tal como aparece publicada por la Fundación para el Software Libre, tanto la versión 2 de la Licencia, como cualquier versión posterior (según prefieras).\n\nEste programa es distribuido con la esperanza de que sea útil, pero <strong>sin ninguna garantía</strong>; inclusive, sin la garantía implícita de la <strong>posibilidad de ser comercializado</strong> o de <strong>idoneidad para cualquier finalidad específica</strong>.\nConsulta la Licencia Pública General de GNU para más detalles.\n\nEn conjunto con este programa debes haber recibido [$2 una copia de la Licencia Pública General de GNU]; caso contrario, pídela por escrito a la Fundación para el Software Libre, Inc., 51 Franklin Street, Fifth Floor, Boston, ME La 02110-1301, USA o [https://www.gnu.org/copyleft/gpl.html léela en Internet].",
        "config-sidebar": "* [https://www.mediawiki.org Página principal de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guía de usuario]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guía para administradores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Preguntas frecuentes]",
        "config-sidebar-readme": "Léeme",
        "config-sidebar-relnotes": "Informe de novedades",
index 61a1d2f..21c7fe2 100644 (file)
@@ -46,7 +46,7 @@
        "config-help-restart": "Ezabatu nahi duzu gorde duzun  informazio guztia eta berrebiarazi instalazio prozesua?",
        "config-restart": "Bai, berriz hasi",
        "config-welcome": "=== Ingurumen-egiaztapenak ===\n\nOinarrizko kontrola burutzen ari da, ikusteko ia ingurumena aproposa da MediaWikia instalatzeko.\nLaguntza behar izanez gero instalazio prozesua amaitzeko ez ahaztu sartzea informazio hau .",
-       "config-copyright": "=== Copyright eta terminoak ===\n\n$1\n\nPrograma hau software librea da; birbana eta / edo alda dezakezu GNU Lizentzia Publiko Orokorraren baldintzapean, Free Software Foundation-ek argitaratutakoaren arabera; Lizentziaren 2. bertsioa edo (nahiago baduzu) bertsio berriago bat.\n\nPrograma hau baliagarria izango delakoan elkarbantzen da, baina <strong> bermerik gabe </ strong>; <strong> merkaturatze </ strong> edo <strong>  helburu jakin baterako gaitasuna</ strong> berme inplizitua ere izan gabe.\nIkus GNU Lizentzia Publiko Orokorra xehetasun gehiagorako.\n\n<Doclink href = Kopiatzea> izan beharko zenuke GNU Lizentzia Publiko Orokorraren kopia </ doclink> programa honekin batera; bestela, idatzi Free Software Foundation-en, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, AEB, edo [https://www.gnu.org/copyleft/gpl.html irakurri ezazu online]. .",
+       "config-welcome-section-copyright": "=== Copyright eta terminoak ===\n\n$1\n\nPrograma hau software librea da; birbana eta / edo alda dezakezu GNU Lizentzia Publiko Orokorraren baldintzapean, Free Software Foundation-ek argitaratutakoaren arabera; Lizentziaren 2. bertsioa edo (nahiago baduzu) bertsio berriago bat.\n\nPrograma hau baliagarria izango delakoan elkarbantzen da, baina <strong> bermerik gabe </ strong>; <strong> merkaturatze </ strong> edo <strong>  helburu jakin baterako gaitasuna</ strong> berme inplizitua ere izan gabe.\nIkus GNU Lizentzia Publiko Orokorra xehetasun gehiagorako.\n\n[$2 izan beharko zenuke GNU Lizentzia Publiko Orokorraren kopia] programa honekin batera; bestela, idatzi Free Software Foundation-en, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, AEB, edo [https://www.gnu.org/copyleft/gpl.html irakurri ezazu online]. .",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki nagusia]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Erabiltzaileentzako Gida]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administratzaileentzako Gida]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ MEG]\n----\n* <doclink href=Readme>Irakur nazazu</doclink>\n* <doclink href=ReleaseNotes>Oharren argitalpena</doclink>\n* <doclink href=Copying>Kopiaketa</doclink>\n* <doclink href=UpgradeDoc>Eguneratzea</doclink>",
        "config-env-good": "Ingurumena egiaztatu egin da. \nMediaWiki instalatu ahal duzu.",
        "config-env-bad": "Ingurumena egiaztatu egin da.\nEzin duzu MediaWiki-a instalatu.",
index a36635d..43f4c93 100644 (file)
@@ -58,7 +58,7 @@
        "config-help-restart": "آیا می‌خواهید همهٔ اطلاعات ذخیره شده‌ای که وارد کرده‌اید را پاک کنید و دوباره روند نصب را شروع کنید؟",
        "config-restart": "بله، دوباره شروع کن",
        "config-welcome": "===بررسی‌های محیطی===\nبرای فهمیدن اینکه این محیط برای نصب مدیاویکی مناسب است، اکنون بررسی‌های اساسی انجام خواهد‌شد.\nاگر به دنبال پشتیبانی در چگونگی تکمیل نصب هستید،به یاد داشته باشید این اطلاعات را بگنجانید.",
-       "config-copyright": "=== حق رونوشت و شرایط ===\n\n$1\n\nاین برنامه، نرم‌افزاری آزاد است. می‌توانید تحت شرایط نگارش ۲ یا (بنا به نظر خود) هر نگارش جدیدتری از پروانهٔ جامع همگانی گنو که توسط بنیاد نرم‌افزار آزاد منتشر شده، بازنشرش کرده و/یا تغییرش دهید.\n\n\nاین برنامه با این امید توزیع شده که مفید باشد، ولی <strong>بدون هیچ ضمانتی</strong>، حتا ضمانت ضمنی <strong>معامله‌پذیری</strong> یا <strong>تناسب برای کاربردی خاص </strong>.\n\nبرای جزئیات بیشتر، پروانهٔ جامع همگانی گنو را ببینید.\n\n\nباید همراه این برنامه، <doclink href=Copying>نگارشی از پروانهٔ جامع همگانی گنو</doclink> را گرفته باشید. اگر چنین نیست، با بنیاد نرم‌افزار آزاد به نشانی 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA مکاتبه کرده یا [https://www.gnu.org/copyleft/gpl.html پروانه را برخط بخوانید].",
+       "config-welcome-section-copyright": "=== حق رونوشت و شرایط ===\n\n$1\n\nاین برنامه، نرم‌افزاری آزاد است. می‌توانید تحت شرایط نگارش ۲ یا (بنا به نظر خود) هر نگارش جدیدتری از پروانهٔ جامع همگانی گنو که توسط بنیاد نرم‌افزار آزاد منتشر شده، بازنشرش کرده و/یا تغییرش دهید.\n\n\nاین برنامه با این امید توزیع شده که مفید باشد، ولی <strong>بدون هیچ ضمانتی</strong>، حتا ضمانت ضمنی <strong>معامله‌پذیری</strong> یا <strong>تناسب برای کاربردی خاص </strong>.\n\nبرای جزئیات بیشتر، پروانهٔ جامع همگانی گنو را ببینید.\n\n\nباید همراه این برنامه، [$2 نگارشی از پروانهٔ جامع همگانی گنو] را گرفته باشید. اگر چنین نیست، با بنیاد نرم‌افزار آزاد به نشانی 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA مکاتبه کرده یا [https://www.gnu.org/copyleft/gpl.html پروانه را برخط بخوانید].",
        "config-sidebar": "* [//www.mediawiki.org صفحهٔ اصلی مدیاویکی]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents راهنمای کاربر]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents راهنمای مدیر]\n* [//www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ پرسش‌های رایج]\n----\n* <doclink href=Readme>مرا بخوان</doclink>\n* <doclink href=ReleaseNotes>یادداشت‌های انتشار</doclink>\n* <doclink href=Copying>نسخه برداری</doclink>\n* <doclink href=UpgradeDoc>ارتقا</doclink>",
        "config-env-good": "محیط بررسی شده‌است.\nشما می‌توانید مدیاویکی را نصب کنید.",
        "config-env-bad": "محیط بررسی شده‌است.\nشما نمی‌توانید مدیاویکی را نصب کنید.",
index fbc80f8..16ee054 100644 (file)
@@ -63,7 +63,7 @@
        "config-help-restart": "Haluatko poistaa kaikki annetut tiedot ja aloittaa asennuksen alusta?",
        "config-restart": "Kyllä",
        "config-welcome": "=== Ympäristön tarkistukset ===\nVarmistetaan MediaWikin asennettavuus tähän ympäristöön.\nMuista antaa nämä tiedot, jos tarvitset apua asennuksen aikana.",
-       "config-copyright": "=== Tekijänoikeudet ja käyttöehdot ===\n\n$1\n\nTämä ohjelma on vapaa ohjelmisto; voit levittää sitä ja/tai muokata sitä Free Software Foundationin GNU General Public Licensen ehdoilla, joko version 2 tai (halutessasi) minkä tahansa myöhemmän version mukaisesti.\n\nTätä ohjelmaa levitetään siinä toivossa, että se olisi hyödyllinen, mutta <strong>ilman mitään takuuta</strong>; ilman edes hiljaista takuuta <strong>kaupallisesti hyväksyttävästä laadusta</strong> tai <strong>soveltuvuudesta tiettyyn tarkoitukseen.</strong\nKatso GNU Generel Public Licensestä lisää yksityiskohtia.\n\nSinun olisi pitänyt saada <doclink href=Copying>kopio GNU General Public Licensestä</doclink> tämän ohjelman mukana; jos et, kirjoita siitä osoitteeseen Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA tai [https://www.gnu.org/copyleft/gpl.html lue se verkossa].",
+       "config-welcome-section-copyright": "=== Tekijänoikeudet ja käyttöehdot ===\n\n$1\n\nTämä ohjelma on vapaa ohjelmisto; voit levittää sitä ja/tai muokata sitä Free Software Foundationin GNU General Public Licensen ehdoilla, joko version 2 tai (halutessasi) minkä tahansa myöhemmän version mukaisesti.\n\nTätä ohjelmaa levitetään siinä toivossa, että se olisi hyödyllinen, mutta <strong>ilman mitään takuuta</strong>; ilman edes hiljaista takuuta <strong>kaupallisesti hyväksyttävästä laadusta</strong> tai <strong>soveltuvuudesta tiettyyn tarkoitukseen.</strong\nKatso GNU Generel Public Licensestä lisää yksityiskohtia.\n\nSinun olisi pitänyt saada [$2 kopio GNU General Public Licensestä] tämän ohjelman mukana; jos et, kirjoita siitä osoitteeseen Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA tai [https://www.gnu.org/copyleft/gpl.html lue se verkossa].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWikin kotisivu]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Käyttöopas]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Hallintaopas]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ UKK]\n----\n* <doclink href=Readme>Lue minut</doclink>\n* <doclink href=ReleaseNotes>Julkaisutiedot</doclink>\n* <doclink href=Copying>Kopiointi</doclink>\n* <doclink href=UpgradeDoc>Päivittäminen</doclink>",
        "config-env-good": "Asennusympäristö on tarkastettu.\nVoit asentaa MediaWikin.",
        "config-env-bad": "Asennusympäristö on tarkastettu.\nEt voi asentaa MediaWikiä.",
index 697e165..aa37a12 100644 (file)
@@ -71,7 +71,7 @@
        "config-help-restart": "Voulez-vous effacer toutes les données enregistrées que vous avez entrées et relancer le processus d'installation ?",
        "config-restart": "Oui, le relancer",
        "config-welcome": "=== Vérifications liées à l’environnement ===\nDes vérifications de base vont maintenant être effectuées pour voir si cet environnement est adapté à l’installation de MediaWiki.\nRappelez-vous d’inclure ces informations si vous recherchez de l’aide sur la manière de terminer l’installation.",
-       "config-copyright": "=== Droit d’auteur et conditions ===\n\n$1\n\nCe programme est un logiciel libre : vous pouvez le redistribuer ou le modifier selon les termes de la Licence Publique Générale GNU telle que publiée par la Free Software Foundation (version 2 de la Licence, ou, à votre choix, toute version ultérieure).\n\nCe programme est distribué dans l’espoir qu’il sera utile, mais '''sans aucune garantie''' : sans même les garanties implicites de '''commercialisabilité''' ou d’'''adéquation à un usage particulier'''.\nVoir la Licence Publique Générale GNU pour plus de détails.\n\nVous devriez avoir reçu <doclink href=Copying>une copie de la Licence Publique Générale GNU</doclink> avec ce programme ; dans le cas contraire, écrivez à la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ou [https://www.gnu.org/copyleft/gpl.html lisez-la en ligne].",
+       "config-welcome-section-copyright": "=== Droit d’auteur et conditions ===\n\n$1\n\nCe programme est un logiciel libre : vous pouvez le redistribuer ou le modifier selon les termes de la Licence Publique Générale GNU telle que publiée par la Free Software Foundation (version 2 de la Licence, ou, à votre choix, toute version ultérieure).\n\nCe programme est distribué dans l’espoir qu’il sera utile, mais '''sans aucune garantie''' : sans même les garanties implicites de '''commercialisabilité''' ou d’'''adéquation à un usage particulier'''.\nVoir la Licence Publique Générale GNU pour plus de détails.\n\nVous devriez avoir reçu [$2 une copie de la Licence Publique Générale GNU] avec ce programme ; dans le cas contraire, écrivez à la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ou [https://www.gnu.org/copyleft/gpl.html lisez-la en ligne].",
        "config-sidebar": "* [https://www.mediawiki.org Accueil MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guide de l’utilisateur]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guide de l’administrateur]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]",
        "config-sidebar-readme": "Me lire",
        "config-sidebar-relnotes": "Notes de version",
index 86c0c57..a6aa77c 100644 (file)
@@ -47,7 +47,7 @@
        "config-help-restart": "Quere eliminar todos os datos gardados e reiniciar o proceso de instalación?",
        "config-restart": "Si, reiniciala",
        "config-welcome": "=== Comprobación da contorna ===\nCómpre realizar agora unhas comprobacións básicas para ver se a contorna é axeitada para a instalación de MediaWiki.\nLembre incluír esta información se necesita axuda para completar a instalación.",
-       "config-copyright": "=== Dereitos de autor e termos de uso ===\n\n$1\n\nEste programa é software libre; pode redistribuílo e/ou modificalo segundo os termos da licenza pública xeral GNU publicada pola Free Software Foundation; versión 2 ou (na súa escolla) calquera outra posterior.\n\nEste programa distribúese coa esperanza de que poida ser útil, pero <strong>sen garantía ningunha</strong>; nin sequera a garantía implícita de <strong>comercialización</strong> ou <strong>adecuación a unha finalidade específica</strong>.\nOlle a licenza pública xeral GNU para obter máis detalles.\n\nDebería recibir <doclink href=Copying>unha copia da licenza pública xeral GNU</doclink> xunto ao programa; se non é así, escriba á Free Software Foundation, Inc., rúa Franklin, número 51, quinto andar, Boston, Massachusetts, 02110-1301, Estados Unidos de América ou [https://www.gnu.org/copyleft/gpl.html lea a licenza en liña].",
+       "config-welcome-section-copyright": "=== Dereitos de autor e termos de uso ===\n\n$1\n\nEste programa é software libre; pode redistribuílo e/ou modificalo segundo os termos da licenza pública xeral GNU publicada pola Free Software Foundation; versión 2 ou (na súa escolla) calquera outra posterior.\n\nEste programa distribúese coa esperanza de que poida ser útil, pero <strong>sen garantía ningunha</strong>; nin sequera a garantía implícita de <strong>comercialización</strong> ou <strong>adecuación a unha finalidade específica</strong>.\nOlle a licenza pública xeral GNU para obter máis detalles.\n\nDebería recibir [$2 unha copia da licenza pública xeral GNU] xunto ao programa; se non é así, escriba á Free Software Foundation, Inc., rúa Franklin, número 51, quinto andar, Boston, Massachusetts, 02110-1301, Estados Unidos de América ou [https://www.gnu.org/copyleft/gpl.html lea a licenza en liña].",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/gl Páxina principal de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guía de usuario]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guía de administrador]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Preguntas máis frecuentes]\n----\n* <doclink href=Readme>Léame</doclink>\n* <doclink href=ReleaseNotes>Notas de lanzamento</doclink>\n* <doclink href=Copying>Copia</doclink>\n* <doclink href=UpgradeDoc>Actualizacións</doclink>",
        "config-env-good": "Rematou a comprobación da contorna.\nPode instalar MediaWiki.",
        "config-env-bad": "Rematou a comprobación da contorna.\nNon pode instalar MediaWiki.",
index 337a179..a9d0c1c 100644 (file)
@@ -36,7 +36,7 @@
        "config-help-restart": "Witt alli Date, wu Du yygee hesch, lesche un d Inschtallation nomol aafange?",
        "config-restart": "Jo, nomol aafange",
        "config-welcome": "=== Priefig vu dr Inschtallationsumgäbig ===\nBasispriefige wäre durgfiert zum Feschtstelle, eb d Inschtallationsumgäbig fir d Inschtallation vu MediaWiki geignet isch.\nDu sottsch d Ergebnis vu däre Priefig aagee, wänn Du bi dr Inschtallation Hilf bruchsch.",
-       "config-copyright": "=== Copyright un Nutzigsbedingige ===\n\n$1\n\nDes Programm isch e freji Software, d. h. s cha, no dr Bedingige vu dr GNU General Public-Lizänz, wu vu dr Free Software Foundation vereffentligt woren isch, wyterverteilt un/oder modifiziert wäre. Doderbyy cha d Version 2, oder no eigenem Ermässe, jedi nejeri Version vu dr Lizänz brucht wäre.\n\nDes Programm wird in dr Hoffnig verteilt, ass es nitzli isch, aber '''ohni jedi Garanti''' un sogar ohni di impliziert Garanti vun ere '''Märtgängigkeit''' oder '''Eignig fir e bstimmte Zwäck'''. Doderzue git meh Hiiwys in dr GNU General Public-Lizänz.\n\nE <doclink href=Copying>Kopi vu dr GNU General Public-Lizänz</doclink> sott zämme mit däm Programm verteilt wore syy. Wänn des nit eso isch, cha ne Kopi bi dr Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftli aagforderet oder [https://www.gnu.org/copyleft/gpl.html online gläse] wäre.",
+       "config-welcome-section-copyright": "=== Copyright un Nutzigsbedingige ===\n\n$1\n\nDes Programm isch e freji Software, d. h. s cha, no dr Bedingige vu dr GNU General Public-Lizänz, wu vu dr Free Software Foundation vereffentligt woren isch, wyterverteilt un/oder modifiziert wäre. Doderbyy cha d Version 2, oder no eigenem Ermässe, jedi nejeri Version vu dr Lizänz brucht wäre.\n\nDes Programm wird in dr Hoffnig verteilt, ass es nitzli isch, aber '''ohni jedi Garanti''' un sogar ohni di impliziert Garanti vun ere '''Märtgängigkeit''' oder '''Eignig fir e bstimmte Zwäck'''. Doderzue git meh Hiiwys in dr GNU General Public-Lizänz.\n\nE [$2 Kopi vu dr GNU General Public-Lizänz] sott zämme mit däm Programm verteilt wore syy. Wänn des nit eso isch, cha ne Kopi bi dr Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftli aagforderet oder [https://www.gnu.org/copyleft/gpl.html online gläse] wäre.",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki Websyte vu MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Nutzeraaleitig zue MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Adminischtratoreaaleitig zue MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Vilmol gstellti Froge zue MediaWiki]",
        "config-env-good": "D Inschtallationsumgäbig isch prieft wore.\nDu chasch MediaWiki inschtalliere.",
        "config-env-bad": "D Inschtallationsumgäbigisch prieft wore.\nDu chasch MediaWiki nit inschtalliere.",
index 08e56e0..5b954c9 100644 (file)
@@ -51,7 +51,7 @@
        "config-help-restart": "האם ברצונך לנקות את כל הנתונים שהזנת ולהתחיל מחדש את תהליך ההתקנה?",
        "config-restart": "כן, להפעיל מחדש",
        "config-welcome": "=== בדיקות סביבה ===\nבדיקות בסיסיות תתבצענה עכשיו כדי לראות אם הסביבה הזאת מתאימה להתקנת מדיה־ויקי.\nנא לזכור לכלול את המידע הזה בעת בקשת תמיכה עם השלמת ההתקנה.",
-       "config-copyright": "=== זכויות יוצרים ותנאים ===\n\n$1\n\nתכנית זו היא תכנה חופשית; באפשרותך להפיצה מחדש ו/או לשנות אותה על פי תנאי הרישיון הציבורי הכללי של GNU כפי שפורסם על ידי קרן התכנה החופשית; בין אם גרסה 2 של הרישיון, ובין אם (לפי בחירתך) כל גרסה מאוחרת שלו.\n\nתכנית זו מופצת בתקווה שתהיה מועילה, אבל '''בלא אחריות כלשהי'''; ואפילו ללא האחריות המשתמעת בדבר '''מסחריותה''' או '''התאמתה למטרה '''מסוימת'''. לפרטים נוספים, ניתן לעיין ברישיון הציבורי הכללי של GNU.\n\nלתכנית זו אמור היה להיות מצורף <doclink href=Copying>עותק של הרישיון הציבורי הכללי של GNU</doclink>; אם לא קיבלת אותו, אפשר לכתוב ל־Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA או [https://www.gnu.org/copyleft/gpl.html לקרוא אותו דרך האינטרנט].",
+       "config-welcome-section-copyright": "=== זכויות יוצרים ותנאים ===\n\n$1\n\nתכנית זו היא תכנה חופשית; באפשרותך להפיצה מחדש ו/או לשנות אותה על פי תנאי הרישיון הציבורי הכללי של GNU כפי שפורסם על ידי קרן התכנה החופשית; בין אם גרסה 2 של הרישיון, ובין אם (לפי בחירתך) כל גרסה מאוחרת שלו.\n\nתכנית זו מופצת בתקווה שתהיה מועילה, אבל '''בלא אחריות כלשהי'''; ואפילו ללא האחריות המשתמעת בדבר '''מסחריותה''' או '''התאמתה למטרה '''מסוימת'''. לפרטים נוספים, ניתן לעיין ברישיון הציבורי הכללי של GNU.\n\nלתכנית זו אמור היה להיות מצורף [$2 עותק של הרישיון הציבורי הכללי של GNU]; אם לא קיבלת אותו, אפשר לכתוב ל־Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA או [https://www.gnu.org/copyleft/gpl.html לקרוא אותו דרך האינטרנט].",
        "config-sidebar": "* [https://www.mediawiki.org אתר הבית של מדיה־ויקי]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents המדריך למשתמש]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents המדריך למנהל]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ שו״ת]\n----\n* <doclink href=Readme>קרא אותי</doclink>\n* <doclink href=ReleaseNotes>הערות גרסה</doclink>\n* <doclink href=Copying>העתקה</doclink>\n* <doclink href=UpgradeDoc>שדרוג</doclink>",
        "config-env-good": "הסביבה שלכם נבדקה.\nאפשר להתקין מדיה־ויקי.",
        "config-env-bad": "הסביבה שלכם נבדקה.\nאי־אפשר להתקין מדיה־ויקי.",
index 75a76b1..33e5ec9 100644 (file)
@@ -42,7 +42,7 @@
        "config-help-restart": "Solle all bereits ingebne Daten gelöscht und der Installationsvoargang erneit oogefäng sin?",
        "config-restart": "Jo, erneit oonfänge",
        "config-welcome": "=== Prüfung von die Installationsumgebung ===\nDie Basisprüfunge were jetzt doorrichgefüahrt, um festzustelle, ob die Installationsumgebung für MediaWiki geeichnet ist.\nNotier die Informatione und geb se an, sofern du Hellf beim Installiere  benötichst.",
-       "config-copyright": "=== Lizenz und Nutzungsbedingunge ===\n\n$1\n\nDas Programm ist freie Software, d. h. es kann, gemäss den Bedingunge der von der Free Software Foundation veröffentlichte ''GNU General Public License'', weiterverteilt und/oder modifiziert sin. Dabei kann die Version 2, orrer noh eichnem Ermess, jede neuire Version von der Lizenz verwennet sin.\n\nDas Programm weard in der Hoffnung verteilt, dass das nützlich sein weard, dennoch '''ohne jechliche Garantie''' und sogoor ohne die implizierte Garantie von ener '''Marrektgängigkeit''' orrer '''Eichnung für en bestimmte Zweck'''. Hierzu sind weitre Hinweise in der ''GNU General Public License'' enthalt.\n\nEn <doclink href=Copying>Kopie von der GNU General Public License</doclink> sollt zusammer mit dem Programm verteilt woard sin. Sofern das net der Fall woar, kann en Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich verlangt sin orrer uff ehre Website [https://www.gnu.org/copyleft/gpl.html online gelesen] sin.",
+       "config-welcome-section-copyright": "=== Lizenz und Nutzungsbedingunge ===\n\n$1\n\nDas Programm ist freie Software, d. h. es kann, gemäss den Bedingunge der von der Free Software Foundation veröffentlichte ''GNU General Public License'', weiterverteilt und/oder modifiziert sin. Dabei kann die Version 2, orrer noh eichnem Ermess, jede neuire Version von der Lizenz verwennet sin.\n\nDas Programm weard in der Hoffnung verteilt, dass das nützlich sein weard, dennoch '''ohne jechliche Garantie''' und sogoor ohne die implizierte Garantie von ener '''Marrektgängigkeit''' orrer '''Eichnung für en bestimmte Zweck'''. Hierzu sind weitre Hinweise in der ''GNU General Public License'' enthalt.\n\nEn [$2 Kopie von der GNU General Public License] sollt zusammer mit dem Programm verteilt woard sin. Sofern das net der Fall woar, kann en Kopie bei der Free Software Foundation Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, schriftlich verlangt sin orrer uff ehre Website [https://www.gnu.org/copyleft/gpl.html online gelesen] sin.",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/de Website von MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/de Benutzeroonleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/de Administratorenoonleitung]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/de Häifig gestellte Frache]\n----\n* <doclink href=Readme>Lies mich</doclink>\n* <doclink href=ReleaseNotes>Versionsinformatione</doclink>\n* <doclink href=Copying>Lizenzbestimmunge</doclink>\n* <doclink href=UpgradeDoc>Aktualisierung</doclink>",
        "config-env-good": "Die Installationsumgebung woard geprüft.\nMediaWiki kann installiert sin.",
        "config-env-bad": "Die Installationsumgebung woard geprüft.\nMediaWiki kann net installiert sin.",
index a2a4e3b..f6bca7b 100644 (file)
@@ -53,7 +53,7 @@
        "config-help-restart": "Szeretnéd törölni az eddig megadott összes adatot és újraindítani a telepítési folyamatot?",
        "config-restart": "Igen, újraindítás",
        "config-welcome": "=== A környezet ellenőrzése ===\nNéhány alapvető ellenőrzés hajtódik végre, hogy kiderüljön, hogy ez a környezet alkalmas-e a MediaWiki telepítésére.\nHa segítséget kérsz a telepítéssel kapcsolatban, add meg ezen ellenőrzések eredményét.",
-       "config-copyright": "=== Licenc és feltételek ===\n\n$1\n\nEz a program szabad szoftver; terjeszthető, illetve módosítható a Free Software Foundation által kiadott GNU General Public License dokumentumában leírtak; akár a licenc 2-es, akár (tetszőleges) későbbi változata szerint.\n\nEz a program abban a reményben kerül közreadásra, hogy hasznos lesz, de minden egyéb <strong>garancia nélkül</strong>, az <strong>eladhatóságra</strong> vagy <strong>valamely célra való alkalmazhatóságra</strong> való származtatott garanciát is beleértve. További részleteket a GNU General Public License tartalmaz.\n\nA felhasználónak a programmal együtt meg kell kapnia a <doclink href=Copying>GNU General Public License egy példányát</doclink>; ha mégsem kapta meg, akkor írjon a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. címre, vagy [https://www.gnu.org/copyleft/gpl.html tekintse meg online].",
+       "config-welcome-section-copyright": "=== Licenc és feltételek ===\n\n$1\n\nEz a program szabad szoftver; terjeszthető, illetve módosítható a Free Software Foundation által kiadott GNU General Public License dokumentumában leírtak; akár a licenc 2-es, akár (tetszőleges) későbbi változata szerint.\n\nEz a program abban a reményben kerül közreadásra, hogy hasznos lesz, de minden egyéb <strong>garancia nélkül</strong>, az <strong>eladhatóságra</strong> vagy <strong>valamely célra való alkalmazhatóságra</strong> való származtatott garanciát is beleértve. További részleteket a GNU General Public License tartalmaz.\n\nA felhasználónak a programmal együtt meg kell kapnia a [$2 GNU General Public License egy példányát]; ha mégsem kapta meg, akkor írjon a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. címre, vagy [https://www.gnu.org/copyleft/gpl.html tekintse meg online].",
        "config-sidebar": "* [https://www.mediawiki.org A MediaWiki honlapja]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Felhasználói kézikönyv]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Útmutató adminisztrátoroknak]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ GyIK]\n----\n* <doclink href=Readme>Ismertető</doclink>\n* <doclink href=ReleaseNotes>Kiadási megjegyzések</doclink>\n* <doclink href=Copying>Másolás</doclink>\n* <doclink href=UpgradeDoc>Frissítés</doclink>",
        "config-env-good": "A környezet ellenőrzése befejeződött.\nA MediaWiki telepíthető.",
        "config-env-bad": "A környezet ellenőrzése befejeződött.\nA MediaWiki nem telepíthető.",
index 2b85baa..626d15d 100644 (file)
@@ -45,7 +45,7 @@
        "config-help-restart": "Vole tu rader tote le datos salveguardate que tu ha entrate e reinitiar le processo de installation?",
        "config-restart": "Si, reinitia lo",
        "config-welcome": "=== Verificationes del ambiente ===\nVerificationes de base essera ora exequite pro determinar si iste ambiente es apte pro le installation de MediaWiki.\nNon oblida de includer iste information si tu cerca adjuta pro completar le installation.",
-       "config-copyright": "=== Copyright and Terms ===\n\n$1\n\nIste programma es software libere; vos pote redistribuer lo e/o modificar lo sub le conditiones del Licentia Public General de GNU publicate per le Free Software Foundation; version 2 del Licentia, o (a vostre option) qualcunque version posterior.\n\nIste programma es distribuite in le sperantia que illo sia utile, ma '''sin garantia''', sin mesmo le implicite garantia de '''commercialisation''' o '''aptitude pro un proposito particular'''.\nVide le Licentia Public General de GNU pro plus detalios.\n\nVos deberea haber recipite <doclink href=Copying>un exemplar del Licentia Public General de GNU</doclink> con iste programma; si non, scribe al Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lege lo in linea].",
+       "config-welcome-section-copyright": "=== Copyright and Terms ===\n\n$1\n\nIste programma es software libere; vos pote redistribuer lo e/o modificar lo sub le conditiones del Licentia Public General de GNU publicate per le Free Software Foundation; version 2 del Licentia, o (a vostre option) qualcunque version posterior.\n\nIste programma es distribuite in le sperantia que illo sia utile, ma '''sin garantia''', sin mesmo le implicite garantia de '''commercialisation''' o '''aptitude pro un proposito particular'''.\nVide le Licentia Public General de GNU pro plus detalios.\n\nVos deberea haber recipite [$2 un exemplar del Licentia Public General de GNU] con iste programma; si non, scribe al Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lege lo in linea].",
        "config-sidebar": "* [https://www.mediawiki.org Pagina principal de MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guida pro usatores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guida pro administratores]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>Lege me</doclink>\n* <doclink href=ReleaseNotes>Notas de iste version</doclink>\n* <doclink href=Copying>Conditiones de copia</doclink>\n* <doclink href=UpgradeDoc>Actualisation</doclink>",
        "config-env-good": "Le ambiente ha essite verificate.\nTu pote installar MediaWiki.",
        "config-env-bad": "Le ambiente ha essite verificate.\nTu non pote installar MediaWiki.",
index 0071634..792b7bc 100644 (file)
@@ -55,7 +55,7 @@
        "config-help-restart": "Apakah Anda ingin menghapus semua data tersimpan yang telah Anda masukkan dan mengulang proses instalasi?",
        "config-restart": "Ya, nyalakan ulang",
        "config-welcome": "=== Pengecekan lingkungan ===\nPengecekan dasar kini akan dilakukan untuk melihat apakah lingkungan ini memadai untuk instalasi MediaWiki.\nIngatlah untuk menyertakan informasi ini jika Anda mencari bantuan tentang cara menyelesaikan instalasi.",
-       "config-copyright": "=== Hak cipta dan persyaratan ===\n\n$1\n\nProgram ini adalah perangkat lunak bebas; Anda dapat mendistribusikan dan/atau memodifikasi di bawah persyaratan GNU General Public License seperti yang diterbitkan oleh Free Software Foundation; baik versi 2 lisensi, atau (sesuai pilihan Anda) versi yang lebih baru.\n\nProgram ini didistribusikan dengan harapan bahwa itu akan berguna, tetapi <strong>tanpa jaminan apa pun</strong>; bahkan tanpa jaminan tersirat untuk <strong>dapat diperjualbelikan</strong> atau <strong>sesuai untuk tujuan tertentu</strong>.\nLihat GNU General Public License untuk lebih jelasnya.\n\nAnda seharusnya telah menerima <doclink href=\"Copying\">salinan dari GNU General Public License</doclink> bersama dengan program ini; jika tidak, kirimkan surat untuk Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, atau [https://www.gnu.org/copyleft/gpl.html baca versi daring].",
+       "config-welcome-section-copyright": "=== Hak cipta dan persyaratan ===\n\n$1\n\nProgram ini adalah perangkat lunak bebas; Anda dapat mendistribusikan dan/atau memodifikasi di bawah persyaratan GNU General Public License seperti yang diterbitkan oleh Free Software Foundation; baik versi 2 lisensi, atau (sesuai pilihan Anda) versi yang lebih baru.\n\nProgram ini didistribusikan dengan harapan bahwa itu akan berguna, tetapi <strong>tanpa jaminan apa pun</strong>; bahkan tanpa jaminan tersirat untuk <strong>dapat diperjualbelikan</strong> atau <strong>sesuai untuk tujuan tertentu</strong>.\nLihat GNU General Public License untuk lebih jelasnya.\n\nAnda seharusnya telah menerima [$2 salinan dari GNU General Public License] bersama dengan program ini; jika tidak, kirimkan surat untuk Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, atau [https://www.gnu.org/copyleft/gpl.html baca versi daring].",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/id Situs MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/id Pedoman Pengguna]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/id Pedoman Administrator]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/id FAQ]\n----\n* <doclink href=Readme>Read me</doclink>\n* <doclink href=ReleaseNotes>Release notes</doclink>\n* <doclink href=Copying>Copying</doclink>\n* <doclink href=UpgradeDoc>Upgrading</doclink>",
        "config-env-good": "Kondisi telah diperiksa.\nAnda dapat menginstal MediaWiki.",
        "config-env-bad": "Kondisi telah diperiksa.\nAnda tidak dapat menginstal MediaWiki.",
index f1e441b..e7688dc 100644 (file)
@@ -35,7 +35,7 @@
        "config-page-existingwiki": "Existanta wiki",
        "config-help-restart": "Ka vu deziras efacar omna dati qui vu sparis, e rikomencar la procedi pri instalo?",
        "config-restart": "Yes, rikomencez ol",
-       "config-copyright": "=== Autoroyuro e termini ===\n\n$1\n\nCa informatikoprogramo esas libera; vu povas ridistributar ol e/o modifikar ol segun la termini de la Generala Licenco Publika GNU, quale stipulata da Free Software Foundation; segun lua versiono 2 o sequanta.\n\nNi expektas ke ca programo esas utila, kande ni distributas ol. <strong>Tamen, ni ne povas grantar ke ol fakte esos utila</strong>. Ni anke ne povas afirmar ke ol esos <strong>vendebla</strong> o <strong>ke ol esos utila por specifika intenco</strong>.\nLektez la Generala Licenco Publika GNU por plusa detali.\n\nKune ica programo vu certe recevis <doclink href=Copying> kopiuro di la Generala Licenco Publika GNU</doclink>. Se vu ne recevis ol, voluntez skribar a la fonduro Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lektez ol che l'interreto].",
+       "config-welcome-section-copyright": "=== Autoroyuro e termini ===\n\n$1\n\nCa informatikoprogramo esas libera; vu povas ridistributar ol e/o modifikar ol segun la termini de la Generala Licenco Publika GNU, quale stipulata da Free Software Foundation; segun lua versiono 2 o sequanta.\n\nNi expektas ke ca programo esas utila, kande ni distributas ol. <strong>Tamen, ni ne povas grantar ke ol fakte esos utila</strong>. Ni anke ne povas afirmar ke ol esos <strong>vendebla</strong> o <strong>ke ol esos utila por specifika intenco</strong>.\nLektez la Generala Licenco Publika GNU por plusa detali.\n\nKune ica programo vu certe recevis [$2  kopiuro di la Generala Licenco Publika GNU]. Se vu ne recevis ol, voluntez skribar a la fonduro Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/copyleft/gpl.html lektez ol che l'interreto].",
        "config-env-good": "Omno verifikesis.\nVu povas intalar MediaWiki.",
        "config-env-bad": "Omno verifikesis.\nVu NE POVAS intalar MediaWiki.",
        "config-env-php": "PHP $1 instalesis.",
index 3af2fc0..0ea1522 100644 (file)
@@ -31,7 +31,7 @@
        "config-page-upgradedoc": "Uppfærsla",
        "config-page-existingwiki": "Fyrirliggjandi wiki",
        "config-restart": "Já, endurræsa",
-       "config-copyright": "=== Höfundarréttur og skilmálar ===\n\n$1\n\nÞetta er frjáls hugbúnaður; þú mátt dreifa honum og/eða breyta samkvæmt skilmálum í almenna GNU GPL notkunarleyfinu eins og það er gefið út af Frjálsu hugbúnaðarstofnuninni; annaðhvort útgáfu 2 af GPL-leyfinu, eða (ef þér sýnist svo) einhverri nýrri útgáfu leyfisins.\n\nHugbúnaði þessum er dreift í þeirri von að hann geti verið gagnlegur, en <strong>ÁN ALLRAR ÁBYRGÐAR</strong>; einnig án þeirrar ábyrgðar sem gefin er í skyn með <strong>SELJANLEIKA</strong> eða <strong>EIGINLEIKUM TIL TILTEKINNA NOTA</strong>. Sjá almenna GNU GPL notkunarleyfið fyrir nánari upplýsingar.\n\nÞað ætti að hafa fylgt afrit af almenna <doclink href=Copying>GNU GPL notkunarleyfinu</doclink> með forritinu; ef ekki skrifið þá Fjálsu hugbúnarstofnuninni: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, eða [https://www.gnu.org/copyleft/gpl.html lestu það á netinu].",
+       "config-welcome-section-copyright": "=== Höfundarréttur og skilmálar ===\n\n$1\n\nÞetta er frjáls hugbúnaður; þú mátt dreifa honum og/eða breyta samkvæmt skilmálum í almenna GNU GPL notkunarleyfinu eins og það er gefið út af Frjálsu hugbúnaðarstofnuninni; annaðhvort útgáfu 2 af GPL-leyfinu, eða (ef þér sýnist svo) einhverri nýrri útgáfu leyfisins.\n\nHugbúnaði þessum er dreift í þeirri von að hann geti verið gagnlegur, en <strong>ÁN ALLRAR ÁBYRGÐAR</strong>; einnig án þeirrar ábyrgðar sem gefin er í skyn með <strong>SELJANLEIKA</strong> eða <strong>EIGINLEIKUM TIL TILTEKINNA NOTA</strong>. Sjá almenna GNU GPL notkunarleyfið fyrir nánari upplýsingar.\n\nÞað ætti að hafa fylgt afrit af almenna [$2 GNU GPL notkunarleyfinu] með forritinu; ef ekki skrifið þá Fjálsu hugbúnarstofnuninni: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, eða [https://www.gnu.org/copyleft/gpl.html lestu það á netinu].",
        "config-env-php": "PHP $1 er uppsett.",
        "config-env-hhvm": "HHVM $1 er uppsett.",
        "config-apc": "[https://www.php.net/apc APC] er uppsett",
index ce38338..3d08343 100644 (file)
@@ -63,7 +63,7 @@
        "config-help-restart": "Vuoi cancellare tutti i dati salvati che hai inserito e riavviare il processo di installazione?",
        "config-restart": "Sì, riavvia",
        "config-welcome": "=== Controllo dell'ambiente ===\nSaranno eseguiti controlli di base per vedere se questo ambiente è adatto per l'installazione di MediaWiki.\nRicordati di includere queste informazioni se chiedi assistenza su come completare l'installazione.",
-       "config-copyright": "=== Copyright e termini ===\n\n$1\n\nQuesto programma è un software libero; puoi redistribuirlo e/o modificarlo secondo i termini della GNU General Public License, come pubblicata dalla Free Software Foundation; o la versione 2 della Licenza o (a propria scelta) qualunque versione successiva.\n\nQuesto programma è distribuito nella speranza che sia utile, ma SENZA ALCUNA GARANZIA; senza neppure la garanzia implicita di NEGOZIABILITÀ o di APPLICABILITÀ PER UN PARTICOLARE SCOPO.\nSi veda la GNU General Public License per maggiori dettagli.\n\nQuesto programma deve essere distribuito assieme ad <doclink href=Copying>una copia della GNU General Public License</doclink>; in caso contrario, se ne può ottenere una scrivendo alla Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppure [https://www.gnu.org/copyleft/gpl.html leggerla in rete].",
+       "config-welcome-section-copyright": "=== Copyright e termini ===\n\n$1\n\nQuesto programma è un software libero; puoi redistribuirlo e/o modificarlo secondo i termini della GNU General Public License, come pubblicata dalla Free Software Foundation; o la versione 2 della Licenza o (a propria scelta) qualunque versione successiva.\n\nQuesto programma è distribuito nella speranza che sia utile, ma SENZA ALCUNA GARANZIA; senza neppure la garanzia implicita di NEGOZIABILITÀ o di APPLICABILITÀ PER UN PARTICOLARE SCOPO.\nSi veda la GNU General Public License per maggiori dettagli.\n\nQuesto programma deve essere distribuito assieme ad [$2 una copia della GNU General Public License]; in caso contrario, se ne può ottenere una scrivendo alla Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppure [https://www.gnu.org/copyleft/gpl.html leggerla in rete].",
        "config-sidebar": "* [https://www.mediawiki.org Pagina principale MediaWiki]\n* [https://www.mediawiki.org/Special:MyLanguage/Help:Contents Guida ai contenuti per utenti]\n* [https://www.mediawiki.org/Special:MyLanguage/Manual:Contents Guida ai contenuti per admin]\n* [https://www.mediawiki.org/Special:MyLanguage/Manual:FAQ FAQ]",
        "config-sidebar-readme": "Leggimi",
        "config-sidebar-relnotes": "Note di versione",
index 7f7c801..a7bad28 100644 (file)
@@ -65,7 +65,7 @@
        "config-help-restart": "入力した保存データをすべて消去して、インストール作業を再起動しますか?",
        "config-restart": "はい、再起動します",
        "config-welcome": "=== 環境の確認 ===\n基本的な確認では、現在の環境が MediaWiki のインストールに適しているかを確認します。\nインストール方法について助けが必要になった場合は、必ずこの確認結果を添えてください。",
-       "config-copyright": "=== 著作権および規約 ===\n$1\n\nこの作品はフリーソフトウェアです。あなたは、フリーソフトウェア財団の発行する GNU 一般公衆利用許諾書 (GNU General Public License) (バージョン 2、またはそれ以降のライセンス) の規約に基づき、このライブラリを再配布および改変できます。\n\nこの作品は、有用であることを期待して配布されていますが、<strong>商用または特定の目的に適するかどうか</strong>も含めて、暗黙的にも、<strong>一切保証されません</strong>。\n詳しくは、 GNU 一般公衆利用許諾書をご覧ください。\n\nあなたはこのプログラムと共に、<doclink href=Copying>GNU 一般公衆利用許諾契約書の複製</doclink>を受け取ったはずです。受け取っていない場合は、フリーソフトウェア財団 (宛先は the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA) まで請求するか、または[https://www.gnu.org/copyleft/gpl.html オンラインでお読みください]。",
+       "config-welcome-section-copyright": "=== 著作権および規約 ===\n$1\n\nこの作品はフリーソフトウェアです。あなたは、フリーソフトウェア財団の発行する GNU 一般公衆利用許諾書 (GNU General Public License) (バージョン 2、またはそれ以降のライセンス) の規約に基づき、このライブラリを再配布および改変できます。\n\nこの作品は、有用であることを期待して配布されていますが、<strong>商用または特定の目的に適するかどうか</strong>も含めて、暗黙的にも、<strong>一切保証されません</strong>。\n詳しくは、 GNU 一般公衆利用許諾書をご覧ください。\n\nあなたはこのプログラムと共に、[$2 GNU 一般公衆利用許諾契約書の複製]を受け取ったはずです。受け取っていない場合は、フリーソフトウェア財団 (宛先は the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA) まで請求するか、または[https://www.gnu.org/copyleft/gpl.html オンラインでお読みください]。",
        "config-sidebar": "* [https://www.mediawiki.org MediaWikiのホーム]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 利用者向け案内]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents 管理者向け案内]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>お読みください</doclink>\n* <doclink href=ReleaseNotes>リリースノート</doclink>\n* <doclink href=Copying>コピー</doclink>\n* <doclink href=UpgradeDoc>アップグレード</doclink>",
        "config-env-good": "環境を確認しました。\nMediaWiki をインストールできます。",
        "config-env-bad": "環境を確認しました。\nMediaWiki のインストールはできません。",
index f952045..9dc815e 100644 (file)
@@ -53,7 +53,7 @@
        "config-help-restart": "입력한 모든 저장된 데이터를 지우고 설치 과정을 다시 시작하겠습니까?",
        "config-restart": "예, 다시 시작합니다",
        "config-welcome": "=== 사용 환경 검사 ===\n기본 검사는 지금 이 환경이 미디어위키 설치에 적합한지 수행합니다.\n설치를 완료하는 방법에 대한 지원을 찾는다면 이 정보를 포함해야 하는 것을 기억하세요.",
-       "config-copyright": "=== 저작권 및 약관 ===\n\n$1\n\n이 프로그램은 자유 소프트웨어입니다. 당신은 자유 소프트웨어 재단이 발표한 GNU 일반 공중 사용 허가서 버전 2나 그 이후 버전에 따라 이 프로그램을 재배포하거나 수정할 수 있습니다.\n\n이 프로그램이 유용하게 사용될 수 있기를 바라지만 <strong>상용으로 사용</strong>되거나 <strong>특정 목적에 맞을 것</strong>이라는 것을 <strong>보증하지 않습니다</strong>.\n자세한 내용은 GNU 일반 공중 사용 허가서를 참조하십시오.\n\n당신은 이 프로그램을 통해 <doclink href=Copying>GNU 일반 공중 사용 허가서 전문</doclink>을 받았습니다. 그렇지 않다면, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA로 편지를 보내주시거나 [https://www.gnu.org/copyleft/gpl.html 온라인으로 읽어보시기] 바랍니다.",
+       "config-welcome-section-copyright": "=== 저작권 및 약관 ===\n\n$1\n\n이 프로그램은 자유 소프트웨어입니다. 당신은 자유 소프트웨어 재단이 발표한 GNU 일반 공중 사용 허가서 버전 2나 그 이후 버전에 따라 이 프로그램을 재배포하거나 수정할 수 있습니다.\n\n이 프로그램이 유용하게 사용될 수 있기를 바라지만 <strong>상용으로 사용</strong>되거나 <strong>특정 목적에 맞을 것</strong>이라는 것을 <strong>보증하지 않습니다</strong>.\n자세한 내용은 GNU 일반 공중 사용 허가서를 참조하십시오.\n\n당신은 이 프로그램을 통해 [$2 GNU 일반 공중 사용 허가서 전문]을 받았습니다. 그렇지 않다면, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA로 편지를 보내주시거나 [https://www.gnu.org/copyleft/gpl.html 온라인으로 읽어보시기] 바랍니다.",
        "config-sidebar": "* [https://www.mediawiki.org 미디어위키 홈]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents 사용자 가이드]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents 관리자 가이드]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>읽어보기</doclink>\n* <doclink href=ReleaseNotes>릴리스 노트</doclink>\n* <doclink href=Copying>전문</doclink>\n* <doclink href=UpgradeDoc>업그레이드하기</doclink>",
        "config-env-good": "환경이 확인되었습니다.\n미디어위키를 설치할 수 있습니다.",
        "config-env-bad": "환경이 확인되었습니다.\n미디어위키를 설치할 수 없습니다.",
index 50cf468..7be04d1 100644 (file)
@@ -44,7 +44,7 @@
        "config-help-restart": "Wells De all Ding enjejovve Saache fottjeschmeße han, un dä janze Vörjang vun fürre aan neu aanfange?",
        "config-restart": "Joh, neu aanfange!",
        "config-welcome": "=== Ömjevong Pröhfe ===\nMer maache en Aanzahl jrundlääje Pröhvunge, öm erus ze fenge, ov di Ömjävvong heh paß för Mediawiki opzesäze.\nWann de Hölp bem Opsäze hölls, saach wigger, wat heh erus kohm, alsu wat heh schteiht.",
-       "config-copyright": "=== Urhävverrääsch un Lizänzbedengunge ===\n\n$1\n\nDat  Projramm heh es frei, mer kann et wiggerjävve un verdeijle un och verändere onger dä Bedengunge vun de  GNU <i lang=\"en\">General Public License</i> (Alljemeine öffentlesche Lizänz) wi se vun de <i lang=\"en\">Free Software Foundation</i> (de Schteftung för frei Projramme) veröffentlesch woode es. Dobei kanns De Der de Version 2 vun dä Lizanz ußsöhke, udder jeede Version donoh, wi et Der jefällt.\n\nDat Projramm weed wigger jejovve met dä Hoffnung, dat et jät nöz, ävver <strong>der ohne Jarrantie</strong>, sujaa der ohne de onußjeshproche Jarantie, <strong>verkoufbaa</strong> ze sin, udder <strong>för öhnds_ene beshtemmpte Zweck ze bruche</strong> ze sin.\nLiß de GNU <i lang=\"en\">General Public License</i> sellver, öm mieh ze erfahre.\n\nDo sullts en <doclink href=Copying>Kopie vun dä alljemene öffentlesche Lizänz vun dä GNU</doclink> (<i lang=\"en\">GNU General Public License</i>) zosamme met heh däm Projramm krääje han. Wann dat nit esu es, schrief aan de <i lang=\"en\">Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA</i>, udder [https://www.gnu.org/copyleft/gpl.html liß se online övver et Internet].",
+       "config-welcome-section-copyright": "=== Urhävverrääsch un Lizänzbedengunge ===\n\n$1\n\nDat  Projramm heh es frei, mer kann et wiggerjävve un verdeijle un och verändere onger dä Bedengunge vun de  GNU <i lang=\"en\">General Public License</i> (Alljemeine öffentlesche Lizänz) wi se vun de <i lang=\"en\">Free Software Foundation</i> (de Schteftung för frei Projramme) veröffentlesch woode es. Dobei kanns De Der de Version 2 vun dä Lizanz ußsöhke, udder jeede Version donoh, wi et Der jefällt.\n\nDat Projramm weed wigger jejovve met dä Hoffnung, dat et jät nöz, ävver <strong>der ohne Jarrantie</strong>, sujaa der ohne de onußjeshproche Jarantie, <strong>verkoufbaa</strong> ze sin, udder <strong>för öhnds_ene beshtemmpte Zweck ze bruche</strong> ze sin.\nLiß de GNU <i lang=\"en\">General Public License</i> sellver, öm mieh ze erfahre.\n\nDo sullts en [$2 Kopie vun dä alljemene öffentlesche Lizänz vun dä GNU] (<i lang=\"en\">GNU General Public License</i>) zosamme met heh däm Projramm krääje han. Wann dat nit esu es, schrief aan de <i lang=\"en\">Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA</i>, udder [https://www.gnu.org/copyleft/gpl.html liß se online övver et Internet].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki sing Hompäjdsch]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Handbohch för Aanwänder]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Handbohch för Administratohre un Wiki_Köbesse]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Öff jeschtallte Frohre met Antwoote]\n----\n* <doclink href=Readme>Liß Mesch! (<i lang=\"en\">Read me</i>)</doclink>\n* <doclink href=ReleaseNotes><i lang=\"en\">Release notes</i> Övver heh di Projrammversion</doclink>\n* <doclink href=Copying><i lang=\"en\">Copying</i> — Lizänzbeshtemmunge</doclink>\n* <doclink href=UpgradeDoc><i lang=\"en\">Upgrading</i> — Ob en neu Projrammversion jonn</doclink>",
        "config-env-good": "De Ömjävvöng es jepröhf.\nDo kanns MehdijaWikki opsäze.",
        "config-env-bad": "De Ömjävong es jeprööf.\nDo kanns MehdijaWikki nit opsäze.",
index b686444..a018ac7 100644 (file)
@@ -42,7 +42,7 @@
        "config-help-restart": "Ti voeu scassâ tutti i dæti sarvæ che ti t'hæ inseio e riavviâ o processo de installaçion?",
        "config-restart": "Scì, riavvia",
        "config-welcome": "=== Controllo de l'ambiente ===\nSaiâ eseguio di controlli de base pe vedde se questo ambiente o l'è adatto pe l'installaçion de MediaWiki.\nRegordite de includde queste informaçioin se ti domandi ascistença insce comme completâ l'installaçion.",
-       "config-copyright": "=== Copyright e termini ===\n\n$1\n\nQuesto programma o l'è un software libero; ti poeu redistriboîlo e/ò modificâlo segondo i termi da GNU General Public License, comme pubbricâ da-a Free Software Foundation; ò a verscion 2 da Liçença ò (a proppia scelta) qualunque verscion succesciva.\n\nQuesto programma o l'è distribuio inta sperança ch'o segge utile, ma SENSA ARCUNA GARANTIA; sença manco a garantia impliçita de NEGOSSIABILITÆ o de APPRICABILITÆ PE UN PARTICOL SCOPO.\nS'amie a GNU General Public License pe maggioî dettaggi.\n\nQuesto programma o dev'ese distribuio insemme a <doclink href=Copying>una copia da GNU General Public License</doclink>; in caxo contraio, se ne poeu otegnî un-a scrivendo a-a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppù [https://www.gnu.org/copyleft/gpl.html lezila inta ræ'].",
+       "config-welcome-section-copyright": "=== Copyright e termini ===\n\n$1\n\nQuesto programma o l'è un software libero; ti poeu redistriboîlo e/ò modificâlo segondo i termi da GNU General Public License, comme pubbricâ da-a Free Software Foundation; ò a verscion 2 da Liçença ò (a proppia scelta) qualunque verscion succesciva.\n\nQuesto programma o l'è distribuio inta sperança ch'o segge utile, ma SENSA ARCUNA GARANTIA; sença manco a garantia impliçita de NEGOSSIABILITÆ o de APPRICABILITÆ PE UN PARTICOL SCOPO.\nS'amie a GNU General Public License pe maggioî dettaggi.\n\nQuesto programma o dev'ese distribuio insemme a [$2 una copia da GNU General Public License]; in caxo contraio, se ne poeu otegnî un-a scrivendo a-a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA oppù [https://www.gnu.org/copyleft/gpl.html lezila inta ræ'].",
        "config-sidebar": "* [https://www.mediawiki.org Paggina prinçipâ MediaWiki]\n* [https://www.mediawiki.org/wiki/Agiutto:Guidda a-i contegnui pe utenti]\n* [https://www.mediawiki.org/wiki/Manoâ:Guidda ai contegnui per admin]\n* [https://www.mediawiki.org/wiki/Manoâ:FAQ FAQ]\n----\n* <doclink href=Readme>Lezime</doclink>\n* <doclink href=ReleaseNotes>Notte de verscion</doclink>\n* <doclink href=Copying>Copie</doclink>\n* <doclink href=UpgradeDoc>Aggiornamenti</doclink>",
        "config-env-good": "L'ambiente o l'è stæto controllou.\nL'è poscibile installâ MediaWiki.",
        "config-env-bad": "L'ambiente o l'è stæto controllou.\nNon l'è poscibbile installâ MediaWiki.",
index 9547e42..a637c09 100644 (file)
@@ -44,7 +44,7 @@
        "config-help-restart": "آیا می‌خواهید همهٔ اطلاعات ذخیره شده‌ای که وارد کرده‌اید را پاک کنید و دوباره روند نصب را شروع کنید؟",
        "config-restart": "أرێ، دوواره راه‌اندازی کة",
        "config-welcome": "===بررسی‌های محیطی===\nبرای فهمیدن اینکه این محیط برای نصب مدیاویکی مناسب است، اکنون بررسی‌های اساسی انجام خواهد‌شد.\nاگر به دنبال پشتیبانی در چگونگی تکمیل نصب هستید،به یاد داشته باشید این اطلاعات را بگنجانید.",
-       "config-copyright": "===حق چاپ و شرایط===\n$1\nاین برنامه، یک نرم‌افزاری آزاد است. شما می‌توانید آن را بازتوزیع کرده و/یا با شرایط نگارش ۲ یا (با نظر خودتان) هر نگارش جدیدتری از پروانه جامع همگانی گنو که توسط بنیاد نرم‌افزار آزاد منتشر شده، تغییر دهید.\n\nاین برنامه با امید این که مفید واقع‌ شود توزیع شده‌است،اما '''بدون هیچ ضمانتی'''; حتی بدون اشارهٔ ضمانتی از '''قابلیت عرضه''' یا ''' صلاحیت برای یک هدف خاص'''.\nبرای جزئیات بیش‌تر پروانه جامع همگانی گنو را مشاهده کنید.\n\nشما باید <doclink href=Copying> یک نگارش ازمجوز عمومی کلی </doclink> همراه این برنامه دریافت کرده باشید. در غیر این صورت با بنیاد نرم‌افزار آزاد، ایالات متحده امریکا، بوستون، خیابان فرانکلین، پلاک ۵۱، طبقه پنجم، صندوق پستی MA۰۲۱۱۰-۱۳۰ مکاتبه کنید، یا [https://www.gnu.org/copyleft/gpl.html در این‌جا به صورت برخط بخوانید].",
+       "config-welcome-section-copyright": "===حق چاپ و شرایط===\n$1\nاین برنامه، یک نرم‌افزاری آزاد است. شما می‌توانید آن را بازتوزیع کرده و/یا با شرایط نگارش ۲ یا (با نظر خودتان) هر نگارش جدیدتری از پروانه جامع همگانی گنو که توسط بنیاد نرم‌افزار آزاد منتشر شده، تغییر دهید.\n\nاین برنامه با امید این که مفید واقع‌ شود توزیع شده‌است،اما '''بدون هیچ ضمانتی'''; حتی بدون اشارهٔ ضمانتی از '''قابلیت عرضه''' یا ''' صلاحیت برای یک هدف خاص'''.\nبرای جزئیات بیش‌تر پروانه جامع همگانی گنو را مشاهده کنید.\n\nشما باید [$2  یک نگارش ازمجوز عمومی کلی ] همراه این برنامه دریافت کرده باشید. در غیر این صورت با بنیاد نرم‌افزار آزاد، ایالات متحده امریکا، بوستون، خیابان فرانکلین، پلاک ۵۱، طبقه پنجم، صندوق پستی MA۰۲۱۱۰-۱۳۰ مکاتبه کنید، یا [https://www.gnu.org/copyleft/gpl.html در این‌جا به صورت برخط بخوانید].",
        "config-sidebar": "* [https://www.mediawiki.org وةڵگة اصلی مدیاویکی]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents راهنمای کاربر]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents راهنمای مدیر]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ پرسش‌های رایج]\n----\n* <doclink href=Readme>مرا بخوان</doclink>\n* <doclink href=ReleaseNotes>یادداشت‌های انتشار</doclink>\n* <doclink href=Copying>نسخه برداری</doclink>\n* <doclink href=UpgradeDoc>ارتقا</doclink>",
        "config-env-good": "محیط بررسی شده‌است.\nشما می‌توانید مدیاویکی را نصب کنید.",
        "config-env-bad": "محیط بررسی شده‌است.\nشما نمی‌توانید مدیاویکی را نصب کنید.",
index 44930c5..8d80e21 100644 (file)
@@ -37,7 +37,7 @@
        "config-help-restart": "Tianao hofafana avokoa ve ny data voaangona natsofokao ary hamerina ny fizotran'ny fametrahana ?",
        "config-restart": "Eny, avereno atao",
        "config-welcome": "=== Fanamarinana mikasika ny tontolo ===\nNy fanamarihana tsotsotra dia atao hijerena raha mety ho ana rindrankajy Mediawiki ny tontolo.\nTadidio ny mametraka ireto torohay ireo raha mitady fanohanana mikasika ny fomba famaranana ny fametrahana ianao.",
-       "config-copyright": "== Zom-pamorona ary fepetra ==\n\n$1\n\n\nIo fandaharana dia rindrambaiko maimaim-poana; dia afaka zarazarain ary ovaina araka ny fepetra ao amin'ny GNU General Public License navoakan'ny Free Software Foundation; na versiona 2 ao amin'ny lisansa, na (araka ny safidinao) versiona tatỳ aoriana.\n\nIo fandaharaa io dia zaraina amin'ny fanantenana fa ho ilaina, anefa kosa dia <strong>tsy misy fiantohana</strong>; tsy misy fiantohana mikasika ny <strong>fivarotana azy</strong> na <strong>famendrehana ho azo ampiasaina amin'ny tranga iray manokana</strong>.\nJereo ny GNU General Public License hahazoana zavatra amin'ny antsipiriany.\n\nIanao dia tokony nandray <doclink href=Copying> kôpian'nyGNU General Public License </doclink> miaraka amin'ny fandaharana ity; raha tsy izany, manorata any amin'ny Free Software Foundation, Inc., 51 Franklin Street, Fahadimy Floor, Boston, MA 02110-1301, USA, na [https://www.gnu.org/copyleft/gpl.html vakio ao amin'ny Internet izany].",
+       "config-welcome-section-copyright": "== Zom-pamorona ary fepetra ==\n\n$1\n\n\nIo fandaharana dia rindrambaiko maimaim-poana; dia afaka zarazarain ary ovaina araka ny fepetra ao amin'ny GNU General Public License navoakan'ny Free Software Foundation; na versiona 2 ao amin'ny lisansa, na (araka ny safidinao) versiona tatỳ aoriana.\n\nIo fandaharaa io dia zaraina amin'ny fanantenana fa ho ilaina, anefa kosa dia <strong>tsy misy fiantohana</strong>; tsy misy fiantohana mikasika ny <strong>fivarotana azy</strong> na <strong>famendrehana ho azo ampiasaina amin'ny tranga iray manokana</strong>.\nJereo ny GNU General Public License hahazoana zavatra amin'ny antsipiriany.\n\nIanao dia tokony nandray [$2  kôpian'nyGNU General Public License ] miaraka amin'ny fandaharana ity; raha tsy izany, manorata any amin'ny Free Software Foundation, Inc., 51 Franklin Street, Fahadimy Floor, Boston, MA 02110-1301, USA, na [https://www.gnu.org/copyleft/gpl.html vakio ao amin'ny Internet izany].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki fandraisana]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Torolalan'ny mampiasa]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Torolalan'ny mpandrindra]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Fanontaniana mipetraka matetika]\n----\n* <doclink href=Readme>Vakio aho</doclink>\n* <doclink href=ReleaseNotes>Naoty famoahana</doclink>\n* <doclink href=Copying>Fandikàna</doclink>\n* <doclink href=UpgradeDoc>Fampihatsaràna</doclink>",
        "config-env-good": "Voamarina ny tontolo.\nAfaka apetrakao i MediaWiki.",
        "config-env-bad": "Voamarina ny tontolo.\nTsy afaka mametraka an'i MediaWiki ianao.",
index 64cce74..446a12d 100644 (file)
@@ -45,7 +45,7 @@
        "config-help-restart": "Дали сакате да ги исчистите сите зачувани податоци што ги внесовте и да ја започнете воспоставката одново?",
        "config-restart": "Да, почни одново",
        "config-welcome": "=== Проверки на околината ===\nСега ќе се извршиме основни проверки за да се востанови дали околината е погодна за воспоставкa на МедијаВики. Не заборавајте да ги приложите овие информации ако барате помош со довршување на воспоставката.",
-       "config-copyright": "=== Авторски права и услови ===\n\n$1\n\nОва е слободна програмска опрема (free software); можете да го редистрибуирате и/или менувате согласно условите на ГНУ-овата општа јавна лиценца (GNU General Public License) на Фондацијата за слободна програмска опрема (Free Software Foundation); верзија 2 или било која понова верзија на лиценцата (по ваш избор).\n\nОвој програм се нуди со надеж дека ќе биде корисен, но '''без никаква гаранција'''; дури ни подразбраната гаранција за '''продажна способност''' или '''погодност за определена цел'''.\nПовеќе информации ќе најдете во текстот на ГНУ-овата општа јавна лиценца.\n\nБи требало да имате добиено <doclink href=Copying>примерок од ГНУ-овата општа јавна лиценца</doclink> заедно со програмов; ако немате добиено, тогаш пишете ни на Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. или [https://www.gnu.org/copyleft/gpl.html прочитајте ја тука].",
+       "config-welcome-section-copyright": "=== Авторски права и услови ===\n\n$1\n\nОва е слободна програмска опрема (free software); можете да го редистрибуирате и/или менувате согласно условите на ГНУ-овата општа јавна лиценца (GNU General Public License) на Фондацијата за слободна програмска опрема (Free Software Foundation); верзија 2 или било која понова верзија на лиценцата (по ваш избор).\n\nОвој програм се нуди со надеж дека ќе биде корисен, но '''без никаква гаранција'''; дури ни подразбраната гаранција за '''продажна способност''' или '''погодност за определена цел'''.\nПовеќе информации ќе најдете во текстот на ГНУ-овата општа јавна лиценца.\n\nБи требало да имате добиено [$2 примерок од ГНУ-овата општа јавна лиценца] заедно со програмов; ако немате добиено, тогаш пишете ни на Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. или [https://www.gnu.org/copyleft/gpl.html прочитајте ја тука].",
        "config-sidebar": "* [https://www.mediawiki.org Домашна страница на МедијаВики]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Водич за корисници]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Водич за администратори]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ЧПП]",
        "config-sidebar-readme": "Прочитај ме",
        "config-sidebar-relnotes": "Белешки за изданието",
index 67d9f54..9f31fda 100644 (file)
@@ -42,7 +42,7 @@
        "config-help-restart": "आपण टाकून जतन केलेला सर्व डाटा आपणास साफ करावयाचा व उभारणीची प्रक्रिया पुन्हा सुरू करावयाची आहे काय?",
        "config-restart": "होय, परत चालू करा",
        "config-welcome": "=== पारिसरीक तपासण्या ===\nमिडियाविकिच्या उभारणीस हा परिसर योग्य आहे काय याच्या मूळ तपासण्या आता केल्या जातील.\nजर आपणास पुढे याची उभारणी करण्याबद्दल साहाय्य लागल्यास, याचा अंतर्भाव करणे लक्षात ठेवा.",
-       "config-copyright": "=== प्रताधिकार व अटी ===\n\n$1\nहा कार्यसंच,हे एक मुक्त संचेतन आहे;आपण त्यास पुनर्वितरीत व/किंवा त्यास फ्री सॉफ्टवेअर फाऊंडेशन द्वारे प्रकाशित, GNU जनरल पब्लिक लायसन्स अंतर्गत बदलु शकता;या परवान्याची आवृत्ती २ किंवा (आपल्या इच्छेनुसार)त्यानंतरची आवृत्ती.\n\nहा कार्यसंचाचे वितरण,पण, <strong>कोणत्याही हमीशिवाय</strong>; याशिवाय <strong>व्यापारीकरणाच्या</strong> कोणत्याही अभिप्रेत आश्वासनाशिवाय किंवा <strong>एखाद्या विशिष्ट कार्यासाठीच्या अर्हतेशिवाय</strong>ही आशा ठेऊन केले आहे कि, तो उपयोगी असेल.\nअधिक माहितीसाठी GNU जनरल पब्लिक लायसन्स बघा.\nआपणास या कार्यसंचासमवेत <doclink href=Copying>GNU जनरल पब्लिक लायसन्सची प्रत मिळाली असेल,</doclink>नसल्यास,फ्री सॉफ्टवेअर फाऊंडेशनला या पत्त्यावर लिहा.Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. किंवा त्यास [https://www.gnu.org/copyleft/gpl.html ऑनलाईन वाचा].",
+       "config-welcome-section-copyright": "=== प्रताधिकार व अटी ===\n\n$1\nहा कार्यसंच,हे एक मुक्त संचेतन आहे;आपण त्यास पुनर्वितरीत व/किंवा त्यास फ्री सॉफ्टवेअर फाऊंडेशन द्वारे प्रकाशित, GNU जनरल पब्लिक लायसन्स अंतर्गत बदलु शकता;या परवान्याची आवृत्ती २ किंवा (आपल्या इच्छेनुसार)त्यानंतरची आवृत्ती.\n\nहा कार्यसंचाचे वितरण,पण, <strong>कोणत्याही हमीशिवाय</strong>; याशिवाय <strong>व्यापारीकरणाच्या</strong> कोणत्याही अभिप्रेत आश्वासनाशिवाय किंवा <strong>एखाद्या विशिष्ट कार्यासाठीच्या अर्हतेशिवाय</strong>ही आशा ठेऊन केले आहे कि, तो उपयोगी असेल.\nअधिक माहितीसाठी GNU जनरल पब्लिक लायसन्स बघा.\nआपणास या कार्यसंचासमवेत [$2 GNU जनरल पब्लिक लायसन्सची प्रत मिळाली असेल,]नसल्यास,फ्री सॉफ्टवेअर फाऊंडेशनला या पत्त्यावर लिहा.Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. किंवा त्यास [https://www.gnu.org/copyleft/gpl.html ऑनलाईन वाचा].",
        "config-sidebar": "* [https://www.mediawiki.org मिडियाविकि गृह]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents सदस्य मार्गदर्शिका]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents प्रशासकाची मार्गदर्शिका]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ एफएक्यू]\n----\n* <doclink href=Readme>रीड मी</doclink>\n* <doclink href=ReleaseNotes>विमोचन टिप्पण्या</doclink>\n* <doclink href=Copying>नकलविणे</doclink>\n* <doclink href=UpgradeDoc>दर्जोन्नती करणे</doclink>",
        "config-env-good": "पारिसरीक तपासणी झाली आहे.\nआपण मिडियाविकि उभारू शकता.",
        "config-env-bad": "पारिसरीक तपासणी झाली आहे.\nआपण मिडियाविकि उभारू शकत नाही.",
index 0318f64..d466362 100644 (file)
@@ -47,7 +47,7 @@
        "config-help-restart": "Adakah anda ingin untuk membersihkan semua data yang disimpan yang anda telah masukkan dan memulakan semula proses pemasangan?",
        "config-restart": "Ya, mula semula",
        "config-welcome": "=== Pemeriksaan persekitaran ===\nPemeriksaan asas kini boleh dilakukan untuk melihat jika persekitaran ini adalah sesuai untuk pemasangan MediaWiki.\nIngat untuk memasukkan maklumat ini jika anda mahukan sokongan tentang bagaimana untuk menyelesaikan pemasangan.",
-       "config-copyright": "=== Hakcipta dan Syarat-Syarat ===\n\n$1\n\nProgram ini merupakan perisian bebas; anda boleh mengedarkannya semula dan/atau mengubahsuainya di bawah syarat-syarat Lesen Awam GNU seperti yang diterbitkan oleh Yayasan Perisian Bebas; sama ada versi 2 Lesen ini atau (mengikut pilihan anda) mana-mana versi selepas ini.\n\nProgram ini diedarkan dengan harapan bahawa ia akan menjadi berguna, tetapi '''tanpa sebarang waranti'''; tanpa jaminan yang tersirat '''kebolehdagangan''' atau '''kesesuaian untuk tujuan tertentu'''.\nLihat Lesen Awam GNU untuk maklumat lanjut.\n\nAnda sepatutnya telah menerima <doclink href=Copying> satu salinan Lesen Awam GNU </doclink> bersama-sama dengan program ini, jika tidak, menulis surat kepada Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, atau [https://www.gnu.org/copyleft/gpl.html membacanya dalam talian].",
+       "config-welcome-section-copyright": "=== Hakcipta dan Syarat-Syarat ===\n\n$1\n\nProgram ini merupakan perisian bebas; anda boleh mengedarkannya semula dan/atau mengubahsuainya di bawah syarat-syarat Lesen Awam GNU seperti yang diterbitkan oleh Yayasan Perisian Bebas; sama ada versi 2 Lesen ini atau (mengikut pilihan anda) mana-mana versi selepas ini.\n\nProgram ini diedarkan dengan harapan bahawa ia akan menjadi berguna, tetapi '''tanpa sebarang waranti'''; tanpa jaminan yang tersirat '''kebolehdagangan''' atau '''kesesuaian untuk tujuan tertentu'''.\nLihat Lesen Awam GNU untuk maklumat lanjut.\n\nAnda sepatutnya telah menerima [$2  satu salinan Lesen Awam GNU ] bersama-sama dengan program ini, jika tidak, menulis surat kepada Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, atau [https://www.gnu.org/copyleft/gpl.html membacanya dalam talian].",
        "config-sidebar": "* [https://www.mediawiki.org Laman utama MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Panduan Pengguna]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Panduan Penyelia]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Soalan lazim]\n----\n* <doclink href=Readme>Baca saya</doclink>\n* <doclink href=ReleaseNotes>Nota keluaran</doclink>\n* <doclink href=Copying>Menyalin</doclink>\n* <doclink href=UpgradeDoc>Menaik taraf</doclink>",
        "config-env-good": "Persekitaran telah diperiksa.\nAnda boleh memasang MediaWiki.",
        "config-env-bad": "Persekitaran telah diperiksa. \nAnda tidak boleh memasang MediaWiki.",
index 67c20e0..c90f816 100644 (file)
@@ -43,7 +43,7 @@
        "config-help-restart": "你敢欲共你拍的佮保存的資料攏清掉,並且重開始安裝的動作?",
        "config-restart": "是,重來",
        "config-welcome": "=== 環境檢測 ===\n這馬欲做基本的檢測,看環境是毋是適合裝 MediaWiki。\n若你愛有支援,才裝會起來,請共遮的資訊記起來。",
-       "config-copyright": "=== Pán-koân seng-bêng kap siū-koân tiâu-khoán ===\n\n$1\n\nPún têng-sek sī chū-iû nńg-thé; lí thang chiàu Chū-iû Nńg-thé Ki-kim-hoē só͘ hoat-piáu--ê GNU Thong-iōng Kong-kiōng Siū-koân Tiâu-khoán kui-tēng, kā pún têng-sek têng hoat-pò͘ iah-sī siu-kái; bô-lūn lí sī chiàu pún siū-koân tiâu-khoán--ê tē 2 pán iah koh khah sin--ê pán-pún (lí thang ka-kī kéng).\n\nPún têng-sek hoat-pò͘--ê bo̍k-tek sī ǹg-bāng ē-tàng pang-chān, m̄-koh <strong>bô hù jīm-hô tam-pó͘ chek-jīm</strong>; iah bô piáu-sī kóng tùi <strong>hoàn-bē-sèng</strong> iah <strong>te̍k-tēng iōng-tô͘--ê sek-iōng-sèng</strong>--ê chêng-hêng tam-pó͘. Siông-sè chhiáⁿ chham-khó GNU Thong-iōng Kong-kiōng Siū-koân.\n\nLí èng-kai tùi pún têng-sek siu-tio̍h <doclink href=\"Copying\">GNU Thong-iōng Kong-kiōng Siū-koân--ê Hù-pún</doclink>; nā-bô, chhiáⁿ siá-phoe thong-tī Chū-iû Nńg-thé KI-kim-hoē, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, iah-sī [https://www.gnu.org/copyleft/gpl.html soàⁿ-téng khoàⁿ].",
+       "config-welcome-section-copyright": "=== Pán-koân seng-bêng kap siū-koân tiâu-khoán ===\n\n$1\n\nPún têng-sek sī chū-iû nńg-thé; lí thang chiàu Chū-iû Nńg-thé Ki-kim-hoē só͘ hoat-piáu--ê GNU Thong-iōng Kong-kiōng Siū-koân Tiâu-khoán kui-tēng, kā pún têng-sek têng hoat-pò͘ iah-sī siu-kái; bô-lūn lí sī chiàu pún siū-koân tiâu-khoán--ê tē 2 pán iah koh khah sin--ê pán-pún (lí thang ka-kī kéng).\n\nPún têng-sek hoat-pò͘--ê bo̍k-tek sī ǹg-bāng ē-tàng pang-chān, m̄-koh <strong>bô hù jīm-hô tam-pó͘ chek-jīm</strong>; iah bô piáu-sī kóng tùi <strong>hoàn-bē-sèng</strong> iah <strong>te̍k-tēng iōng-tô͘--ê sek-iōng-sèng</strong>--ê chêng-hêng tam-pó͘. Siông-sè chhiáⁿ chham-khó GNU Thong-iōng Kong-kiōng Siū-koân.\n\nLí èng-kai tùi pún têng-sek siu-tio̍h [$2 GNU Thong-iōng Kong-kiōng Siū-koân--ê Hù-pún]; nā-bô, chhiáⁿ siá-phoe thong-tī Chū-iû Nńg-thé KI-kim-hoē, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, iah-sī [https://www.gnu.org/copyleft/gpl.html soàⁿ-téng khoàⁿ].",
        "config-sidebar": "* [www.mediawiki.org/wiki/MediaWiki/zh-hant MediaWiki 頭頁]\n* [www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/zh 使用者指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/zh 管理者指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/zh-hant 四常問題集]\n----\n* <doclink href=Readme>讀我說明</doclink>\n* <doclink href=ReleaseNotes>發行說明</doclink>\n* <doclink href=Copying>版權聲明</doclink>\n* <doclink href=UpgradeDoc>升級</doclink>",
        "config-env-good": "環境檢查已完成。\n你會當安裝 MediaWiki。",
        "config-env-bad": "Khoân-kèng kiám-cha oân-sêng--ah.\nLí bô-hoat-tō͘ an-chng MediaWiki.",
index d4dc4b6..d4ae472 100644 (file)
@@ -44,7 +44,7 @@
        "config-help-restart": "Vulite scancellà tutt' 'e date astipate c'avite nzertato e riabbià 'o prucesso d'installazione?",
        "config-restart": "Sì, riabbìa",
        "config-welcome": "=== Cuntrollo 'e ll'ambiente ===\nSarranno eseguite 'e cuntrolle bbase pe' putè vedè si st'ambiente è adatto pe' ne ffà l'installazione 'e MediaWiki.\nArricurdateve d'includere sti nfurmaziune si spiate assistenza ncopp' 'a maniera 'e cumpletà l'installazione.",
-       "config-copyright": "=== Copyright e termine ===\n\n$1\n\nChistu programma è nu software libbero; vuje 'o putite redestribbuì e/o cagnà sott' 'e termine d' 'a licienza GNU GPL ('a Licienza Pubbreca Generale) comme pubbrecata d' 'a Free Software Foundation; o pure 'a verziona 2 d' 'a Licienza, o pure (comme vulite vuje) 'a n'ata verziona cchiù nnova.\n\nChistu programma è destribbuito c' 'a speranza d'essere utile, ma SENZA NISCIUNA GARANZIA; senza manco 'a garanzia p' 'a CUMMERCIABBELETÀ O IDONIETÀ PE' NU SCOPO PARTICULARE.\nIate a vedé 'a GNU GPL pe' n'avé cchiù nfurmaziune.\n\nCu stu programma avísseve 'a ricevere <doclink href=Copying>na copia d' 'a Licienza GNU GPL</doclink> cu stu prugramma; si nò, scrivete â Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [https://www.gnu.org/copyleft/gpl.html liggite sta paggena ncopp' 'a l'Internet].",
+       "config-welcome-section-copyright": "=== Copyright e termine ===\n\n$1\n\nChistu programma è nu software libbero; vuje 'o putite redestribbuì e/o cagnà sott' 'e termine d' 'a licienza GNU GPL ('a Licienza Pubbreca Generale) comme pubbrecata d' 'a Free Software Foundation; o pure 'a verziona 2 d' 'a Licienza, o pure (comme vulite vuje) 'a n'ata verziona cchiù nnova.\n\nChistu programma è destribbuito c' 'a speranza d'essere utile, ma SENZA NISCIUNA GARANZIA; senza manco 'a garanzia p' 'a CUMMERCIABBELETÀ O IDONIETÀ PE' NU SCOPO PARTICULARE.\nIate a vedé 'a GNU GPL pe' n'avé cchiù nfurmaziune.\n\nCu stu programma avísseve 'a ricevere [$2 na copia d' 'a Licienza GNU GPL] cu stu prugramma; si nò, scrivete â Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA o [https://www.gnu.org/copyleft/gpl.html liggite sta paggena ncopp' 'a l'Internet].",
        "config-sidebar": "* [https://www.mediawiki.org Paggina prencepale MediaWiki]\n* [https://www.mediawiki.org/wiki/Aiuto:Guida a 'e cuntenute pe' l'utente]\n* [https://www.mediawiki.org/wiki/Manuale:Guida a 'e cuntenute pe l'ammenistrature]\n* [https://www.mediawiki.org/wiki/Manuale:FAQ FAQ]\n----\n* <doclink href=Readme>Lieggeme</doclink>\n* <doclink href=ReleaseNotes>Note 'e verziona</doclink>\n* <doclink href=Copying>Copie</doclink>\n* <doclink href=UpgradeDoc>Agghiurnamento</doclink>",
        "config-env-good": "L'ambiente è stato cuntrullato.\nÈ pussibbele installare MediaWiki.",
        "config-env-bad": "L'ambiente è stato cuntrullato.\nNun se può installà MediaWiki.",
index fc299fb..699cd0f 100644 (file)
@@ -49,7 +49,7 @@
        "config-help-restart": "Ønsker du å fjerne alle lagrede data som du har skrevet inn og starte installasjonsprosessen på nytt?",
        "config-restart": "Ja, start på nytt",
        "config-welcome": "=== Miljøsjekker ===\nGrunnleggende sjekker utføres for å se om dette miljøet er egnet for en MediaWiki-installasjon.\nDu bør oppgi resultatene fra disse sjekkene om du trenger hjelp under installasjonen.",
-       "config-copyright": "=== Opphavsrett og vilkår ===\n\n$1\n\nMediaWiki er fri programvare; du kan redistribuere det og/eller modifisere det under betingelsene i GNU General Public License som publisert av Free Software Foundation; enten versjon 2 av lisensen, eller (etter eget valg) enhver senere versjon.\n\nDette programmet er distribuert i håp om at det vil være nyttig, men '''uten noen garanti'''; ikke engang implisitt garanti av '''salgbarhet''' eller '''egnethet for et bestemt formål'''.\nSe GNU General Public License for flere detaljer.\n\nDu skal ha mottatt <doclink href=Copying>en kopi av GNU General Public License</doclink> sammen med dette programmet; hvis ikke, skriv til Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA eller [https://www.gnu.org/copyleft/gpl.html les det på nettet].",
+       "config-welcome-section-copyright": "=== Opphavsrett og vilkår ===\n\n$1\n\nMediaWiki er fri programvare; du kan redistribuere det og/eller modifisere det under betingelsene i GNU General Public License som publisert av Free Software Foundation; enten versjon 2 av lisensen, eller (etter eget valg) enhver senere versjon.\n\nDette programmet er distribuert i håp om at det vil være nyttig, men '''uten noen garanti'''; ikke engang implisitt garanti av '''salgbarhet''' eller '''egnethet for et bestemt formål'''.\nSe GNU General Public License for flere detaljer.\n\nDu skal ha mottatt [$2 en kopi av GNU General Public License] sammen med dette programmet; hvis ikke, skriv til Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA eller [https://www.gnu.org/copyleft/gpl.html les det på nettet].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki hjem]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Brukerguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administratorguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ OSS]\n----\n* <doclink href=Readme>Les meg</doclink>\n* <doclink href=ReleaseNotes>Utgivelsesnotater</doclink>\n* <doclink href=Copying>Kopiering</doclink>\n* <doclink href=UpgradeDoc>Oppgradering</doclink>",
        "config-env-good": "Miljøet har blitt sjekket.\nDu kan installere MediaWiki.",
        "config-env-bad": "Miljøet har blitt sjekket.\nDu kan ikke installere MediaWiki.",
index 49e0e58..1ef41d2 100644 (file)
@@ -14,7 +14,7 @@
        "config-your-language": "Jouw taal:",
        "config-help-restart": "Wil je alle opgeslagen gegevens die je hebt ingevoerd wissen en het installatieproces opnieuw starten?",
        "config-welcome": "=== Controle omgeving ===\nEr worden een aantal basiscontroles uitgevoerd met als doel vast te stellen of deze omgeving geschikt is voor een installatie van MediaWiki.\nAls je hulp nodig hebt bij de installatie, lever deze gegevens dan ook aan.",
-       "config-copyright": "=== Auteursrechten en voorwaarden ===\n\n$1\n\nDit programma is vrije software. Je mag het verder verspreiden en/of aanpassen in overeenstemming met de voorwaarden van de GNU General Public License zoals uitgegeven door de Free Software Foundation; ofwel versie 2 van de Licentie of - naar eigen keuze - enige latere versie.\n\nDit programma wordt verspreid in de hoop dat het nuttig is, maar '''zonder enige garantie''', zelfs zonder de impliciete garantie van '''verkoopbaarheid''' of '''geschiktheid voor een bepaald doel'''.\nZie de GNU General Public License voor meer informatie.\n\nSamen met dit programma hoor je een <doclink href=Copying>exemplaar van de GNU General Public License</doclink> ontvangen te hebben; zo niet, schrijf dan aan de Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, Verenigde Staten. Of [https://www.gnu.org/copyleft/gpl.html lees de licentie online].",
+       "config-welcome-section-copyright": "=== Auteursrechten en voorwaarden ===\n\n$1\n\nDit programma is vrije software. Je mag het verder verspreiden en/of aanpassen in overeenstemming met de voorwaarden van de GNU General Public License zoals uitgegeven door de Free Software Foundation; ofwel versie 2 van de Licentie of - naar eigen keuze - enige latere versie.\n\nDit programma wordt verspreid in de hoop dat het nuttig is, maar '''zonder enige garantie''', zelfs zonder de impliciete garantie van '''verkoopbaarheid''' of '''geschiktheid voor een bepaald doel'''.\nZie de GNU General Public License voor meer informatie.\n\nSamen met dit programma hoor je een [$2 exemplaar van de GNU General Public License] ontvangen te hebben; zo niet, schrijf dan aan de Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, Verenigde Staten. Of [https://www.gnu.org/copyleft/gpl.html lees de licentie online].",
        "config-env-good": "De omgeving is gecontroleerd.\nJe kunt MediaWiki installeren.",
        "config-env-bad": "De omgeving is gecontroleerd.\nJe kunt MediaWiki niet installeren.",
        "config-unicode-pure-php-warning": "'''Waarschuwing''': de [https://pecl.php.net/intl PECL-extensie intl] is niet beschikbaar om de Unicodenormalisatie af te handelen en daarom wordt de langzame PHP-implementatie gebruikt.\nAls je MediaWiki voor een website met veel verkeer installeert, lees je dan in over [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations Unicodenormalisatie].",
index 6a62fb6..a17f6f9 100644 (file)
        "config-help-restart": "Wilt u alle opgeslagen gegevens die u hebt ingevoerd wissen en het installatieproces opnieuw starten?",
        "config-restart": "Ja, opnieuw starten",
        "config-welcome": "=== Controle omgeving ===\nEr worden een aantal basiscontroles uitgevoerd met als doel vast te stellen of deze omgeving geschikt is voor een installatie van MediaWiki.\nLever deze gegevens aan als u ondersteuning vraagt bij de installatie.",
-       "config-copyright": "=== Auteursrechten en voorwaarden ===\n\n$1\n\nDit programma is vrije software. U mag het verder verspreiden en/of aanpassen in overeenstemming met de voorwaarden van de GNU General Public License zoals uitgegeven door de Free Software Foundation; ofwel versie 2 van de Licentie of - naar uw keuze - enige latere versie.\n\nDit programma wordt verspreid in de hoop dat het nuttig is, maar '''zonder enige garantie''', zelfs zonder de impliciete garantie van '''verkoopbaarheid''' of '''geschiktheid voor een bepaald doel'''.\nZie de GNU General Public License voor meer informatie.\n\nSamen met dit programma hoort u een <doclink href=Copying>exemplaar van de GNU General Public License</doclink> ontvangen te hebben; zo niet, schrijf dan aan de Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, Verenigde Staten. Of [https://www.gnu.org/copyleft/gpl.html lees de licentie online].",
-       "config-sidebar": "* [https://www.mediawiki.org MediaWiki-thuispagina]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Gebruikershandleiding] (Engelstalig)\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Beheerdershandleiding] (Engelstalig)\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Veelgestelde vragen] (Engelstalig)\n----\n* <doclink href=Readme>Leesmij</doclink> (Engelstalig)\n* <doclink href=ReleaseNotes>Release notes</doclink> (Engelstalig)\n* <doclink href=Copying>Kopiëren</doclink> (Engelstalig)\n* <doclink href=UpgradeDoc>Versie bijwerken</doclink> (Engelstalig)",
+       "config-welcome-section-copyright": "=== Auteursrechten en voorwaarden ===\n\n$1\n\nDit programma is vrije software. U mag het verder verspreiden en/of aanpassen in overeenstemming met de voorwaarden van de GNU General Public License zoals uitgegeven door de Free Software Foundation; ofwel versie 2 van de Licentie of - naar uw keuze - enige latere versie.\n\nDit programma wordt verspreid in de hoop dat het nuttig is, maar '''zonder enige garantie''', zelfs zonder de impliciete garantie van '''verkoopbaarheid''' of '''geschiktheid voor een bepaald doel'''.\nZie de GNU General Public License voor meer informatie.\n\nSamen met dit programma hoort u een [$2 exemplaar van de GNU General Public License] ontvangen te hebben; zo niet, schrijf dan aan de Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, Verenigde Staten. Of [https://www.gnu.org/copyleft/gpl.html lees de licentie online].",
+       "config-sidebar": "* [https://www.mediawiki.org MediaWiki-thuispagina]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Gebruikershandleiding]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Beheerdershandleiding]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Veelgestelde vragen]",
+       "config-sidebar-readme": "Leesmij",
+       "config-sidebar-relnotes": "Release notes",
+       "config-sidebar-license": "Licentie",
+       "config-sidebar-upgrade": "Informatie over het bijwerken",
        "config-env-good": "De omgeving is gecontroleerd.\nU kunt MediaWiki installeren.",
        "config-env-bad": "De omgeving is gecontroleerd.\nU kunt MediaWiki niet installeren.",
        "config-env-php": "PHP $1 is geïnstalleerd.",
index 18dc924..66266b4 100644 (file)
@@ -64,7 +64,7 @@
        "config-help-restart": "Czy chcesz usunąć wszystkie zapisane dane i uruchomić ponownie proces instalacji?",
        "config-restart": "Tak, zacznij od nowa",
        "config-welcome": "=== Sprawdzenie środowiska instalacji ===\nTeraz zostaną wykonane podstawowe testy sprawdzające czy to środowisko jest odpowiednie dla instalacji MediaWiki.\nJeśli potrzebujesz pomocy podczas instalacji, załącz wyniki tych testów.",
-       "config-copyright": "=== Prawa autorskie i warunki użytkowania ===\n\n$1\n\nTo oprogramowanie jest wolne; możesz je rozprowadzać dalej i modyfikować zgodnie z warunkami licencji GNU General Public License opublikowanej przez Free Software Foundation w wersji 2 tej licencji lub (według Twojego wyboru) którejś z późniejszych jej wersji.\n\nNiniejsze oprogramowanie jest rozpowszechniane w nadziei, że będzie użyteczne, ale '''bez żadnej gwarancji'''; nawet bez domniemanej gwarancji '''handlowej''' lub '''przydatności do określonego celu'''.\nZobacz treść licencji GNU General Public License, aby uzyskać więcej szczegółów.\n\nRazem z oprogramowaniem powinieneś otrzymać <doclink href=Copying>kopię licencji GNU General Public License</doclink>. Jeśli jej nie otrzymałeś, napisz do Free Software Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. lub [https://www.gnu.org/copyleft/gpl.html przeczytaj ją online].",
+       "config-welcome-section-copyright": "=== Prawa autorskie i warunki użytkowania ===\n\n$1\n\nTo oprogramowanie jest wolne; możesz je rozprowadzać dalej i modyfikować zgodnie z warunkami licencji GNU General Public License opublikowanej przez Free Software Foundation w wersji 2 tej licencji lub (według Twojego wyboru) którejś z późniejszych jej wersji.\n\nNiniejsze oprogramowanie jest rozpowszechniane w nadziei, że będzie użyteczne, ale '''bez żadnej gwarancji'''; nawet bez domniemanej gwarancji '''handlowej''' lub '''przydatności do określonego celu'''.\nZobacz treść licencji GNU General Public License, aby uzyskać więcej szczegółów.\n\nRazem z oprogramowaniem powinieneś otrzymać [$2 kopię licencji GNU General Public License]. Jeśli jej nie otrzymałeś, napisz do Free Software Foundation, Inc, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. lub [https://www.gnu.org/copyleft/gpl.html przeczytaj ją online].",
        "config-sidebar": "* [https://www.mediawiki.org Strona domowa MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Podręcznik użytkownika]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Podręcznik administratora]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Odpowiedzi na często zadawane pytania]\n----\n* <doclink href=Readme>Przeczytaj to</doclink>\n* <doclink href=ReleaseNotes>Informacje o tej wersji</doclink>\n* <doclink href=Copying>Kopiowanie</doclink>\n* <doclink href=UpgradeDoc>Aktualizacja</doclink>",
        "config-env-good": "Środowisko oprogramowania zostało sprawdzone.\nMożesz teraz zainstalować MediaWiki.",
        "config-env-bad": "Środowisko oprogramowania zostało sprawdzone.\nNie możesz zainstalować MediaWiki.",
index 130d507..08678ae 100644 (file)
@@ -48,7 +48,7 @@
        "config-help-restart": "Veul-lo scancelé tùit ij dat salvà ch'a l'ha anserì e anandié torna ël process d'instalassion?",
        "config-restart": "É!, felo torna parte",
        "config-welcome": "=== Contròj d'ambient ===\nDle verìfiche ëd base a saran adess fàite për vëdde se st'ambient a va bin për l'instalassion ëd MediaWiki.\nCh'as visa d'anserì coste anformassion s'a sërca d'agiut su com completé l'instalassion.",
-       "config-copyright": "=== Drit d'Autor e Condission ===\n\n$1\n\nCost-sì a l'é un programa lìber e a gràtis: a peul ridistribuilo e/o modifichelo sota le condission dla licensa pùblica general GNU com publicà da la Free Software Foundation; la version 2 dla Licensa, o (a toa sèrnìa) qualsëssìa version pi recenta.\n\nCost programa a l'é distribuì ant la speransa ch'a sia ùtil, ma '''sensa gnun-e garansìe'''; sensa gnanca la garansia implìssita ëd '''comersiabilità''' o '''d'esse adat a un but particolar'''.\n\nA dovrìa avèj arseivù <doclink href=Copying>na còpia ëd la licensa pùblica general GNU</doclink> ansema a sto programa; dësnò, ch'a scriva a la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA opura [https://www.gnu.org/copyleft/gpl.html ch'a la lesa an linia].",
+       "config-welcome-section-copyright": "=== Drit d'Autor e Condission ===\n\n$1\n\nCost-sì a l'é un programa lìber e a gràtis: a peul ridistribuilo e/o modifichelo sota le condission dla licensa pùblica general GNU com publicà da la Free Software Foundation; la version 2 dla Licensa, o (a toa sèrnìa) qualsëssìa version pi recenta.\n\nCost programa a l'é distribuì ant la speransa ch'a sia ùtil, ma '''sensa gnun-e garansìe'''; sensa gnanca la garansia implìssita ëd '''comersiabilità''' o '''d'esse adat a un but particolar'''.\n\nA dovrìa avèj arseivù [$2 na còpia ëd la licensa pùblica general GNU] ansema a sto programa; dësnò, ch'a scriva a la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA opura [https://www.gnu.org/copyleft/gpl.html ch'a la lesa an linia].",
        "config-sidebar": "* [https://www.mediawiki.org Intrada MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Guida dl'Utent]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Guida dl'Aministrator]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Soens an ciamo]\n----\n* <doclink href=Readme>Ch'am lesa</doclink>\n* <doclink href=ReleaseNotes>Nòte ëd publicassion</doclink>\n* <doclink href=Copying>Còpia</doclink>\n* <doclink href=UpgradeDoc>Agiornament</doclink>",
        "config-env-good": "L'ambient a l'é stàit controlà.\nIt peule instalé MediaWiki.",
        "config-env-bad": "L'ambient a l'é stàit controlà.\nIt peule pa instalé MediaWiki.",
index 1b0b054..041e021 100644 (file)
@@ -63,7 +63,7 @@
        "config-help-restart": "Deseja limpar todos os dados salvos que você introduziu e reiniciar o processo de instalação?",
        "config-restart": "Sim, reiniciar",
        "config-welcome": "=== Verificações de ambiente ===\nSerão realizadas verificações básicas para determinar se este ambiente é apropriado para a instalação do MediaWiki.\nLembre-se de incluir estas informações se for procurar por suporte para como concluir a instalação.",
-       "config-copyright": "=== Direitos autorais e Termos de uso ===\n\n$1\n\nEste programa é software livre; você pode redistribuí-lo e/ou modificá-lo nos termos da licença GNU General Public License tal como publicada pela Free Software Foundation; tanto a versão 2 da Licença, como (por opção sua) qualquer versão posterior.\n\nEste programa é distribuído na esperança de que seja útil, mas <strong>sem qualquer garantia</strong>; inclusive, sem a garantia implícita da <strong>possibilidade de ser comercializado</strong> ou de <strong>adequação para qualquer finalidade específica</strong>.\nConsulte a licença GNU General Public License para mais detalhes.\n\nEm conjunto com este programa você deve ter recebido <doclink href=Copying>uma cópia da licença GNU General Public License</doclink>; se não a recebeu, peça-a por escrito para Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ou [https://www.gnu.org/copyleft/gpl.html leia-a na internet].",
+       "config-welcome-section-copyright": "=== Direitos autorais e Termos de uso ===\n\n$1\n\nEste programa é software livre; você pode redistribuí-lo e/ou modificá-lo nos termos da licença GNU General Public License tal como publicada pela Free Software Foundation; tanto a versão 2 da Licença, como (por opção sua) qualquer versão posterior.\n\nEste programa é distribuído na esperança de que seja útil, mas <strong>sem qualquer garantia</strong>; inclusive, sem a garantia implícita da <strong>possibilidade de ser comercializado</strong> ou de <strong>adequação para qualquer finalidade específica</strong>.\nConsulte a licença GNU General Public License para mais detalhes.\n\nEm conjunto com este programa você deve ter recebido [$2 uma cópia da licença GNU General Public License]; se não a recebeu, peça-a por escrito para Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ou [https://www.gnu.org/copyleft/gpl.html leia-a na internet].",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/pt-br Página principal do MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/pt-br Ajuda]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/pt-br Manual técnico]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/pt-br FAQ]",
        "config-sidebar-readme": "Leia-me",
        "config-sidebar-relnotes": "Notas de lançamento",
index 93e6f08..2eadc8c 100644 (file)
@@ -62,7 +62,7 @@
        "config-help-restart": "Deseja limpar todos os dados gravados que introduziu e reiniciar o processo de instalação?",
        "config-restart": "Sim, reiniciar",
        "config-welcome": "=== Verificações do ambiente ===\nSerão agora realizadas verificações básicas para determinar se este ambiente é apropriado para instalação do MediaWiki.\nLembre-se de fornecer esta informação se necessitar de pedir ajuda para concluir a instalação.",
-       "config-copyright": "=== Direitos de autor e Condições de utilização ===\n\n$1\n\nEste programa é software livre; pode redistribuí-lo e/ou modificá-lo nos termos da licença GNU General Public License, tal como publicada pela Free Software Foundation; tanto a versão 2 da Licença, como (por opção sua) qualquer versão posterior.\n\nEste programa é distribuído na esperança de que seja útil, mas '''sem qualquer garantia'''; inclusive, sem a garantia implícita da '''possibilidade de ser comercializado''' ou de '''adequação para qualquer finalidade específica'''.\nConsulte a licença GNU General Public License para mais detalhes.\n\nEm conjunto com este programa deve ter recebido <doclink href=Copying>uma cópia da licença GNU General Public License</doclink>; se não a recebeu, peça-a por escrito a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ou [https://www.gnu.org/copyleft/gpl.html leia-a na Internet].",
+       "config-welcome-section-copyright": "=== Direitos de autor e Condições de utilização ===\n\n$1\n\nEste programa é software livre; pode redistribuí-lo e/ou modificá-lo nos termos da licença GNU General Public License, tal como publicada pela Free Software Foundation; tanto a versão 2 da Licença, como (por opção sua) qualquer versão posterior.\n\nEste programa é distribuído na esperança de que seja útil, mas '''sem qualquer garantia'''; inclusive, sem a garantia implícita da '''possibilidade de ser comercializado''' ou de '''adequação para qualquer finalidade específica'''.\nConsulte a licença GNU General Public License para mais detalhes.\n\nEm conjunto com este programa deve ter recebido [$2 uma cópia da licença GNU General Public License]; se não a recebeu, peça-a por escrito a Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA ou [https://www.gnu.org/copyleft/gpl.html leia-a na Internet].",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/pt Página principal do MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/pt Ajuda]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/pt Manual técnico]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/pt FAQ]",
        "config-sidebar-readme": "Leia-me",
        "config-sidebar-relnotes": "Notas de lançamento",
index 6a821b8..eddee83 100644 (file)
@@ -63,7 +63,7 @@
        "config-help-restart": "Message in warning box in MediaWiki installer.",
        "config-restart": "Button text to confirm the installation procedure has to be restarted.",
        "config-welcome": "Notice that the installer is about to check as to whether MediaWiki can be installed.",
-       "config-copyright": "This message follows {{msg-mw|config-env-good}}.\n\nParameters:\n* $1 - copyright and author list",
+       "config-welcome-section-copyright": "This message follows {{msg-mw|config-env-good}}.\n\nParameters:\n* $1 - copyright and author list\n* $2 - Link to the installer page that contains the license text",
        "config-sidebar": "Maximum width for words is 24 characters. Only visible part of the translation counts to this limit.",
        "config-sidebar-readme": "Link in sidebar to read the README documentation",
        "config-sidebar-relnotes": "Link in sidebar to read the RELEASE-NOTES documentation",
index 66eca85..07d9a6c 100644 (file)
@@ -50,7 +50,7 @@
        "config-help-restart": "Doriți să ștergeți toate datele salvate introduse și să reporniți procesul de instalare?",
        "config-restart": "Da, repornește.",
        "config-welcome": "=== Verificări ale mediului ===\nVerificări de bază vor fi efectuate pentru a vedea dacă este potrivit pentru instalarea MediaWiki.\nNu uitați să includeți aceste informații dacă doriți asistență pentru completarea instalării.",
-       "config-copyright": "=== Drepturi de autor și termeni ===\n\n$1\n\nAcest program este un software liber; îl puteți redistribui și / sau modifica în conformitate cu termenii Licenței Publice Generale GNU, publicată de Fundația pentru Software Liber; fie versiunea 2 a Licenței, fie (la alegere) orice versiune ulterioară.\nAcest program este distribuit în speranța că va fi util, dar <strong>fără nicio garanție</strong>; fără nici măcar garanția implicită de <strong>vandabilitate</strong> sau <strong>fitness pentru un anumit scop</strong>.\nPentru mai multe detalii, consultați Licența publică generală GNU.\nAr fi trebuit să fi primit <doclink href = Copying> o copie a GNU General Public License </ doclink> împreună cu acest program; dacă nu, scrieți la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, SUA, sau [https://www.gnu.org/copyleft/gpl.html citiți-o online] .",
+       "config-welcome-section-copyright": "=== Drepturi de autor și termeni ===\n\n$1\n\nAcest program este un software liber; îl puteți redistribui și / sau modifica în conformitate cu termenii Licenței Publice Generale GNU, publicată de Fundația pentru Software Liber; fie versiunea 2 a Licenței, fie (la alegere) orice versiune ulterioară.\nAcest program este distribuit în speranța că va fi util, dar <strong>fără nicio garanție</strong>; fără nici măcar garanția implicită de <strong>vandabilitate</strong> sau <strong>fitness pentru un anumit scop</strong>.\nPentru mai multe detalii, consultați Licența publică generală GNU.\nAr fi trebuit să fi primit [$2 o copie a GNU General Public License] împreună cu acest program; dacă nu, scrieți la Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, SUA, sau [https://www.gnu.org/copyleft/gpl.html citiți-o online] .",
        "config-sidebar": "* [https://www.mediawiki.org Acasă MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administrator's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>Read me</doclink>\n* <doclink href=ReleaseNotes>Release notes</doclink>\n* <doclink href=Copying>Copying</doclink>\n* <doclink href=UpgradeDoc>Upgrading</doclink>",
        "config-env-good": "Verificarea mediului a fost efectuată cu succes.\nPuteți instala MediaWiki.",
        "config-env-bad": "Verificarea mediului a fost efectuată.\nNu puteți instala MediaWiki.",
index a34db5f..4346f58 100644 (file)
@@ -69,7 +69,7 @@
        "config-help-restart": "Вы хотите удалить все сохранённые данные, которые вы ввели, и запустить процесс установки заново?",
        "config-restart": "Да, начать заново",
        "config-welcome": "=== Проверка окружения ===\nБудут проведены базовые проверки с целью определить, подходит ли данная система для установки MediaWiki.\nНе забудьте включить эту информацию, если вам потребуется помощь для завершения установки.",
-       "config-copyright": "=== Авторские права и условия ===\n\n$1\n\nMediaWiki — свободное программное обеспечение, которое вы можете распространять и/или изменять в соответствии с условиями лицензии GNU General Public License, опубликованной фондом свободного программного обеспечения; второй версии, либо любой более поздней версии.\n\nMediaWiki распространяется в надежде, что она будет полезной, но <strong>без каких-либо гарантий</strong>, даже без подразумеваемых гарантий <strong>коммерческой ценности</strong> или <strong>пригодности для определённой цели</strong>. См. лицензию GNU General Public License для более подробной информации.\n\nВы должны были получить <doclink href=Copying>копию GNU General Public License</doclink> вместе с этой программой, если нет, то напишите Free Software Foundation, Inc., по адресу: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или [https://www.gnu.org/copyleft/gpl.html прочтите её онлайн].",
+       "config-welcome-section-copyright": "=== Авторские права и условия ===\n\n$1\n\nMediaWiki — свободное программное обеспечение, которое вы можете распространять и/или изменять в соответствии с условиями лицензии GNU General Public License, опубликованной фондом свободного программного обеспечения; второй версии, либо любой более поздней версии.\n\nMediaWiki распространяется в надежде, что она будет полезной, но <strong>без каких-либо гарантий</strong>, даже без подразумеваемых гарантий <strong>коммерческой ценности</strong> или <strong>пригодности для определённой цели</strong>. См. лицензию GNU General Public License для более подробной информации.\n\nВы должны были получить [$2 копию GNU General Public License] вместе с этой программой, если нет, то напишите Free Software Foundation, Inc., по адресу: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA или [https://www.gnu.org/copyleft/gpl.html прочтите её онлайн].",
        "config-sidebar": "* [https://www.mediawiki.org Сайт MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Справка для пользователей]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Справка для администраторов]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]",
        "config-sidebar-readme": "Прочти меня",
        "config-sidebar-relnotes": "Информация о версии",
index a8bada1..6703c8e 100644 (file)
@@ -45,7 +45,7 @@
        "config-help-restart": "Div ye wish tae clear aw hained data that ye'v entered n restairt the instawlation process?",
        "config-restart": "Ai, restart it",
        "config-welcome": "=== Environmêntal checks ===\nBasic checks will nou be performed tae see gif this environment is suitable fer MediaWiki installâtion.\nMynd tae inclæde this information gif ye seek heelp oan hou tae complete the installâtion.",
-       "config-copyright": "=== Copiericht n Terms ===\n\n$1\n\nThis program is free saffware; ye can redistreebute it n/or modifie it unner the terms o the GNU General Public License aes published bi the Free Software Foundation; either version 2 o the License, or (yer optie) onie later version.\n\nThis program is distributed in the hope that it will be uiseful, but <strong>wioot onie warrantie</strong>; wioot even the implied warrantie o <strong>merchantabeelity</strong> or <strong>fitness fer ae parteecular purpose</strong>.\nSee the GNU General Public License fer mair details.\n\nYe shid hae receeved <doclink href=Copying> ae copie o the GNU General Publeec License</doclink> alang wi this program; gif naw, write til the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, or [https://www.gnu.org/copyleft/gpl.html read it online].",
+       "config-welcome-section-copyright": "=== Copiericht n Terms ===\n\n$1\n\nThis program is free saffware; ye can redistreebute it n/or modifie it unner the terms o the GNU General Public License aes published bi the Free Software Foundation; either version 2 o the License, or (yer optie) onie later version.\n\nThis program is distributed in the hope that it will be uiseful, but <strong>wioot onie warrantie</strong>; wioot even the implied warrantie o <strong>merchantabeelity</strong> or <strong>fitness fer ae parteecular purpose</strong>.\nSee the GNU General Public License fer mair details.\n\nYe shid hae receeved [$2  ae copie o the GNU General Publeec License] alang wi this program; gif naw, write til the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, or [https://www.gnu.org/copyleft/gpl.html read it online].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki home]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents User's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administrator's Guide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>Read me</doclink>\n* <doclink href=ReleaseNotes>Release notes</doclink>\n* <doclink href=Copying>Copiein</doclink>\n* <doclink href=UpgradeDoc>Upgradin</doclink>",
        "config-env-good": "The environment haes been checked.\nYe can install MediaWiki.",
        "config-env-bad": "The environment haes been checked.\nYe canna install MediaWiki.",
index e884770..5234eba 100644 (file)
@@ -44,7 +44,7 @@
        "config-help-restart": "Želite li obrisati sve sačuvane podatke koje ste unijeli i ponovo pokrenuti uspostavu?",
        "config-restart": "Da, pokreni ponovo",
        "config-welcome": "=== Provjere okoline ===\nSada ćemo obaviti osnovne provjere kako bismo utvrdili je li okruženje prikladno za uspostavu MediaWikija. Ne zaboravite navesti ove informacije ako tražite pomoć s dovršavanjem uspostave.",
-       "config-copyright": "=== Autorska prava i uvjeti ===\n\n$1\n\nTo je slobodni softver (free software); možete ga redistribuirati i/ili mijenjati pod uvjetima GNU-ove opće javne licence (GNU General Public License) Zaklade za slobodni softver (Free Software Foundation); verzija 2 ili bilo koja kasnija verzija licence (po vašem izboru).\n\nOvaj se program nudi u nadi da će biti koristan, ali '''bez ikakvog jamstva'''; čak i implicirano jamstvo '''sposobnosti prodaje''' ili '''prikladnosti za određenu svrhu'''.\nViše informacija ćete naći u tekstu GNU-ove opće javne licence.\n\nTrebali ste primiti <doclink href=Copying>primjerak GNU-ove opće javne licence</doclink> zajedno s programima; ako ga ne primite, pišite nam na Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ili [https://www.gnu.org/copyleft/gpl.html pročitajte je ovdje].",
+       "config-welcome-section-copyright": "=== Autorska prava i uvjeti ===\n\n$1\n\nTo je slobodni softver (free software); možete ga redistribuirati i/ili mijenjati pod uvjetima GNU-ove opće javne licence (GNU General Public License) Zaklade za slobodni softver (Free Software Foundation); verzija 2 ili bilo koja kasnija verzija licence (po vašem izboru).\n\nOvaj se program nudi u nadi da će biti koristan, ali '''bez ikakvog jamstva'''; čak i implicirano jamstvo '''sposobnosti prodaje''' ili '''prikladnosti za određenu svrhu'''.\nViše informacija ćete naći u tekstu GNU-ove opće javne licence.\n\nTrebali ste primiti [$2 primjerak GNU-ove opće javne licence] zajedno s programima; ako ga ne primite, pišite nam na Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. ili [https://www.gnu.org/copyleft/gpl.html pročitajte je ovdje].",
        "config-sidebar": "* [https://www.mediawiki.org Početna strana MediaWikija]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Vodič za korisnike]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Vodič za administratore]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ ČPP]\n----\n* <doclink href=Readme>Pročitaj me</doclink>\n* <doclink href=ReleaseNotes>Bilješke o izdanju</doclink>\n* <doclink href=Copying>Kopiranje</doclink>\n* <doclink href=UpgradeDoc>Nadograđivanje</doclink>",
        "config-env-good": "Okruženje je provjereno.\nMožete uspostaviti MediaWiki.",
        "config-env-bad": "Okruženje je provjereno.\nNe možete uspostaviti MediaWiki.",
index 1db3fca..d9fc8dd 100644 (file)
@@ -49,7 +49,7 @@
        "config-help-restart": "Vill du rensa all sparad data som du har angivit och starta om installationen?",
        "config-restart": "Ja, starta om",
        "config-welcome": "=== Miljökontroller ===\nGrundläggande kontroller kommer nu att utföras för att se om denna miljö är lämplig för installation av MediaWiki.\nKom ihåg att ta med denna information om du söker stöd för hur du skall slutföra installationen.",
-       "config-copyright": "=== Upphovsrätt och Villkor ===\n\n$1\n\nDetta program är fri programvara; du kan vidaredistribuera den och/eller modifiera det enligt villkoren i GNU General Public License som publicerats av Free Software Foundation; antingen genom version 2 av licensen, eller (på ditt initiativ) någon senare version.\n\nDetta program är distribuerat i hopp om att det kommer att vara användbart, men '''utan någon garanti'''; utan att ens ha en underförstådd garanti om '''säljbarhet''' eller '''lämplighet för ett särskilt ändamål'''.\nSe GNU General Public License för mer detaljer.\n\nDu bör ha fått <doclink href=Copying>en kopia av GNU General Public License</doclink> tillsammans med detta program; om inte, skriv till Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, eller [https://www.gnu.org/copyleft/gpl.html läs den online].",
+       "config-welcome-section-copyright": "=== Upphovsrätt och Villkor ===\n\n$1\n\nDetta program är fri programvara; du kan vidaredistribuera den och/eller modifiera det enligt villkoren i GNU General Public License som publicerats av Free Software Foundation; antingen genom version 2 av licensen, eller (på ditt initiativ) någon senare version.\n\nDetta program är distribuerat i hopp om att det kommer att vara användbart, men '''utan någon garanti'''; utan att ens ha en underförstådd garanti om '''säljbarhet''' eller '''lämplighet för ett särskilt ändamål'''.\nSe GNU General Public License för mer detaljer.\n\nDu bör ha fått [$2 en kopia av GNU General Public License] tillsammans med detta program; om inte, skriv till Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, eller [https://www.gnu.org/copyleft/gpl.html läs den online].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWikis webbplats]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Användarguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Administratörsguide]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Frågor och svar]",
        "config-sidebar-readme": "Läs mig",
        "config-sidebar-relnotes": "Utgivningsanteckningar",
index 1460fec..49bb4f4 100644 (file)
@@ -44,7 +44,7 @@
        "config-help-restart": "మీరు భద్రపరిచిన డేటా మొత్తాన్ని తీసివేసి స్థాపనను తిరిగి ప్రారంభించాలా?",
        "config-restart": "ఔను, తిరిగి ప్రారంభించు",
        "config-welcome": "=== పర్యావరణ పరీక్షలు ===\nఈ పర్యావరణం MediaWiki స్థాపనకు అనుకూలంగా ఉందో లేదో చూసే ప్రాథమిక పరీక్షలు ఇపుడు చేస్తాం.\nస్థాపనను ఎలా పూర్తి చెయ్యాలనే విషయమై మీకు సహాయం అడిగేటపుడు, ఈ సమాచారాన్ని ఇవ్వాలని గుర్తుంచుకోండి.",
-       "config-copyright": "=== కాపీహక్కు, నిబంధనలు===\n\n$1\n\nఇది ఉచిత సాఫ్ట్‌వేరు; ఫ్రీ సాఫ్ట్‌వేర్ ఫౌండేషన్ వారు ప్రచురించిన GNU జనరల్ పబ్లిక్ లైసెన్సును (2వ లేదా తరువాతి వర్షన్) అనుసరించి దీన్ని పంపిణీ చెయ్యవచ్చు లేదా మార్చుకోనూవచ్చు.\n\nదీని వలన ఉపయోగం ఉంటుందనే నమ్మకంతో ప్రచురింపబడింది. కానీ <strong>ఎటువంటి వారంటీ లేదు</strong>; <strong> వర్తకం చేయదగ్గ </strong>  లేదా <strong> ఒక అవసరానికి సరిపడే సామర్థ్యం</strong> ఉన్నదనే అంతరార్థ వారంటీ కూడా లేదు.\nమరిన్ని వివరాలకు GNU జనరల్ పబ్లిక్ లైసెన్స్ చూడండి.\n\nమీరు ఈ ప్రోగ్రాముతో పాటు <doclink href=Copying> GNU జనరల్ పబ్లిక్ లైసెన్స్ ప్రతిని </doclink> అందుకుని ఉండాలి; లేకపోతే, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA కు జాబు రాయండి లేదా [https://www.gnu.org/copyleft/gpl.html ఆన్‌లైన్‌లో చదివండి].",
+       "config-welcome-section-copyright": "=== కాపీహక్కు, నిబంధనలు===\n\n$1\n\nఇది ఉచిత సాఫ్ట్‌వేరు; ఫ్రీ సాఫ్ట్‌వేర్ ఫౌండేషన్ వారు ప్రచురించిన GNU జనరల్ పబ్లిక్ లైసెన్సును (2వ లేదా తరువాతి వర్షన్) అనుసరించి దీన్ని పంపిణీ చెయ్యవచ్చు లేదా మార్చుకోనూవచ్చు.\n\nదీని వలన ఉపయోగం ఉంటుందనే నమ్మకంతో ప్రచురింపబడింది. కానీ <strong>ఎటువంటి వారంటీ లేదు</strong>; <strong> వర్తకం చేయదగ్గ </strong>  లేదా <strong> ఒక అవసరానికి సరిపడే సామర్థ్యం</strong> ఉన్నదనే అంతరార్థ వారంటీ కూడా లేదు.\nమరిన్ని వివరాలకు GNU జనరల్ పబ్లిక్ లైసెన్స్ చూడండి.\n\nమీరు ఈ ప్రోగ్రాముతో పాటు [$2  GNU జనరల్ పబ్లిక్ లైసెన్స్ ప్రతిని ] అందుకుని ఉండాలి; లేకపోతే, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA కు జాబు రాయండి లేదా [https://www.gnu.org/copyleft/gpl.html ఆన్‌లైన్‌లో చదివండి].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki మొదటిపేజీ]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents వాడుకరుల మార్గదర్శి]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents అధికారుల మార్గదర్శి]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]\n----\n* <doclink href=Readme>చదవాల్సినవి</doclink>\n* <doclink href=ReleaseNotes>విడుదల గమనికలు</doclink>\n* <doclink href=Copying>కాపీ చెయ్యడం</doclink>\n* <doclink href=UpgradeDoc>ఉన్నతీకరించడం</doclink>",
        "config-env-good": "పర్యావరణాన్ని పరీక్షించాం.\nఇక మీరు MediaWiki ని స్థాపించుకోవచ్చు.",
        "config-env-bad": "పర్యావరణాన్ని పరీక్షించాం.\nమీరు MediaWiki ని స్థాపించలేరు.",
index a4e2ac0..d4e6731 100644 (file)
@@ -47,7 +47,7 @@
        "config-help-restart": "คุณต้องการล้างข้อมูลทั้งหมดที่คุณกรอกและเริ่มกระบวนการติดตั้งใหม่อีกครั้งหรือไม่?",
        "config-restart": "ใช่ เริ่มใหม่อีกครั้ง",
        "config-welcome": "=== การตรวจสอบสภาพแวดล้อม ===\nการตรวจสอบเบื้องต้นจะกระทำขึ้น เพื่อยืนยันว่าสภาพแวดล้อมปัจจุบันเหมาะสมสำหรับการติดตั้ง MediaWiki หรือไม่\nโปรดจำไว้ว่าให้รวบรวมผลลัพธ์การตรวจสอบนี้ ถ้าคุณต้องการแสวงหาการสนับสนุนเพื่อที่จะติดตั้งให้สมบูรณ์",
-       "config-copyright": "=== ลิขสิทธิ์และเงื่อนไข ===\n\n$1\n\nโปรแกรมนี้เป็นซอฟต์แวร์เสรี คุณสามารถนำโปรแกรมนี้มาเผยแพร่ซ้ำและ/หรือดัดแปลงได้ภายใต้เงื่อนไขของสัญญาอนุญาตสาธารณะทั่วไปของ GNU (GNU General Public License) ซึ่งเผยแพร่โดย Free Software Foundation (สัญญาอนุญาตรุ่น 2 ขึ้นไป)\n\nโปรแกรมนี้ถูกเผยแพร่โดยหวังว่าจะเป็นประโยชน์แก่ผู้ใช้ แต่<strong>จะไม่มีการรับประกันใด ๆ</strong> แม้แต่การรับประกันเกี่ยวกับ<strong>การนำไปใช้ในการซื้อขาย</strong> หรือ<strong>ความเหมาะสมสำหรับวัตถุประสงค์เฉพาะ</strong>\nสำหรับรายละเอียดเพิ่มเติม โปรดดูที่สัญญาอนุญาตสาธารณะทั่วไปของ GNU\n\nคุณควรได้รับ<doclink href=Copying>สำเนาของสัญญาอนุญาตสาธารณะทั่วไปของ GNU</doclink> มาพร้อมกับโปรแกรมนี้ ถ้าไม่ได้รับ ให้ขอได้ที่ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, \nหรือ[https://www.gnu.org/copyleft/gpl.html อ่านออนไลน์ที่นี่]",
+       "config-welcome-section-copyright": "=== ลิขสิทธิ์และเงื่อนไข ===\n\n$1\n\nโปรแกรมนี้เป็นซอฟต์แวร์เสรี คุณสามารถนำโปรแกรมนี้มาเผยแพร่ซ้ำและ/หรือดัดแปลงได้ภายใต้เงื่อนไขของสัญญาอนุญาตสาธารณะทั่วไปของ GNU (GNU General Public License) ซึ่งเผยแพร่โดย Free Software Foundation (สัญญาอนุญาตรุ่น 2 ขึ้นไป)\n\nโปรแกรมนี้ถูกเผยแพร่โดยหวังว่าจะเป็นประโยชน์แก่ผู้ใช้ แต่<strong>จะไม่มีการรับประกันใด ๆ</strong> แม้แต่การรับประกันเกี่ยวกับ<strong>การนำไปใช้ในการซื้อขาย</strong> หรือ<strong>ความเหมาะสมสำหรับวัตถุประสงค์เฉพาะ</strong>\nสำหรับรายละเอียดเพิ่มเติม โปรดดูที่สัญญาอนุญาตสาธารณะทั่วไปของ GNU\n\nคุณควรได้รับ[$2 สำเนาของสัญญาอนุญาตสาธารณะทั่วไปของ GNU] มาพร้อมกับโปรแกรมนี้ ถ้าไม่ได้รับ ให้ขอได้ที่ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, \nหรือ[https://www.gnu.org/copyleft/gpl.html อ่านออนไลน์ที่นี่]",
        "config-sidebar": "* [https://www.mediawiki.org โฮมเพจของ MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents แนวปฏิบัติของผู้ใช้]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents แนวปฏิบัติของผู้ดูแลระบบ]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ คำถามที่ถามบ่อย]\n----\n* <doclink href=Readme>อ่านเอกสารกำกับ</doclink>\n* <doclink href=ReleaseNotes>บันทึกการเผยแพร่</doclink>\n* <doclink href=Copying>การคัดลอก</doclink>\n* <doclink href=UpgradeDoc>การอัปเกรด</doclink>",
        "config-env-good": "ตรวจสอบสภาพแวดล้อมแล้ว\nคุณสามารถติดตั้ง MediaWiki",
        "config-env-bad": "ตรวจสอบสภาพแวดล้อมแล้ว\nคุณไม่สามารถติดตั้ง MediaWiki",
index fe53fa1..dc17ddf 100644 (file)
@@ -48,7 +48,7 @@
        "config-help-restart": "Nais mo bang hawiin ang lahat ng nasagip na datong ipinasok mo at muling simulan ang proseso ng pagluluklok?",
        "config-restart": "Oo, muling simulan ito",
        "config-welcome": "=== Pagsusuring pangkapaligiran ===\nIsasagawa ang payak na mga pagsusuri upang makita kung ang kapaligirang ito ay angkop para sa pagluluklok ng MediaWiki.\nTandaan na dapat isama mo itong impormasyon kung kailangan mo ng tulong kung paano tapusin ang instalasyon.",
-       "config-copyright": "=== Karapatang-ari at Tadhana ===\n\n$1\n\nAng programang ito ay malayang software; maaari mo itong ipamahagi at/o baguhin sa ilalim ng mga tadhana ng Pangkalahatang Pampublikong Lisensiyang GNU ayon sa pagkakalathala ng Free Software Foundation; na maaaring bersyong 2 ng Lisensiya, o (kung nais mo) anumang susunod na bersyon.\n\nIpinamamahagi ang programang ito na umaasang magiging gamitin, subaliut '''walang anumang katiyakan'''; na walang pahiwatig ng '''pagiging mabenta''' o '''kaangkupan para sa isang tiyak na layunin'''.\nTingnan ang Pangkalahatang Pampublikong Lisensiyang GNU para sa mas maraming detalye.\n\nDapat nakatanggap ka ng <doclink href=Copying>isang sipi ng Pangkalahatang Pampublikong Lisensiyang GNU</doclink> kasama ng programang ito; kung hindi, sumulat sa Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/licenses//gpl.html basahin ito sa Internet].",
+       "config-welcome-section-copyright": "=== Karapatang-ari at Tadhana ===\n\n$1\n\nAng programang ito ay malayang software; maaari mo itong ipamahagi at/o baguhin sa ilalim ng mga tadhana ng Pangkalahatang Pampublikong Lisensiyang GNU ayon sa pagkakalathala ng Free Software Foundation; na maaaring bersyong 2 ng Lisensiya, o (kung nais mo) anumang susunod na bersyon.\n\nIpinamamahagi ang programang ito na umaasang magiging gamitin, subaliut '''walang anumang katiyakan'''; na walang pahiwatig ng '''pagiging mabenta''' o '''kaangkupan para sa isang tiyak na layunin'''.\nTingnan ang Pangkalahatang Pampublikong Lisensiyang GNU para sa mas maraming detalye.\n\nDapat nakatanggap ka ng [$2 isang sipi ng Pangkalahatang Pampublikong Lisensiyang GNU] kasama ng programang ito; kung hindi, sumulat sa Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA, o [https://www.gnu.org/licenses//gpl.html basahin ito sa Internet].",
        "config-sidebar": "* [https://www.mediawiki.org Tahanan ng MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Gabay ng Tagagamit]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Gabay ng Tagapangasiwa]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Mga Malimit Itanong]\n----\n* <doclink href=Readme>Basahin ako</doclink>\n* <doclink href=ReleaseNotes>Mga tala ng paglalabas</doclink>\n* <doclink href=Copying>Pagkopya</doclink>\n* <doclink href=UpgradeDoc>Pagsasapanahon</doclink>",
        "config-env-good": "Nasuri na ang kapaligiran.\nMailuluklok mo ang MediaWiki.",
        "config-env-bad": "Nasuri na ang kapaligiran.\nHindi mo mailuklok ang MediaWiki.",
index feb682a..95e53ca 100644 (file)
@@ -60,7 +60,7 @@
        "config-help-restart": "Girişini yaptığınız tüm kayıtlı verileri silerek, yükleme işlemini yeniden başlatmak ister misiniz?",
        "config-restart": "Evet, yeniden başlat",
        "config-welcome": "===Ortam Kontrolleri===\nOrtamın Mediawiki kurulumuna uygun olup olmadığını anlamak için basit kontroller yapılacak.\nKurulumu nasıl tamamlayacağınız konusunda destek isterken bu bilgileri eklemeyi unutmayın.",
-       "config-copyright": "=== Telif Hakları ve Koşulları ===\n\n$1\n\nBu program ücretsiz bir yazılımdır; yeniden dağıtabilir veya Özgür Yazılım Kuruluşu tarafından yayınlanan (GNU) Genel Kamu Lisansı koşulları altında değiştirebilirsiniz; isterseniz ikinci lisans sürümünü veya (sizin seçeneğiniz) herhangi bir sonraki lisans sürümünü kullanabilirsiniz.\n\nBu program, faydalı olacağı umuduyla dağıtılmaktadır, ancak ''' herhangi bir garantisi yoktur '''; ''' uygunluk ''' veya ''' belirli bir amaca uygunluk ''' gibi dolaylı garantileri bile yoktur.\nDaha fazla ayrıntı için (GNU) Genel Kamu Lisansına bakınız.\n\nBu program ile birlikte <doclink href=\"Copying\">bir (GNU) Genel Kamu Lisansının bir kopyasını </doclink> almış olmanız gerekir; bu program (GNU) Genel Kamu Lisansı ile dağıtılmadıysa, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ABD adresine yazın veya [https://www.gnu.org/copyleft/gpl.html online olarak okuyun].",
+       "config-welcome-section-copyright": "=== Telif Hakları ve Koşulları ===\n\n$1\n\nBu program ücretsiz bir yazılımdır; yeniden dağıtabilir veya Özgür Yazılım Kuruluşu tarafından yayınlanan (GNU) Genel Kamu Lisansı koşulları altında değiştirebilirsiniz; isterseniz ikinci lisans sürümünü veya (sizin seçeneğiniz) herhangi bir sonraki lisans sürümünü kullanabilirsiniz.\n\nBu program, faydalı olacağı umuduyla dağıtılmaktadır, ancak ''' herhangi bir garantisi yoktur '''; ''' uygunluk ''' veya ''' belirli bir amaca uygunluk ''' gibi dolaylı garantileri bile yoktur.\nDaha fazla ayrıntı için (GNU) Genel Kamu Lisansına bakınız.\n\nBu program ile birlikte [$2 bir (GNU) Genel Kamu Lisansının bir kopyasını ] almış olmanız gerekir; bu program (GNU) Genel Kamu Lisansı ile dağıtılmadıysa, Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ABD adresine yazın veya [https://www.gnu.org/copyleft/gpl.html online olarak okuyun].",
        "config-sidebar": "* [https://www.mediawiki.org MediaWiki anasayfa]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Kullanıcı Kılavuzu]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Hizmetli Rehberi]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ SSS]\n----\n* <doclink href=Readme>Beni oku</doclink>\n* <doclink href=ReleaseNotes>Sürüm notları</doclink>\n* <doclink href=Copying>Kopyalama</doclink>\n* <doclink href=UpgradeDoc>Yükseltme</doclink>",
        "config-env-good": "Ortam kontrol edildi.\nMediaWiki'yi kurabilirsiniz.",
        "config-env-bad": "Ortam kontrol edildi.\nMediaWiki'yi kuramazsınız.",
index bfb0e64..b92e58d 100644 (file)
@@ -55,7 +55,7 @@
        "config-help-restart": "Ви бажаєте видалити всі введені та збережені вами дані і запустити процес установки спочатку?",
        "config-restart": "Так, перезапустити установку",
        "config-welcome": "=== Перевірка оточення ===\nБудуть проведені базові перевірки, щоб виявити, чи можлива установка MediaWiki у даній системі.\nНе забудьте включити цю інформацію, якщо ви звернетеся по підтримку, як завершити установку.",
-       "config-copyright": "=== Авторське право і умови ===\n\n$1\n\nЦя програма є вільним програмним забезпеченням; Ви можете розповсюджувати та/або змінювати її під ліцензією GNU General Public License, опублікованою Фондом вільного програмного забезпечення; версією 2 цієї ліцензії або будь-якою пізнішою на Ваш вибір.\n\nЦя програма поширюється з надією на те, що вона буде корисною, однак '''без жодних гарантій'''; навіть без неявної гарантії '''комерційної цінності''' або '''придатності для певних цілей'''.\nДив. GNU General Public License для детальної інформації.\n\nВи повинні були отримати <doclink href=Copying>копію GNU General Public License</doclink> разом із цією програмою; якщо ж ні, зверніться до Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. або [https://www.gnu.org/copyleft/gpl.html ознайомтесь з нею онлайн].",
+       "config-welcome-section-copyright": "=== Авторське право і умови ===\n\n$1\n\nЦя програма є вільним програмним забезпеченням; Ви можете розповсюджувати та/або змінювати її під ліцензією GNU General Public License, опублікованою Фондом вільного програмного забезпечення; версією 2 цієї ліцензії або будь-якою пізнішою на Ваш вибір.\n\nЦя програма поширюється з надією на те, що вона буде корисною, однак '''без жодних гарантій'''; навіть без неявної гарантії '''комерційної цінності''' або '''придатності для певних цілей'''.\nДив. GNU General Public License для детальної інформації.\n\nВи повинні були отримати [$2 копію GNU General Public License] разом із цією програмою; якщо ж ні, зверніться до Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. або [https://www.gnu.org/copyleft/gpl.html ознайомтесь з нею онлайн].",
        "config-sidebar": "* [https://www.mediawiki.org Сайт MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Посібник користувача]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Посібник адміністратора]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ FAQ]",
        "config-sidebar-readme": "Прочитай мене",
        "config-sidebar-relnotes": "Інформація про версію",
index 03fb785..f71cbfc 100644 (file)
@@ -49,7 +49,7 @@
        "config-help-restart": "Bạn có muốn xóa tất cả dữ liệu được lưu mà bạn vừa nhập và khởi động lại quá trình cài đặt?",
        "config-restart": "Có, khởi động lại nó",
        "config-welcome": "=== Kiểm tra môi trường ===\nBây giờ sẽ kiểm tra sơ qua môi trường này có phù hợp cho việc cài đặt MediaWiki.\nHãy nhớ bao gồm thông tin này khi nào xin hỗ trợ hoàn thành việc cài đặt.",
-       "config-copyright": "=== Bản quyền và Điều khoản ===\n\n$1\n\nChương trình này là phần mềm tự do; bạn được phép tái phân phối và/hoặc sửa đổi nó theo những điều khoản của Giấy phép Công cộng GNU do Quỹ Phần mềm Tự do xuất bản; phiên bản 2 hay bất kỳ phiên bản nào mới hơn nào của Giấy phép (tùy bạn lựa chọn).\n\nChương trình này được phân phối với hy vọng rằng nó sẽ hữu ích, nhưng <strong>không có bất kỳ một đảm bảo nào</strong>, ngay cả những bảo đảm ngụ ý cho <strong>tính thương mại</strong> hoặc <strong>phù hợp với mục đích đặc biệt nào đó</strong>. \nXem Giấy phép Công cộng GNU để biết thêm chi tiết.\n\nCó lẽ bạn đã nhận được <doclink href=Copying>bản sao Giấy phép Công cộng GNU</doclink> đi kèm với chương trình này; nếu không, hãy viết thư đến:\n Free Software Foundation, Inc.\n 51 Franklin St., Fifth Floor\n Boston, MA 02110-1301\n USA\nhoặc [https://www.gnu.org/copyleft/gpl.html đọc nó trực tuyến].",
+       "config-welcome-section-copyright": "=== Bản quyền và Điều khoản ===\n\n$1\n\nChương trình này là phần mềm tự do; bạn được phép tái phân phối và/hoặc sửa đổi nó theo những điều khoản của Giấy phép Công cộng GNU do Quỹ Phần mềm Tự do xuất bản; phiên bản 2 hay bất kỳ phiên bản nào mới hơn nào của Giấy phép (tùy bạn lựa chọn).\n\nChương trình này được phân phối với hy vọng rằng nó sẽ hữu ích, nhưng <strong>không có bất kỳ một đảm bảo nào</strong>, ngay cả những bảo đảm ngụ ý cho <strong>tính thương mại</strong> hoặc <strong>phù hợp với mục đích đặc biệt nào đó</strong>. \nXem Giấy phép Công cộng GNU để biết thêm chi tiết.\n\nCó lẽ bạn đã nhận được [$2 bản sao Giấy phép Công cộng GNU] đi kèm với chương trình này; nếu không, hãy viết thư đến:\n Free Software Foundation, Inc.\n 51 Franklin St., Fifth Floor\n Boston, MA 02110-1301\n USA\nhoặc [https://www.gnu.org/copyleft/gpl.html đọc nó trực tuyến].",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/Special:MyLanguage/MediaWiki Trang chủ MediaWiki]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents Hướng dẫn sử dụng]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents Hướng dẫn quản lý]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ Câu thường hỏi]",
        "config-sidebar-readme": "Đọc thêm",
        "config-sidebar-relnotes": "Thông báo phát hành",
index 6035c61..47a593a 100644 (file)
@@ -65,7 +65,7 @@
        "config-help-restart": "是否要清除所有已输入且保存的数据,并重新启动安装过程吗?",
        "config-restart": "是的,重启吧",
        "config-welcome": "=== 环境检查 ===\n将简单检查当前环境是否适合安装MediaWiki。如果您要寻求安装过程的支持,请记得附上此信息。",
-       "config-copyright": "=== 版权和条款 ===\n\n$1\n\n本程序为自由软件;您可依据自由软件基金会所发表的GNU通用公共授权条款规定,就本程序再为发布与/或修改;无论您依据的是本授权的第二版或(您自行选择的)任一日后发行的版本。\n\n本程序是基于使用目的而加以发布,然而'''不负任何担保责任''';亦无对'''适售性'''或'''特定目的适用性'''所为的默示性担保。详情请参照GNU通用公共授权。\n\n您应已收到附随于本程序的<doclink href=\"Copying\">GNU通用公共授权的副本</doclink>;如果没有,请写信至自由软件基金会:59 Temple Place - Suite 330, Boston, Ma 02111-1307, USA,或[https://www.gnu.org/copyleft/gpl.html 在线阅读]。",
+       "config-welcome-section-copyright": "=== 版权和条款 ===\n\n$1\n\n本程序为自由软件;您可依据自由软件基金会所发表的GNU通用公共授权条款规定,就本程序再为发布与/或修改;无论您依据的是本授权的第二版或(您自行选择的)任一日后发行的版本。\n\n本程序是基于使用目的而加以发布,然而'''不负任何担保责任''';亦无对'''适售性'''或'''特定目的适用性'''所为的默示性担保。详情请参照GNU通用公共授权。\n\n您应已收到附随于本程序的[$2 GNU通用公共授权的副本];如果没有,请写信至自由软件基金会:59 Temple Place - Suite 330, Boston, Ma 02111-1307, USA,或[https://www.gnu.org/copyleft/gpl.html 在线阅读]。",
        "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/zh-hans MediaWiki首页]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/zh-hans 用户指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents 管理员指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/zh-hans 常见问题解答]\n----\n* <doclink href=Readme>自述文件</doclink>\n* <doclink href=ReleaseNotes>发行说明</doclink>\n* <doclink href=Copying>协议副本</doclink>\n* <doclink href=UpgradeDoc>升级</doclink>",
        "config-env-good": "环境检查已经完成。您可以安装MediaWiki。",
        "config-env-bad": "环境检查已经完成。您不能安装MediaWiki。",
index 13a9241..743118c 100644 (file)
        "config-page-restart": "重新安裝",
        "config-page-readme": "讀我說明",
        "config-page-releasenotes": "發佈說明",
-       "config-page-copying": "複製",
+       "config-page-copying": "副本",
        "config-page-upgradedoc": "升級",
        "config-page-existingwiki": "現有的 wiki",
        "config-help-restart": "是否要清除所有已輸入且儲存的資料,並重新開始安裝程序嗎?",
        "config-restart": "是的,重新開始",
        "config-welcome": "=== 環境檢查 ===\n現在會做基本的檢查,檢查環境是否符合 MediaWiki 安裝所需。\n若您要尋求如何完成安裝的協助,請記得提供以下訊息。",
-       "config-copyright": "=== 版權聲明與授權條款 ===\n\n$1\n\n本程式為自由軟體;您可依據自由軟體基金會所發表的 GNU 通用公共授權條款規定,將本程式重新發佈與/或修改;無論您依據的是本授權條款的第二版或 (您可自行選擇) 之後的任何版本。\n\n本程式發佈的目的是希望可以提供幫助,但 <strong>不負任何擔保責任</strong>;亦無隱含對 <strong>適售性</strong> 或 <strong>特定用途的適用性</strong> 的情形擔保。詳情請參照 GNU 通用公共授權。\n\n您應已隨本程式收到 <doclink href=\"Copying\">GNU 通用公共授權條款的副本</doclink>;如果沒有,請信件通知自由軟體基金會,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA,或 [https://www.gnu.org/copyleft/gpl.html 線上閱讀]。",
-       "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/zh-hant MediaWiki 首頁]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/zh 使用者指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/zh 管理員指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/zh 常見問題集]\n----\n* <doclink href=Readme>讀我說明</doclink>\n* <doclink href=ReleaseNotes>發行說明</doclink>\n* <doclink href=Copying>版權聲明</doclink>\n* <doclink href=UpgradeDoc>升級</doclink>",
+       "config-welcome-section-copyright": "=== 版權聲明與授權條款 ===\n\n$1\n\n本程式為自由軟體;您可依據自由軟體基金會所發表的 GNU 通用公共授權條款規定,將本程式重新發佈與/或修改;無論您依據的是本授權條款的第二版或 (您可自行選擇) 之後的任何版本。\n\n本程式發佈的目的是希望可以提供幫助,但 <strong>不負任何擔保責任</strong>;亦無隱含對 <strong>適售性</strong> 或 <strong>特定用途的適用性</strong> 的情形擔保。詳情請參照 GNU 通用公共授權。\n\n您應已隨本程式收到 [$2 GNU 通用公共授權條款的副本];如果沒有,請信件通知自由軟體基金會,51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA,或 [https://www.gnu.org/copyleft/gpl.html 線上閱讀]。",
+       "config-sidebar": "* [https://www.mediawiki.org/wiki/MediaWiki/zh-hant MediaWiki 首頁]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Contents/zh 使用者指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:Contents/zh 管理員指南]\n* [https://www.mediawiki.org/wiki/Special:MyLanguage/Manual:FAQ/zh 常見問題集]",
+       "config-sidebar-readme": "讀我檔案",
+       "config-sidebar-relnotes": "發佈說明",
+       "config-sidebar-license": "副本",
+       "config-sidebar-upgrade": "升級",
        "config-env-good": "環境檢查已完成。\n您可以安裝 MediaWiki。",
        "config-env-bad": "環境檢查已完成。\n您無法安裝 MediaWiki。",
        "config-env-php": "PHP $1 已安裝。",
        "config-env-hhvm": "HHVM $1 已安裝。",
-       "config-unicode-using-intl": "使用 [https://pecl.php.net/intl intl PECL 擴充套件] 做 Unicode 正規化。",
-       "config-unicode-pure-php-warning": "<strong>警告:</strong> 無法使用 [https://pecl.php.net/intl intl PECL 擴充套件] 處理 Unicode 正規化,故回退使用純 PHP 實作的正規化程式,此方式處理速度較緩慢。\n\n如果您的網站瀏覽人次很高,您應先閱讀 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations/zh Unicode 正規化]。",
+       "config-unicode-using-intl": "使用 [https://php.net/manual/en/book.intl.php PHP intl 擴充套件]做 Unicode 正規化。",
+       "config-unicode-pure-php-warning": "<strong>警告:</strong> 無法使用 [https://php.net/manual/en/book.intl.php PHP intl 擴充套件]處理 Unicode 正規化,故回退使用純 PHP 實作的正規化程式,此方式處理速度較緩慢。\n\n如果您的網站瀏覽人次很高,您應先閱讀 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations/zh Unicode 正規化]。",
        "config-unicode-update-warning": "<strong>警告</strong>:目前安裝的 Unicode 正規化包裝程式使用了舊版 [http://site.icu-project.org/ ICU 計劃] 的程式庫。\n若您需要使用 Unicode,您應先進行 [https://www.mediawiki.org/wiki/Special:MyLanguage/Unicode_normalization_considerations 升級]。",
        "config-no-db": "找不到合適的資料庫驅動程式!您需要安裝 PHP 資料庫驅動程式。\n目前支援以下{{PLURAL:$2|類型|類型}}的資料庫: $1 。\n\n如果您是自行編譯 PHP,您必須重新設定並開啟資料庫客戶端,例:使用 <code>./configure --with-mysqli</code> 指令參數。\n如果您是使用 Debian 或 Ubuntu 的套件安裝 PHP ,您則需要額外安裝,例:<code>php-mysql</code> 套件。",
        "config-outdated-sqlite": "<strong>警告:</strong>您已安裝 SQLite $2,但是它的版本低於最低需求版本 $1。 因此您無法使用 SQLite。",
index 465fe82..0954ac8 100644 (file)
@@ -33,7 +33,7 @@
  *
  * @ingroup Cache
  */
-class APCBagOStuff extends BagOStuff {
+class APCBagOStuff extends MediumSpecificBagOStuff {
        /** @var bool Whether to trust the APC implementation to serialization */
        private $nativeSerialize;
 
index b14ac7c..021cdf7 100644 (file)
@@ -33,7 +33,7 @@
  *
  * @ingroup Cache
  */
-class APCUBagOStuff extends BagOStuff {
+class APCUBagOStuff extends MediumSpecificBagOStuff {
        /** @var bool Whether to trust the APC implementation to serialization */
        private $nativeSerialize;
 
index dce49c4..906e955 100644 (file)
@@ -30,7 +30,6 @@ use Psr\Log\LoggerAwareInterface;
 use Psr\Log\LoggerInterface;
 use Psr\Log\NullLogger;
 use Wikimedia\ScopedCallback;
-use Wikimedia\WaitConditionLoop;
 
 /**
  * Class representing a cache/ephemeral data store
@@ -62,86 +61,40 @@ use Wikimedia\WaitConditionLoop;
  * @ingroup Cache
  */
 abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInterface {
-       /** @var array[] Lock tracking */
-       protected $locks = [];
-       /** @var int ERR_* class constant */
-       protected $lastError = self::ERR_NONE;
-       /** @var string */
-       protected $keyspace = 'local';
        /** @var LoggerInterface */
        protected $logger;
+
        /** @var callable|null */
        protected $asyncHandler;
-       /** @var int Seconds */
-       protected $syncTimeout;
-       /** @var int Bytes; chunk size of segmented cache values */
-       protected $segmentationSize;
-       /** @var int Bytes; maximum total size of a segmented cache value */
-       protected $segmentedValueMaxSize;
+       /** @var int[] Map of (ATTR_* class constant => QOS_* class constant) */
+       protected $attrMap = [];
 
        /** @var bool */
-       private $debugMode = false;
-       /** @var array */
-       private $duplicateKeyLookups = [];
-       /** @var bool */
-       private $reportDupes = false;
-       /** @var bool */
-       private $dupeTrackScheduled = false;
-
-       /** @var callable[] */
-       protected $busyCallbacks = [];
+       protected $debugMode = false;
 
        /** @var float|null */
        private $wallClockOverride;
 
-       /** @var int[] Map of (ATTR_* class constant => QOS_* class constant) */
-       protected $attrMap = [];
-
-       /** Bitfield constants for get()/getMulti() */
-       const READ_LATEST = 1; // use latest data for replicated stores
-       const READ_VERIFIED = 2; // promise that caller can tell when keys are stale
-       /** Bitfield constants for set()/merge() */
-       const WRITE_SYNC = 4; // synchronously write to all locations for replicated stores
-       const WRITE_CACHE_ONLY = 8; // Only change state of the in-memory cache
-       const WRITE_ALLOW_SEGMENTS = 16; // Allow partitioning of the value if it is large
-       const WRITE_PRUNE_SEGMENTS = 32; // Delete all partition segments of the value
-
-       /** @var string Component to use for key construction of blob segment keys */
-       const SEGMENT_COMPONENT = 'segment';
+       /** Bitfield constants for get()/getMulti(); these are only advisory */
+       const READ_LATEST = 1; // if supported, avoid reading stale data due to replication
+       const READ_VERIFIED = 2; // promise that the caller handles detection of staleness
+       /** Bitfield constants for set()/merge(); these are only advisory */
+       const WRITE_SYNC = 4; // if supported, block until the write is fully replicated
+       const WRITE_CACHE_ONLY = 8; // only change state of the in-memory cache
+       const WRITE_ALLOW_SEGMENTS = 16; // allow partitioning of the value if it is large
+       const WRITE_PRUNE_SEGMENTS = 32; // delete all the segments if the value is partitioned
+       const WRITE_BACKGROUND = 64; // if supported, do not block on completion until the next read
 
        /**
-        * $params include:
+        * Parameters include:
         *   - logger: Psr\Log\LoggerInterface instance
-        *   - keyspace: Default keyspace for $this->makeKey()
         *   - asyncHandler: Callable to use for scheduling tasks after the web request ends.
         *      In CLI mode, it should run the task immediately.
-        *   - reportDupes: Whether to emit warning log messages for all keys that were
-        *      requested more than once (requires an asyncHandler).
-        *   - syncTimeout: How long to wait with WRITE_SYNC in seconds.
-        *   - segmentationSize: The chunk size, in bytes, of segmented values. The value should
-        *      not exceed the maximum size of values in the storage backend, as configured by
-        *      the site administrator.
-        *   - segmentedValueMaxSize: The maximum total size, in bytes, of segmented values.
-        *      This should be configured to a reasonable size give the site traffic and the
-        *      amount of I/O between application and cache servers that the network can handle.
         * @param array $params
         */
        public function __construct( array $params = [] ) {
                $this->setLogger( $params['logger'] ?? new NullLogger() );
-
-               if ( isset( $params['keyspace'] ) ) {
-                       $this->keyspace = $params['keyspace'];
-               }
-
                $this->asyncHandler = $params['asyncHandler'] ?? null;
-
-               if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
-                       $this->reportDupes = true;
-               }
-
-               $this->syncTimeout = $params['syncTimeout'] ?? 3;
-               $this->segmentationSize = $params['segmentationSize'] ?? 8388608; // 8MiB
-               $this->segmentedValueMaxSize = $params['segmentedValueMaxSize'] ?? 67108864; // 64MiB
        }
 
        /**
@@ -153,10 +106,10 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
        }
 
        /**
-        * @param bool $bool
+        * @param bool $enabled
         */
-       public function setDebug( $bool ) {
-               $this->debugMode = $bool;
+       public function setDebug( $enabled ) {
+               $this->debugMode = $enabled;
        }
 
        /**
@@ -200,52 +153,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
         * @return mixed Returns false on failure or if the item does not exist
         */
-       public function get( $key, $flags = 0 ) {
-               $this->trackDuplicateKeys( $key );
-
-               return $this->resolveSegments( $key, $this->doGet( $key, $flags ) );
-       }
-
-       /**
-        * Track the number of times that a given key has been used.
-        * @param string $key
-        */
-       private function trackDuplicateKeys( $key ) {
-               if ( !$this->reportDupes ) {
-                       return;
-               }
-
-               if ( !isset( $this->duplicateKeyLookups[$key] ) ) {
-                       // Track that we have seen this key. This N-1 counting style allows
-                       // easy filtering with array_filter() later.
-                       $this->duplicateKeyLookups[$key] = 0;
-               } else {
-                       $this->duplicateKeyLookups[$key] += 1;
-
-                       if ( $this->dupeTrackScheduled === false ) {
-                               $this->dupeTrackScheduled = true;
-                               // Schedule a callback that logs keys processed more than once by get().
-                               call_user_func( $this->asyncHandler, function () {
-                                       $dups = array_filter( $this->duplicateKeyLookups );
-                                       foreach ( $dups as $key => $count ) {
-                                               $this->logger->warning(
-                                                       'Duplicate get(): "{key}" fetched {count} times',
-                                                       // Count is N-1 of the actual lookup count
-                                                       [ 'key' => $key, 'count' => $count + 1, ]
-                                               );
-                                       }
-                               } );
-                       }
-               }
-       }
-
-       /**
-        * @param string $key
-        * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
-        * @param mixed|null &$casToken Token to use for check-and-set comparisons
-        * @return mixed Returns false on failure or if the item does not exist
-        */
-       abstract protected function doGet( $key, $flags = 0, &$casToken = null );
+       abstract public function get( $key, $flags = 0 );
 
        /**
         * Set an item
@@ -256,70 +164,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param int $flags Bitfield of BagOStuff::WRITE_* constants
         * @return bool Success
         */
-       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
-               if (
-                       is_int( $value ) || // avoid breaking incr()/decr()
-                       ( $flags & self::WRITE_ALLOW_SEGMENTS ) != self::WRITE_ALLOW_SEGMENTS ||
-                       is_infinite( $this->segmentationSize )
-               ) {
-                       return $this->doSet( $key, $value, $exptime, $flags );
-               }
-
-               $serialized = $this->serialize( $value );
-               $segmentSize = $this->getSegmentationSize();
-               $maxTotalSize = $this->getSegmentedValueMaxSize();
-
-               $size = strlen( $serialized );
-               if ( $size <= $segmentSize ) {
-                       // Since the work of serializing it was already done, just use it inline
-                       return $this->doSet(
-                               $key,
-                               SerializedValueContainer::newUnified( $serialized ),
-                               $exptime,
-                               $flags
-                       );
-               } elseif ( $size > $maxTotalSize ) {
-                       $this->setLastError( "Key $key exceeded $maxTotalSize bytes." );
-
-                       return false;
-               }
-
-               $chunksByKey = [];
-               $segmentHashes = [];
-               $count = intdiv( $size, $segmentSize ) + ( ( $size % $segmentSize ) ? 1 : 0 );
-               for ( $i = 0; $i < $count; ++$i ) {
-                       $segment = substr( $serialized, $i * $segmentSize, $segmentSize );
-                       $hash = sha1( $segment );
-                       $chunkKey = $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $hash );
-                       $chunksByKey[$chunkKey] = $segment;
-                       $segmentHashes[] = $hash;
-               }
-
-               $flags &= ~self::WRITE_ALLOW_SEGMENTS; // sanity
-               $ok = $this->setMulti( $chunksByKey, $exptime, $flags );
-               if ( $ok ) {
-                       // Only when all segments are stored should the main key be changed
-                       $ok = $this->doSet(
-                               $key,
-                               SerializedValueContainer::newSegmented( $segmentHashes ),
-                               $exptime,
-                               $flags
-                       );
-               }
-
-               return $ok;
-       }
-
-       /**
-        * Set an item
-        *
-        * @param string $key
-        * @param mixed $value
-        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
-        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
-        * @return bool Success
-        */
-       abstract protected function doSet( $key, $value, $exptime = 0, $flags = 0 );
+       abstract public function set( $key, $value, $exptime = 0, $flags = 0 );
 
        /**
         * Delete an item
@@ -332,38 +177,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @return bool True if the item was deleted or not found, false on failure
         * @param int $flags Bitfield of BagOStuff::WRITE_* constants
         */
-       public function delete( $key, $flags = 0 ) {
-               if ( ( $flags & self::WRITE_PRUNE_SEGMENTS ) != self::WRITE_PRUNE_SEGMENTS ) {
-                       return $this->doDelete( $key, $flags );
-               }
-
-               $mainValue = $this->doGet( $key, self::READ_LATEST );
-               if ( !$this->doDelete( $key, $flags ) ) {
-                       return false;
-               }
-
-               if ( !SerializedValueContainer::isSegmented( $mainValue ) ) {
-                       return true; // no segments to delete
-               }
-
-               $orderedKeys = array_map(
-                       function ( $segmentHash ) use ( $key ) {
-                               return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
-                       },
-                       $mainValue->{SerializedValueContainer::SEGMENTED_HASHES}
-               );
-
-               return $this->deleteMulti( $orderedKeys, $flags );
-       }
-
-       /**
-        * Delete an item
-        *
-        * @param string $key
-        * @return bool True if the item was deleted or not found, false on failure
-        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
-        */
-       abstract protected function doDelete( $key, $flags = 0 );
+       abstract public function delete( $key, $flags = 0 );
 
        /**
         * Insert an item if it does not already exist
@@ -393,99 +207,13 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @return bool Success
         * @throws InvalidArgumentException
         */
-       public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
-               return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
-       }
-
-       /**
-        * @see BagOStuff::merge()
-        *
-        * @param string $key
-        * @param callable $callback Callback method to be executed
-        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
-        * @param int $attempts The amount of times to attempt a merge in case of failure
-        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
-        * @return bool Success
-        */
-       final protected function mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags ) {
-               do {
-                       $casToken = null; // passed by reference
-                       // Get the old value and CAS token from cache
-                       $this->clearLastError();
-                       $currentValue = $this->resolveSegments(
-                               $key,
-                               $this->doGet( $key, self::READ_LATEST, $casToken )
-                       );
-                       if ( $this->getLastError() ) {
-                               $this->logger->warning(
-                                       __METHOD__ . ' failed due to I/O error on get() for {key}.',
-                                       [ 'key' => $key ]
-                               );
-
-                               return false; // don't spam retries (retry only on races)
-                       }
-
-                       // Derive the new value from the old value
-                       $value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
-                       $hadNoCurrentValue = ( $currentValue === false );
-                       unset( $currentValue ); // free RAM in case the value is large
-
-                       $this->clearLastError();
-                       if ( $value === false ) {
-                               $success = true; // do nothing
-                       } elseif ( $hadNoCurrentValue ) {
-                               // Try to create the key, failing if it gets created in the meantime
-                               $success = $this->add( $key, $value, $exptime, $flags );
-                       } else {
-                               // Try to update the key, failing if it gets changed in the meantime
-                               $success = $this->cas( $casToken, $key, $value, $exptime, $flags );
-                       }
-                       if ( $this->getLastError() ) {
-                               $this->logger->warning(
-                                       __METHOD__ . ' failed due to I/O error for {key}.',
-                                       [ 'key' => $key ]
-                               );
-
-                               return false; // IO error; don't spam retries
-                       }
-
-               } while ( !$success && --$attempts );
-
-               return $success;
-       }
-
-       /**
-        * Check and set an item
-        *
-        * @param mixed $casToken
-        * @param string $key
-        * @param mixed $value
-        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
-        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
-        * @return bool Success
-        */
-       protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
-               if ( !$this->lock( $key, 0 ) ) {
-                       return false; // non-blocking
-               }
-
-               $curCasToken = null; // passed by reference
-               $this->doGet( $key, self::READ_LATEST, $curCasToken );
-               if ( $casToken === $curCasToken ) {
-                       $success = $this->set( $key, $value, $exptime, $flags );
-               } else {
-                       $this->logger->info(
-                               __METHOD__ . ' failed due to race condition for {key}.',
-                               [ 'key' => $key ]
-                       );
-
-                       $success = false; // mismatched or failed
-               }
-
-               $this->unlock( $key );
-
-               return $success;
-       }
+       abstract public function merge(
+               $key,
+               callable $callback,
+               $exptime = 0,
+               $attempts = 10,
+               $flags = 0
+       );
 
        /**
         * Change the expiration on a key if it exists
@@ -504,39 +232,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @return bool Success Returns false on failure or if the item does not exist
         * @since 1.28
         */
-       public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
-               return $this->doChangeTTL( $key, $exptime, $flags );
-       }
-
-       /**
-        * @param string $key
-        * @param int $exptime
-        * @param int $flags
-        * @return bool
-        */
-       protected function doChangeTTL( $key, $exptime, $flags ) {
-               $expiry = $this->convertToExpiry( $exptime );
-               $delete = ( $expiry != 0 && $expiry < $this->getCurrentTime() );
-
-               if ( !$this->lock( $key, 0 ) ) {
-                       return false;
-               }
-               // Use doGet() to avoid having to trigger resolveSegments()
-               $blob = $this->doGet( $key, self::READ_LATEST );
-               if ( $blob ) {
-                       if ( $delete ) {
-                               $ok = $this->doDelete( $key, $flags );
-                       } else {
-                               $ok = $this->doSet( $key, $blob, $exptime, $flags );
-                       }
-               } else {
-                       $ok = false;
-               }
-
-               $this->unlock( $key );
-
-               return $ok;
-       }
+       abstract public function changeTTL( $key, $exptime = 0, $flags = 0 );
 
        /**
         * Acquire an advisory lock on a key string
@@ -549,51 +245,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param string $rclass Allow reentry if set and the current lock used this value
         * @return bool Success
         */
-       public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
-               // Avoid deadlocks and allow lock reentry if specified
-               if ( isset( $this->locks[$key] ) ) {
-                       if ( $rclass != '' && $this->locks[$key]['class'] === $rclass ) {
-                               ++$this->locks[$key]['depth'];
-                               return true;
-                       } else {
-                               return false;
-                       }
-               }
-
-               $fname = __METHOD__;
-               $expiry = min( $expiry ?: INF, self::TTL_DAY );
-               $loop = new WaitConditionLoop(
-                       function () use ( $key, $expiry, $fname ) {
-                               $this->clearLastError();
-                               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
-                                       return WaitConditionLoop::CONDITION_REACHED; // locked!
-                               } elseif ( $this->getLastError() ) {
-                                       $this->logger->warning(
-                                               $fname . ' failed due to I/O error for {key}.',
-                                               [ 'key' => $key ]
-                                       );
-
-                                       return WaitConditionLoop::CONDITION_ABORTED; // network partition?
-                               }
-
-                               return WaitConditionLoop::CONDITION_CONTINUE;
-                       },
-                       $timeout
-               );
-
-               $code = $loop->invoke();
-               $locked = ( $code === $loop::CONDITION_REACHED );
-               if ( $locked ) {
-                       $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
-               } elseif ( $code === $loop::CONDITION_TIMED_OUT ) {
-                       $this->logger->warning(
-                               "$fname failed due to timeout for {key}.",
-                               [ 'key' => $key, 'timeout' => $timeout ]
-                       );
-               }
-
-               return $locked;
-       }
+       abstract public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' );
 
        /**
         * Release an advisory lock on a key string
@@ -601,27 +253,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param string $key
         * @return bool Success
         */
-       public function unlock( $key ) {
-               if ( !isset( $this->locks[$key] ) ) {
-                       return false;
-               }
-
-               if ( --$this->locks[$key]['depth'] <= 0 ) {
-                       unset( $this->locks[$key] );
-
-                       $ok = $this->doDelete( "{$key}:lock" );
-                       if ( !$ok ) {
-                               $this->logger->warning(
-                                       __METHOD__ . ' failed to release lock for {key}.',
-                                       [ 'key' => $key ]
-                               );
-                       }
-
-                       return $ok;
-               }
-
-               return true;
-       }
+       abstract public function unlock( $key );
 
        /**
         * Get a lightweight exclusive self-unlocking lock
@@ -672,37 +304,11 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         *
         * @return bool Success; false if unimplemented
         */
-       public function deleteObjectsExpiringBefore(
+       abstract public function deleteObjectsExpiringBefore(
                $timestamp,
                callable $progress = null,
                $limit = INF
-       ) {
-               return false;
-       }
-
-       /**
-        * Get an associative array containing the item for each of the keys that have items.
-        * @param string[] $keys List of keys; can be a map of (unused => key) for convenience
-        * @param int $flags Bitfield; supports READ_LATEST [optional]
-        * @return mixed[] Map of (key => value) for existing keys; preserves the order of $keys
-        */
-       public function getMulti( array $keys, $flags = 0 ) {
-               $foundByKey = $this->doGetMulti( $keys, $flags );
-
-               $res = [];
-               foreach ( $keys as $key ) {
-                       // Resolve one blob at a time (avoids too much I/O at once)
-                       if ( array_key_exists( $key, $foundByKey ) ) {
-                               // A value should not appear in the key if a segment is missing
-                               $value = $this->resolveSegments( $key, $foundByKey[$key] );
-                               if ( $value !== false ) {
-                                       $res[$key] = $value;
-                               }
-                       }
-               }
-
-               return $res;
-       }
+       );
 
        /**
         * Get an associative array containing the item for each of the keys that have items.
@@ -710,79 +316,36 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param int $flags Bitfield; supports READ_LATEST [optional]
         * @return mixed[] Map of (key => value) for existing keys
         */
-       protected function doGetMulti( array $keys, $flags = 0 ) {
-               $res = [];
-               foreach ( $keys as $key ) {
-                       $val = $this->doGet( $key, $flags );
-                       if ( $val !== false ) {
-                               $res[$key] = $val;
-                       }
-               }
-
-               return $res;
-       }
+       abstract public function getMulti( array $keys, $flags = 0 );
 
        /**
         * Batch insertion/replace
         *
         * This does not support WRITE_ALLOW_SEGMENTS to avoid excessive read I/O
         *
+        * WRITE_BACKGROUND can be used for bulk insertion where the response is not vital
+        *
         * @param mixed[] $data Map of (key => value)
         * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
         * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
         * @return bool Success
         * @since 1.24
         */
-       public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
-               if ( ( $flags & self::WRITE_ALLOW_SEGMENTS ) === self::WRITE_ALLOW_SEGMENTS ) {
-                       throw new InvalidArgumentException( __METHOD__ . ' got WRITE_ALLOW_SEGMENTS' );
-               }
-               return $this->doSetMulti( $data, $exptime, $flags );
-       }
-
-       /**
-        * @param mixed[] $data Map of (key => value)
-        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
-        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
-        * @return bool Success
-        */
-       protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
-               $res = true;
-               foreach ( $data as $key => $value ) {
-                       $res = $this->doSet( $key, $value, $exptime, $flags ) && $res;
-               }
-               return $res;
-       }
+       abstract public function setMulti( array $data, $exptime = 0, $flags = 0 );
 
        /**
         * Batch deletion
         *
         * This does not support WRITE_ALLOW_SEGMENTS to avoid excessive read I/O
         *
+        * WRITE_BACKGROUND can be used for bulk deletion where the response is not vital
+        *
         * @param string[] $keys List of keys
         * @param int $flags Bitfield of BagOStuff::WRITE_* constants
         * @return bool Success
         * @since 1.33
         */
-       public function deleteMulti( array $keys, $flags = 0 ) {
-               if ( ( $flags & self::WRITE_ALLOW_SEGMENTS ) === self::WRITE_ALLOW_SEGMENTS ) {
-                       throw new InvalidArgumentException( __METHOD__ . ' got WRITE_ALLOW_SEGMENTS' );
-               }
-               return $this->doDeleteMulti( $keys, $flags );
-       }
-
-       /**
-        * @param string[] $keys List of keys
-        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
-        * @return bool Success
-        */
-       protected function doDeleteMulti( array $keys, $flags = 0 ) {
-               $res = true;
-               foreach ( $keys as $key ) {
-                       $res = $this->doDelete( $key, $flags ) && $res;
-               }
-               return $res;
-       }
+       abstract public function deleteMulti( array $keys, $flags = 0 );
 
        /**
         * Change the expiration of multiple keys that exist
@@ -795,14 +358,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @return bool Success
         * @since 1.34
         */
-       public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
-               $res = true;
-               foreach ( $keys as $key ) {
-                       $res = $this->doChangeTTL( $key, $exptime, $flags ) && $res;
-               }
-
-               return $res;
-       }
+       abstract public function changeTTLMulti( array $keys, $exptime, $flags = 0 );
 
        /**
         * Increase stored value of $key by $value while preserving its TTL
@@ -818,9 +374,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param int $value Value to subtract from $key (default: 1) [optional]
         * @return int|bool New value or false on failure
         */
-       public function decr( $key, $value = 1 ) {
-               return $this->incr( $key, - $value );
-       }
+       abstract public function decr( $key, $value = 1 );
 
        /**
         * Increase stored value of $key by $value while preserving its TTL
@@ -834,83 +388,20 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @return int|bool New value or false on failure
         * @since 1.24
         */
-       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
-               $this->clearLastError();
-               $newValue = $this->incr( $key, $value );
-               if ( $newValue === false && !$this->getLastError() ) {
-                       // No key set; initialize
-                       $newValue = $this->add( $key, (int)$init, $ttl ) ? $init : false;
-                       if ( $newValue === false && !$this->getLastError() ) {
-                               // Raced out initializing; increment
-                               $newValue = $this->incr( $key, $value );
-                       }
-               }
-
-               return $newValue;
-       }
-
-       /**
-        * Get and reassemble the chunks of blob at the given key
-        *
-        * @param string $key
-        * @param mixed $mainValue
-        * @return string|null|bool The combined string, false if missing, null on error
-        */
-       final protected function resolveSegments( $key, $mainValue ) {
-               if ( SerializedValueContainer::isUnified( $mainValue ) ) {
-                       return $this->unserialize( $mainValue->{SerializedValueContainer::UNIFIED_DATA} );
-               }
-
-               if ( SerializedValueContainer::isSegmented( $mainValue ) ) {
-                       $orderedKeys = array_map(
-                               function ( $segmentHash ) use ( $key ) {
-                                       return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
-                               },
-                               $mainValue->{SerializedValueContainer::SEGMENTED_HASHES}
-                       );
-
-                       $segmentsByKey = $this->doGetMulti( $orderedKeys );
-
-                       $parts = [];
-                       foreach ( $orderedKeys as $segmentKey ) {
-                               if ( isset( $segmentsByKey[$segmentKey] ) ) {
-                                       $parts[] = $segmentsByKey[$segmentKey];
-                               } else {
-                                       return false; // missing segment
-                               }
-                       }
-
-                       return $this->unserialize( implode( '', $parts ) );
-               }
-
-               return $mainValue;
-       }
+       abstract public function incrWithInit( $key, $ttl, $value = 1, $init = 1 );
 
        /**
         * Get the "last error" registered; clearLastError() should be called manually
         * @return int ERR_* constant for the "last error" registry
         * @since 1.23
         */
-       public function getLastError() {
-               return $this->lastError;
-       }
+       abstract public function getLastError();
 
        /**
         * Clear the "last error" registry
         * @since 1.23
         */
-       public function clearLastError() {
-               $this->lastError = self::ERR_NONE;
-       }
-
-       /**
-        * Set the "last error" registry
-        * @param int $err ERR_* constant
-        * @since 1.23
-        */
-       protected function setLastError( $err ) {
-               $this->lastError = $err;
-       }
+       abstract public function clearLastError();
 
        /**
         * Let a callback be run to avoid wasting time on special blocking calls
@@ -932,75 +423,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param callable $workCallback
         * @since 1.28
         */
-       final public function addBusyCallback( callable $workCallback ) {
-               $this->busyCallbacks[] = $workCallback;
-       }
-
-       /**
-        * @param string $text
-        */
-       protected function debug( $text ) {
-               if ( $this->debugMode ) {
-                       $this->logger->debug( "{class} debug: $text", [ 'class' => static::class ] );
-               }
-       }
-
-       /**
-        * @param int $exptime
-        * @return bool
-        */
-       final protected function expiryIsRelative( $exptime ) {
-               return ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) );
-       }
-
-       /**
-        * Convert an optionally relative timestamp to an absolute time
-        *
-        * The input value will be cast to an integer and interpreted as follows:
-        *   - zero: no expiry; return zero (e.g. TTL_INDEFINITE)
-        *   - negative: relative TTL; return UNIX timestamp offset by this value
-        *   - positive (< 10 years): relative TTL; return UNIX timestamp offset by this value
-        *   - positive (>= 10 years): absolute UNIX timestamp; return this value
-        *
-        * @param int $exptime Absolute TTL or 0 for indefinite
-        * @return int
-        */
-       final protected function convertToExpiry( $exptime ) {
-               return $this->expiryIsRelative( $exptime )
-                       ? (int)$this->getCurrentTime() + $exptime
-                       : $exptime;
-       }
-
-       /**
-        * Convert an optionally absolute expiry time to a relative time. If an
-        * absolute time is specified which is in the past, use a short expiry time.
-        *
-        * @param int $exptime
-        * @return int
-        */
-       final protected function convertToRelative( $exptime ) {
-               return $this->expiryIsRelative( $exptime )
-                       ? (int)$exptime
-                       : max( $exptime - (int)$this->getCurrentTime(), 1 );
-       }
-
-       /**
-        * Check if a value is an integer
-        *
-        * @param mixed $value
-        * @return bool
-        */
-       final protected function isInteger( $value ) {
-               if ( is_int( $value ) ) {
-                       return true;
-               } elseif ( !is_string( $value ) ) {
-                       return false;
-               }
-
-               $integer = (int)$value;
-
-               return ( $value === (string)$integer );
-       }
+       abstract public function addBusyCallback( callable $workCallback );
 
        /**
         * Construct a cache key.
@@ -1010,13 +433,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param array $args
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         */
-       public function makeKeyInternal( $keyspace, $args ) {
-               $key = $keyspace;
-               foreach ( $args as $arg ) {
-                       $key .= ':' . str_replace( ':', '%3A', $arg );
-               }
-               return strtr( $key, ' ', '_' );
-       }
+       abstract public function makeKeyInternal( $keyspace, $args );
 
        /**
         * Make a global cache key.
@@ -1026,9 +443,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param string|null $component [optional] Key component (starting with a key collection name)
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         */
-       public function makeGlobalKey( $class, $component = null ) {
-               return $this->makeKeyInternal( 'global', func_get_args() );
-       }
+       abstract public function makeGlobalKey( $class, $component = null );
 
        /**
         * Make a cache key, scoped to this instance's keyspace.
@@ -1038,9 +453,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @param string|null $component [optional] Key component (starting with a key collection name)
         * @return string Colon-delimited list of $keyspace followed by escaped components of $args
         */
-       public function makeKey( $class, $component = null ) {
-               return $this->makeKeyInternal( $this->keyspace, func_get_args() );
-       }
+       abstract public function makeKey( $class, $component = null );
 
        /**
         * @param int $flag ATTR_* class constant
@@ -1056,7 +469,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @since 1.34
         */
        public function getSegmentationSize() {
-               return $this->segmentationSize;
+               return INF;
        }
 
        /**
@@ -1064,7 +477,7 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
         * @since 1.34
         */
        public function getSegmentedValueMaxSize() {
-               return $this->segmentedValueMaxSize;
+               return INF;
        }
 
        /**
@@ -1105,22 +518,4 @@ abstract class BagOStuff implements IExpiringStore, IStoreKeyEncoder, LoggerAwar
        public function setMockTime( &$time ) {
                $this->wallClockOverride =& $time;
        }
-
-       /**
-        * @param mixed $value
-        * @return string|int String/integer representation
-        * @note Special handling is usually needed for integers so incr()/decr() work
-        */
-       protected function serialize( $value ) {
-               return is_int( $value ) ? $value : serialize( $value );
-       }
-
-       /**
-        * @param string|int $value
-        * @return mixed Original value or false on error
-        * @note Special handling is usually needed for integers so incr()/decr() work
-        */
-       protected function unserialize( $value ) {
-               return $this->isInteger( $value ) ? (int)$value : unserialize( $value );
-       }
 }
index ea434e0..c97f56e 100644 (file)
@@ -44,8 +44,6 @@ class CachedBagOStuff extends BagOStuff {
         * @param array $params Parameters for HashBagOStuff
         */
        public function __construct( BagOStuff $backend, $params = [] ) {
-               unset( $params['reportDupes'] ); // useless here
-
                parent::__construct( $params );
 
                $this->backend = $backend;
@@ -53,17 +51,41 @@ class CachedBagOStuff extends BagOStuff {
                $this->attrMap = $backend->attrMap;
        }
 
-       protected function doGet( $key, $flags = 0, &$casToken = null ) {
-               $ret = $this->procCache->get( $key, $flags );
-               if ( $ret === false && !$this->procCache->hasKey( $key ) ) {
-                       $ret = $this->backend->get( $key, $flags );
-                       $this->set( $key, $ret, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
+       public function setDebug( $enabled ) {
+               parent::setDebug( $enabled );
+               $this->backend->setDebug( $enabled );
+       }
+
+       public function get( $key, $flags = 0 ) {
+               $value = $this->procCache->get( $key, $flags );
+               if ( $value === false && !$this->procCache->hasKey( $key ) ) {
+                       $value = $this->backend->get( $key, $flags );
+                       $this->set( $key, $value, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
+               }
+
+               return $value;
+       }
+
+       public function getMulti( array $keys, $flags = 0 ) {
+               $valuesByKeyCached = [];
+
+               $keysMissing = [];
+               foreach ( $keys as $key ) {
+                       $value = $this->procCache->get( $key, $flags );
+                       if ( $value === false && !$this->procCache->hasKey( $key ) ) {
+                               $keysMissing[] = $key;
+                       } else {
+                               $valuesByKeyCached[$key] = $value;
+                       }
                }
 
-               return $ret;
+               $valuesByKeyFetched = $this->backend->getMulti( $keys, $flags );
+               $this->setMulti( $valuesByKeyFetched, self::TTL_INDEFINITE, self::WRITE_CACHE_ONLY );
+
+               return $valuesByKeyCached + $valuesByKeyFetched;
        }
 
-       protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
                $this->procCache->set( $key, $value, $exptime, $flags );
                if ( ( $flags & self::WRITE_CACHE_ONLY ) != self::WRITE_CACHE_ONLY ) {
                        $this->backend->set( $key, $value, $exptime, $flags );
@@ -72,7 +94,7 @@ class CachedBagOStuff extends BagOStuff {
                return true;
        }
 
-       protected function doDelete( $key, $flags = 0 ) {
+       public function delete( $key, $flags = 0 ) {
                $this->procCache->delete( $key, $flags );
                if ( ( $flags & self::WRITE_CACHE_ONLY ) != self::WRITE_CACHE_ONLY ) {
                        $this->backend->delete( $key, $flags );
@@ -81,19 +103,6 @@ class CachedBagOStuff extends BagOStuff {
                return true;
        }
 
-       public function deleteObjectsExpiringBefore(
-               $timestamp,
-               callable $progress = null,
-               $limit = INF
-       ) {
-               $this->procCache->deleteObjectsExpiringBefore( $timestamp, $progress, $limit );
-
-               return $this->backend->deleteObjectsExpiringBefore( $timestamp, $progress, $limit );
-       }
-
-       // These just call the backend (tested elsewhere)
-       // @codeCoverageIgnoreStart
-
        public function add( $key, $value, $exptime = 0, $flags = 0 ) {
                if ( $this->get( $key ) === false ) {
                        return $this->set( $key, $value, $exptime, $flags );
@@ -102,12 +111,19 @@ class CachedBagOStuff extends BagOStuff {
                return false; // key already set
        }
 
-       public function incr( $key, $value = 1 ) {
-               $n = $this->backend->incr( $key, $value );
+       // These just call the backend (tested elsewhere)
+       // @codeCoverageIgnoreStart
 
+       public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
                $this->procCache->delete( $key );
 
-               return $n;
+               return $this->backend->merge( $key, $callback, $exptime, $attempts, $flags );
+       }
+
+       public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
+               $this->procCache->delete( $key );
+
+               return $this->backend->changeTTL( $key, $exptime, $flags );
        }
 
        public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
@@ -118,6 +134,16 @@ class CachedBagOStuff extends BagOStuff {
                return $this->backend->unlock( $key );
        }
 
+       public function deleteObjectsExpiringBefore(
+               $timestamp,
+               callable $progress = null,
+               $limit = INF
+       ) {
+               $this->procCache->deleteObjectsExpiringBefore( $timestamp, $progress, $limit );
+
+               return $this->backend->deleteObjectsExpiringBefore( $timestamp, $progress, $limit );
+       }
+
        public function makeKeyInternal( $keyspace, $args ) {
                return $this->backend->makeKeyInternal( ...func_get_args() );
        }
@@ -130,11 +156,6 @@ class CachedBagOStuff extends BagOStuff {
                return $this->backend->makeGlobalKey( ...func_get_args() );
        }
 
-       public function setDebug( $bool ) {
-               parent::setDebug( $bool );
-               $this->backend->setDebug( $bool );
-       }
-
        public function getLastError() {
                return $this->backend->getLastError();
        }
@@ -143,5 +164,60 @@ class CachedBagOStuff extends BagOStuff {
                return $this->backend->clearLastError();
        }
 
+       public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
+               $this->procCache->setMulti( $data, $exptime, $flags );
+               if ( ( $flags & self::WRITE_CACHE_ONLY ) != self::WRITE_CACHE_ONLY ) {
+                       return $this->backend->setMulti( $data, $exptime, $flags );
+               }
+
+               return true;
+       }
+
+       public function deleteMulti( array $keys, $flags = 0 ) {
+               $this->procCache->deleteMulti( $keys, $flags );
+               if ( ( $flags & self::WRITE_CACHE_ONLY ) != self::WRITE_CACHE_ONLY ) {
+                       return $this->backend->deleteMulti( $keys, $flags );
+               }
+
+               return true;
+       }
+
+       public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
+               $this->procCache->changeTTLMulti( $keys, $exptime, $flags );
+               if ( ( $flags & self::WRITE_CACHE_ONLY ) != self::WRITE_CACHE_ONLY ) {
+                       return $this->backend->changeTTLMulti( $keys, $exptime, $flags );
+               }
+
+               return true;
+       }
+
+       public function incr( $key, $value = 1 ) {
+               $this->procCache->delete( $key );
+
+               return $this->backend->incr( $key, $value );
+       }
+
+       public function decr( $key, $value = 1 ) {
+               $this->procCache->delete( $key );
+
+               return $this->backend->decr( $key, $value );
+       }
+
+       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+               $this->procCache->delete( $key );
+
+               return $this->backend->incrWithInit( $key, $ttl, $value, $init );
+       }
+
+       public function addBusyCallback( callable $workCallback ) {
+               $this->backend->addBusyCallback( $workCallback );
+       }
+
+       public function setMockTime( &$time ) {
+               parent::setMockTime( $time );
+               $this->procCache->setMockTime( $time );
+               $this->backend->setMockTime( $time );
+       }
+
        // @codeCoverageIgnoreEnd
 }
index 6dc1363..dab8ba1 100644 (file)
@@ -26,7 +26,7 @@
  *
  * @ingroup Cache
  */
-class EmptyBagOStuff extends BagOStuff {
+class EmptyBagOStuff extends MediumSpecificBagOStuff {
        protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
index c74bb6e..83c8004 100644 (file)
@@ -28,7 +28,7 @@
  *
  * @ingroup Cache
  */
-class HashBagOStuff extends BagOStuff {
+class HashBagOStuff extends MediumSpecificBagOStuff {
        /** @var mixed[] */
        protected $bag = [];
        /** @var int Max entries allowed */
diff --git a/includes/libs/objectcache/MediumSpecificBagOStuff.php b/includes/libs/objectcache/MediumSpecificBagOStuff.php
new file mode 100644 (file)
index 0000000..fb088c8
--- /dev/null
@@ -0,0 +1,932 @@
+<?php
+/**
+ * Storage medium specific cache for storing items.
+ *
+ * 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 Wikimedia\WaitConditionLoop;
+
+/**
+ * Storage medium specific cache for storing items (e.g. redis, memcached, ...)
+ *
+ * This should not be used for proxy classes that simply wrap other cache instances
+ *
+ * @ingroup Cache
+ * @since 1.34
+ */
+abstract class MediumSpecificBagOStuff extends BagOStuff {
+       /** @var array[] Lock tracking */
+       protected $locks = [];
+       /** @var int ERR_* class constant */
+       protected $lastError = self::ERR_NONE;
+       /** @var string */
+       protected $keyspace = 'local';
+       /** @var int Seconds */
+       protected $syncTimeout;
+       /** @var int Bytes; chunk size of segmented cache values */
+       protected $segmentationSize;
+       /** @var int Bytes; maximum total size of a segmented cache value */
+       protected $segmentedValueMaxSize;
+
+       /** @var array */
+       private $duplicateKeyLookups = [];
+       /** @var bool */
+       private $reportDupes = false;
+       /** @var bool */
+       private $dupeTrackScheduled = false;
+
+       /** @var callable[] */
+       protected $busyCallbacks = [];
+
+       /** @var string Component to use for key construction of blob segment keys */
+       const SEGMENT_COMPONENT = 'segment';
+
+       /**
+        * @see BagOStuff::__construct()
+        * Additional $params options include:
+        *   - logger: Psr\Log\LoggerInterface instance
+        *   - keyspace: Default keyspace for $this->makeKey()
+        *   - reportDupes: Whether to emit warning log messages for all keys that were
+        *      requested more than once (requires an asyncHandler).
+        *   - syncTimeout: How long to wait with WRITE_SYNC in seconds.
+        *   - segmentationSize: The chunk size, in bytes, of segmented values. The value should
+        *      not exceed the maximum size of values in the storage backend, as configured by
+        *      the site administrator.
+        *   - segmentedValueMaxSize: The maximum total size, in bytes, of segmented values.
+        *      This should be configured to a reasonable size give the site traffic and the
+        *      amount of I/O between application and cache servers that the network can handle.
+        * @param array $params
+        */
+       public function __construct( array $params = [] ) {
+               parent::__construct( $params );
+
+               if ( isset( $params['keyspace'] ) ) {
+                       $this->keyspace = $params['keyspace'];
+               }
+
+               if ( !empty( $params['reportDupes'] ) && is_callable( $this->asyncHandler ) ) {
+                       $this->reportDupes = true;
+               }
+
+               $this->syncTimeout = $params['syncTimeout'] ?? 3;
+               $this->segmentationSize = $params['segmentationSize'] ?? 8388608; // 8MiB
+               $this->segmentedValueMaxSize = $params['segmentedValueMaxSize'] ?? 67108864; // 64MiB
+       }
+
+       /**
+        * Get an item with the given key
+        *
+        * If the key includes a deterministic input hash (e.g. the key can only have
+        * the correct value) or complete staleness checks are handled by the caller
+        * (e.g. nothing relies on the TTL), then the READ_VERIFIED flag should be set.
+        * This lets tiered backends know they can safely upgrade a cached value to
+        * higher tiers using standard TTLs.
+        *
+        * @param string $key
+        * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
+        * @return mixed Returns false on failure or if the item does not exist
+        */
+       public function get( $key, $flags = 0 ) {
+               $this->trackDuplicateKeys( $key );
+
+               return $this->resolveSegments( $key, $this->doGet( $key, $flags ) );
+       }
+
+       /**
+        * Track the number of times that a given key has been used.
+        * @param string $key
+        */
+       private function trackDuplicateKeys( $key ) {
+               if ( !$this->reportDupes ) {
+                       return;
+               }
+
+               if ( !isset( $this->duplicateKeyLookups[$key] ) ) {
+                       // Track that we have seen this key. This N-1 counting style allows
+                       // easy filtering with array_filter() later.
+                       $this->duplicateKeyLookups[$key] = 0;
+               } else {
+                       $this->duplicateKeyLookups[$key] += 1;
+
+                       if ( $this->dupeTrackScheduled === false ) {
+                               $this->dupeTrackScheduled = true;
+                               // Schedule a callback that logs keys processed more than once by get().
+                               call_user_func( $this->asyncHandler, function () {
+                                       $dups = array_filter( $this->duplicateKeyLookups );
+                                       foreach ( $dups as $key => $count ) {
+                                               $this->logger->warning(
+                                                       'Duplicate get(): "{key}" fetched {count} times',
+                                                       // Count is N-1 of the actual lookup count
+                                                       [ 'key' => $key, 'count' => $count + 1, ]
+                                               );
+                                       }
+                               } );
+                       }
+               }
+       }
+
+       /**
+        * @param string $key
+        * @param int $flags Bitfield of BagOStuff::READ_* constants [optional]
+        * @param mixed|null &$casToken Token to use for check-and-set comparisons
+        * @return mixed Returns false on failure or if the item does not exist
+        */
+       abstract protected function doGet( $key, $flags = 0, &$casToken = null );
+
+       /**
+        * Set an item
+        *
+        * @param string $key
+        * @param mixed $value
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        */
+       public function set( $key, $value, $exptime = 0, $flags = 0 ) {
+               if (
+                       is_int( $value ) || // avoid breaking incr()/decr()
+                       ( $flags & self::WRITE_ALLOW_SEGMENTS ) != self::WRITE_ALLOW_SEGMENTS ||
+                       is_infinite( $this->segmentationSize )
+               ) {
+                       return $this->doSet( $key, $value, $exptime, $flags );
+               }
+
+               $serialized = $this->serialize( $value );
+               $segmentSize = $this->getSegmentationSize();
+               $maxTotalSize = $this->getSegmentedValueMaxSize();
+
+               $size = strlen( $serialized );
+               if ( $size <= $segmentSize ) {
+                       // Since the work of serializing it was already done, just use it inline
+                       return $this->doSet(
+                               $key,
+                               SerializedValueContainer::newUnified( $serialized ),
+                               $exptime,
+                               $flags
+                       );
+               } elseif ( $size > $maxTotalSize ) {
+                       $this->setLastError( "Key $key exceeded $maxTotalSize bytes." );
+
+                       return false;
+               }
+
+               $chunksByKey = [];
+               $segmentHashes = [];
+               $count = intdiv( $size, $segmentSize ) + ( ( $size % $segmentSize ) ? 1 : 0 );
+               for ( $i = 0; $i < $count; ++$i ) {
+                       $segment = substr( $serialized, $i * $segmentSize, $segmentSize );
+                       $hash = sha1( $segment );
+                       $chunkKey = $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $hash );
+                       $chunksByKey[$chunkKey] = $segment;
+                       $segmentHashes[] = $hash;
+               }
+
+               $flags &= ~self::WRITE_ALLOW_SEGMENTS; // sanity
+               $ok = $this->setMulti( $chunksByKey, $exptime, $flags );
+               if ( $ok ) {
+                       // Only when all segments are stored should the main key be changed
+                       $ok = $this->doSet(
+                               $key,
+                               SerializedValueContainer::newSegmented( $segmentHashes ),
+                               $exptime,
+                               $flags
+                       );
+               }
+
+               return $ok;
+       }
+
+       /**
+        * Set an item
+        *
+        * @param string $key
+        * @param mixed $value
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        */
+       abstract protected function doSet( $key, $value, $exptime = 0, $flags = 0 );
+
+       /**
+        * Delete an item
+        *
+        * For large values written using WRITE_ALLOW_SEGMENTS, this only deletes the main
+        * segment list key unless WRITE_PRUNE_SEGMENTS is in the flags. While deleting the segment
+        * list key has the effect of functionally deleting the key, it leaves unused blobs in cache.
+        *
+        * @param string $key
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool True if the item was deleted or not found, false on failure
+        */
+       public function delete( $key, $flags = 0 ) {
+               if ( ( $flags & self::WRITE_PRUNE_SEGMENTS ) != self::WRITE_PRUNE_SEGMENTS ) {
+                       return $this->doDelete( $key, $flags );
+               }
+
+               $mainValue = $this->doGet( $key, self::READ_LATEST );
+               if ( !$this->doDelete( $key, $flags ) ) {
+                       return false;
+               }
+
+               if ( !SerializedValueContainer::isSegmented( $mainValue ) ) {
+                       return true; // no segments to delete
+               }
+
+               $orderedKeys = array_map(
+                       function ( $segmentHash ) use ( $key ) {
+                               return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
+                       },
+                       $mainValue->{SerializedValueContainer::SEGMENTED_HASHES}
+               );
+
+               return $this->deleteMulti( $orderedKeys, $flags );
+       }
+
+       /**
+        * Delete an item
+        *
+        * @param string $key
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool True if the item was deleted or not found, false on failure
+        */
+       abstract protected function doDelete( $key, $flags = 0 );
+
+       /**
+        * Merge changes into the existing cache value (possibly creating a new one)
+        *
+        * The callback function returns the new value given the current value
+        * (which will be false if not present), and takes the arguments:
+        * (this BagOStuff, cache key, current value, TTL).
+        * The TTL parameter is reference set to $exptime. It can be overriden in the callback.
+        * Nothing is stored nor deleted if the callback returns false.
+        *
+        * @param string $key
+        * @param callable $callback Callback method to be executed
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $attempts The amount of times to attempt a merge in case of failure
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        * @throws InvalidArgumentException
+        */
+       public function merge( $key, callable $callback, $exptime = 0, $attempts = 10, $flags = 0 ) {
+               return $this->mergeViaCas( $key, $callback, $exptime, $attempts, $flags );
+       }
+
+       /**
+        * @param string $key
+        * @param callable $callback Callback method to be executed
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $attempts The amount of times to attempt a merge in case of failure
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        * @see BagOStuff::merge()
+        *
+        */
+       final protected function mergeViaCas( $key, callable $callback, $exptime, $attempts, $flags ) {
+               do {
+                       $casToken = null; // passed by reference
+                       // Get the old value and CAS token from cache
+                       $this->clearLastError();
+                       $currentValue = $this->resolveSegments(
+                               $key,
+                               $this->doGet( $key, self::READ_LATEST, $casToken )
+                       );
+                       if ( $this->getLastError() ) {
+                               $this->logger->warning(
+                                       __METHOD__ . ' failed due to I/O error on get() for {key}.',
+                                       [ 'key' => $key ]
+                               );
+
+                               return false; // don't spam retries (retry only on races)
+                       }
+
+                       // Derive the new value from the old value
+                       $value = call_user_func( $callback, $this, $key, $currentValue, $exptime );
+                       $hadNoCurrentValue = ( $currentValue === false );
+                       unset( $currentValue ); // free RAM in case the value is large
+
+                       $this->clearLastError();
+                       if ( $value === false ) {
+                               $success = true; // do nothing
+                       } elseif ( $hadNoCurrentValue ) {
+                               // Try to create the key, failing if it gets created in the meantime
+                               $success = $this->add( $key, $value, $exptime, $flags );
+                       } else {
+                               // Try to update the key, failing if it gets changed in the meantime
+                               $success = $this->cas( $casToken, $key, $value, $exptime, $flags );
+                       }
+                       if ( $this->getLastError() ) {
+                               $this->logger->warning(
+                                       __METHOD__ . ' failed due to I/O error for {key}.',
+                                       [ 'key' => $key ]
+                               );
+
+                               return false; // IO error; don't spam retries
+                       }
+
+               } while ( !$success && --$attempts );
+
+               return $success;
+       }
+
+       /**
+        * Check and set an item
+        *
+        * @param mixed $casToken
+        * @param string $key
+        * @param mixed $value
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        */
+       protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
+               if ( !$this->lock( $key, 0 ) ) {
+                       return false; // non-blocking
+               }
+
+               $curCasToken = null; // passed by reference
+               $this->doGet( $key, self::READ_LATEST, $curCasToken );
+               if ( $casToken === $curCasToken ) {
+                       $success = $this->set( $key, $value, $exptime, $flags );
+               } else {
+                       $this->logger->info(
+                               __METHOD__ . ' failed due to race condition for {key}.',
+                               [ 'key' => $key ]
+                       );
+
+                       $success = false; // mismatched or failed
+               }
+
+               $this->unlock( $key );
+
+               return $success;
+       }
+
+       /**
+        * Change the expiration on a key if it exists
+        *
+        * If an expiry in the past is given then the key will immediately be expired
+        *
+        * For large values written using WRITE_ALLOW_SEGMENTS, this only changes the TTL of the
+        * main segment list key. While lowering the TTL of the segment list key has the effect of
+        * functionally lowering the TTL of the key, it might leave unused blobs in cache for longer.
+        * Raising the TTL of such keys is not effective, since the expiration of a single segment
+        * key effectively expires the entire value.
+        *
+        * @param string $key
+        * @param int $exptime TTL or UNIX timestamp
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
+        * @return bool Success Returns false on failure or if the item does not exist
+        * @since 1.28
+        */
+       public function changeTTL( $key, $exptime = 0, $flags = 0 ) {
+               return $this->doChangeTTL( $key, $exptime, $flags );
+       }
+
+       /**
+        * @param string $key
+        * @param int $exptime
+        * @param int $flags
+        * @return bool
+        */
+       protected function doChangeTTL( $key, $exptime, $flags ) {
+               $expiry = $this->convertToExpiry( $exptime );
+               $delete = ( $expiry != 0 && $expiry < $this->getCurrentTime() );
+
+               if ( !$this->lock( $key, 0 ) ) {
+                       return false;
+               }
+               // Use doGet() to avoid having to trigger resolveSegments()
+               $blob = $this->doGet( $key, self::READ_LATEST );
+               if ( $blob ) {
+                       if ( $delete ) {
+                               $ok = $this->doDelete( $key, $flags );
+                       } else {
+                               $ok = $this->doSet( $key, $blob, $exptime, $flags );
+                       }
+               } else {
+                       $ok = false;
+               }
+
+               $this->unlock( $key );
+
+               return $ok;
+       }
+
+       /**
+        * Acquire an advisory lock on a key string
+        *
+        * Note that if reentry is enabled, duplicate calls ignore $expiry
+        *
+        * @param string $key
+        * @param int $timeout Lock wait timeout; 0 for non-blocking [optional]
+        * @param int $expiry Lock expiry [optional]; 1 day maximum
+        * @param string $rclass Allow reentry if set and the current lock used this value
+        * @return bool Success
+        */
+       public function lock( $key, $timeout = 6, $expiry = 6, $rclass = '' ) {
+               // Avoid deadlocks and allow lock reentry if specified
+               if ( isset( $this->locks[$key] ) ) {
+                       if ( $rclass != '' && $this->locks[$key]['class'] === $rclass ) {
+                               ++$this->locks[$key]['depth'];
+                               return true;
+                       } else {
+                               return false;
+                       }
+               }
+
+               $fname = __METHOD__;
+               $expiry = min( $expiry ?: INF, self::TTL_DAY );
+               $loop = new WaitConditionLoop(
+                       function () use ( $key, $expiry, $fname ) {
+                               $this->clearLastError();
+                               if ( $this->add( "{$key}:lock", 1, $expiry ) ) {
+                                       return WaitConditionLoop::CONDITION_REACHED; // locked!
+                               } elseif ( $this->getLastError() ) {
+                                       $this->logger->warning(
+                                               $fname . ' failed due to I/O error for {key}.',
+                                               [ 'key' => $key ]
+                                       );
+
+                                       return WaitConditionLoop::CONDITION_ABORTED; // network partition?
+                               }
+
+                               return WaitConditionLoop::CONDITION_CONTINUE;
+                       },
+                       $timeout
+               );
+
+               $code = $loop->invoke();
+               $locked = ( $code === $loop::CONDITION_REACHED );
+               if ( $locked ) {
+                       $this->locks[$key] = [ 'class' => $rclass, 'depth' => 1 ];
+               } elseif ( $code === $loop::CONDITION_TIMED_OUT ) {
+                       $this->logger->warning(
+                               "$fname failed due to timeout for {key}.",
+                               [ 'key' => $key, 'timeout' => $timeout ]
+                       );
+               }
+
+               return $locked;
+       }
+
+       /**
+        * Release an advisory lock on a key string
+        *
+        * @param string $key
+        * @return bool Success
+        */
+       public function unlock( $key ) {
+               if ( !isset( $this->locks[$key] ) ) {
+                       return false;
+               }
+
+               if ( --$this->locks[$key]['depth'] <= 0 ) {
+                       unset( $this->locks[$key] );
+
+                       $ok = $this->doDelete( "{$key}:lock" );
+                       if ( !$ok ) {
+                               $this->logger->warning(
+                                       __METHOD__ . ' failed to release lock for {key}.',
+                                       [ 'key' => $key ]
+                               );
+                       }
+
+                       return $ok;
+               }
+
+               return true;
+       }
+
+       /**
+        * Delete all objects expiring before a certain date.
+        * @param string|int $timestamp The reference date in MW or TS_UNIX format
+        * @param callable|null $progress Optional, a function which will be called
+        *     regularly during long-running operations with the percentage progress
+        *     as the first parameter. [optional]
+        * @param int $limit Maximum number of keys to delete [default: INF]
+        *
+        * @return bool Success; false if unimplemented
+        */
+       public function deleteObjectsExpiringBefore(
+               $timestamp,
+               callable $progress = null,
+               $limit = INF
+       ) {
+               return false;
+       }
+
+       /**
+        * Get an associative array containing the item for each of the keys that have items.
+        * @param string[] $keys List of keys; can be a map of (unused => key) for convenience
+        * @param int $flags Bitfield; supports READ_LATEST [optional]
+        * @return mixed[] Map of (key => value) for existing keys; preserves the order of $keys
+        */
+       public function getMulti( array $keys, $flags = 0 ) {
+               $foundByKey = $this->doGetMulti( $keys, $flags );
+
+               $res = [];
+               foreach ( $keys as $key ) {
+                       // Resolve one blob at a time (avoids too much I/O at once)
+                       if ( array_key_exists( $key, $foundByKey ) ) {
+                               // A value should not appear in the key if a segment is missing
+                               $value = $this->resolveSegments( $key, $foundByKey[$key] );
+                               if ( $value !== false ) {
+                                       $res[$key] = $value;
+                               }
+                       }
+               }
+
+               return $res;
+       }
+
+       /**
+        * Get an associative array containing the item for each of the keys that have items.
+        * @param string[] $keys List of keys
+        * @param int $flags Bitfield; supports READ_LATEST [optional]
+        * @return array Map of (key => value) for existing keys
+        */
+       protected function doGetMulti( array $keys, $flags = 0 ) {
+               $res = [];
+               foreach ( $keys as $key ) {
+                       $val = $this->doGet( $key, $flags );
+                       if ( $val !== false ) {
+                               $res[$key] = $val;
+                       }
+               }
+
+               return $res;
+       }
+
+       /**
+        * Batch insertion/replace
+        *
+        * This does not support WRITE_ALLOW_SEGMENTS to avoid excessive read I/O
+        *
+        * @param mixed[] $data Map of (key => value)
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
+        * @return bool Success
+        * @since 1.24
+        */
+       public function setMulti( array $data, $exptime = 0, $flags = 0 ) {
+               if ( ( $flags & self::WRITE_ALLOW_SEGMENTS ) === self::WRITE_ALLOW_SEGMENTS ) {
+                       throw new InvalidArgumentException( __METHOD__ . ' got WRITE_ALLOW_SEGMENTS' );
+               }
+               return $this->doSetMulti( $data, $exptime, $flags );
+       }
+
+       /**
+        * @param mixed[] $data Map of (key => value)
+        * @param int $exptime Either an interval in seconds or a unix timestamp for expiry
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        */
+       protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
+               $res = true;
+               foreach ( $data as $key => $value ) {
+                       $res = $this->doSet( $key, $value, $exptime, $flags ) && $res;
+               }
+               return $res;
+       }
+
+       /**
+        * Batch deletion
+        *
+        * This does not support WRITE_ALLOW_SEGMENTS to avoid excessive read I/O
+        *
+        * @param string[] $keys List of keys
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        * @since 1.33
+        */
+       public function deleteMulti( array $keys, $flags = 0 ) {
+               if ( ( $flags & self::WRITE_ALLOW_SEGMENTS ) === self::WRITE_ALLOW_SEGMENTS ) {
+                       throw new InvalidArgumentException( __METHOD__ . ' got WRITE_ALLOW_SEGMENTS' );
+               }
+               return $this->doDeleteMulti( $keys, $flags );
+       }
+
+       /**
+        * @param string[] $keys List of keys
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants
+        * @return bool Success
+        */
+       protected function doDeleteMulti( array $keys, $flags = 0 ) {
+               $res = true;
+               foreach ( $keys as $key ) {
+                       $res = $this->doDelete( $key, $flags ) && $res;
+               }
+               return $res;
+       }
+
+       /**
+        * Change the expiration of multiple keys that exist
+        *
+        * @param string[] $keys List of keys
+        * @param int $exptime TTL or UNIX timestamp
+        * @param int $flags Bitfield of BagOStuff::WRITE_* constants (since 1.33)
+        * @return bool Success
+        * @see BagOStuff::changeTTL()
+        *
+        * @since 1.34
+        */
+       public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
+               $res = true;
+               foreach ( $keys as $key ) {
+                       $res = $this->doChangeTTL( $key, $exptime, $flags ) && $res;
+               }
+
+               return $res;
+       }
+
+       /**
+        * Decrease stored value of $key by $value while preserving its TTL
+        * @param string $key
+        * @param int $value Value to subtract from $key (default: 1) [optional]
+        * @return int|bool New value or false on failure
+        */
+       public function decr( $key, $value = 1 ) {
+               return $this->incr( $key, -$value );
+       }
+
+       /**
+        * Increase stored value of $key by $value while preserving its TTL
+        *
+        * This will create the key with value $init and TTL $ttl instead if not present
+        *
+        * @param string $key
+        * @param int $ttl
+        * @param int $value
+        * @param int $init
+        * @return int|bool New value or false on failure
+        * @since 1.24
+        */
+       public function incrWithInit( $key, $ttl, $value = 1, $init = 1 ) {
+               $this->clearLastError();
+               $newValue = $this->incr( $key, $value );
+               if ( $newValue === false && !$this->getLastError() ) {
+                       // No key set; initialize
+                       $newValue = $this->add( $key, (int)$init, $ttl ) ? $init : false;
+                       if ( $newValue === false && !$this->getLastError() ) {
+                               // Raced out initializing; increment
+                               $newValue = $this->incr( $key, $value );
+                       }
+               }
+
+               return $newValue;
+       }
+
+       /**
+        * Get and reassemble the chunks of blob at the given key
+        *
+        * @param string $key
+        * @param mixed $mainValue
+        * @return string|null|bool The combined string, false if missing, null on error
+        */
+       final protected function resolveSegments( $key, $mainValue ) {
+               if ( SerializedValueContainer::isUnified( $mainValue ) ) {
+                       return $this->unserialize( $mainValue->{SerializedValueContainer::UNIFIED_DATA} );
+               }
+
+               if ( SerializedValueContainer::isSegmented( $mainValue ) ) {
+                       $orderedKeys = array_map(
+                               function ( $segmentHash ) use ( $key ) {
+                                       return $this->makeGlobalKey( self::SEGMENT_COMPONENT, $key, $segmentHash );
+                               },
+                               $mainValue->{SerializedValueContainer::SEGMENTED_HASHES}
+                       );
+
+                       $segmentsByKey = $this->doGetMulti( $orderedKeys );
+
+                       $parts = [];
+                       foreach ( $orderedKeys as $segmentKey ) {
+                               if ( isset( $segmentsByKey[$segmentKey] ) ) {
+                                       $parts[] = $segmentsByKey[$segmentKey];
+                               } else {
+                                       return false; // missing segment
+                               }
+                       }
+
+                       return $this->unserialize( implode( '', $parts ) );
+               }
+
+               return $mainValue;
+       }
+
+       /**
+        * Get the "last error" registered; clearLastError() should be called manually
+        * @return int ERR_* constant for the "last error" registry
+        * @since 1.23
+        */
+       public function getLastError() {
+               return $this->lastError;
+       }
+
+       /**
+        * Clear the "last error" registry
+        * @since 1.23
+        */
+       public function clearLastError() {
+               $this->lastError = self::ERR_NONE;
+       }
+
+       /**
+        * Set the "last error" registry
+        * @param int $err ERR_* constant
+        * @since 1.23
+        */
+       protected function setLastError( $err ) {
+               $this->lastError = $err;
+       }
+
+       /**
+        * Let a callback be run to avoid wasting time on special blocking calls
+        *
+        * The callbacks may or may not be called ever, in any particular order.
+        * They are likely to be invoked when something WRITE_SYNC is used used.
+        * They should follow a caching pattern as shown below, so that any code
+        * using the work will get it's result no matter what happens.
+        * @code
+        *     $result = null;
+        *     $workCallback = function () use ( &$result ) {
+        *         if ( !$result ) {
+        *             $result = ....
+        *         }
+        *         return $result;
+        *     }
+        * @endcode
+        *
+        * @param callable $workCallback
+        * @since 1.28
+        */
+       final public function addBusyCallback( callable $workCallback ) {
+               $this->busyCallbacks[] = $workCallback;
+       }
+
+       /**
+        * @param int $exptime
+        * @return bool
+        */
+       final protected function expiryIsRelative( $exptime ) {
+               return ( $exptime != 0 && $exptime < ( 10 * self::TTL_YEAR ) );
+       }
+
+       /**
+        * Convert an optionally relative timestamp to an absolute time
+        *
+        * The input value will be cast to an integer and interpreted as follows:
+        *   - zero: no expiry; return zero (e.g. TTL_INDEFINITE)
+        *   - negative: relative TTL; return UNIX timestamp offset by this value
+        *   - positive (< 10 years): relative TTL; return UNIX timestamp offset by this value
+        *   - positive (>= 10 years): absolute UNIX timestamp; return this value
+        *
+        * @param int $exptime Absolute TTL or 0 for indefinite
+        * @return int
+        */
+       final protected function convertToExpiry( $exptime ) {
+               return $this->expiryIsRelative( $exptime )
+                       ? (int)$this->getCurrentTime() + $exptime
+                       : $exptime;
+       }
+
+       /**
+        * Convert an optionally absolute expiry time to a relative time. If an
+        * absolute time is specified which is in the past, use a short expiry time.
+        *
+        * @param int $exptime
+        * @return int
+        */
+       final protected function convertToRelative( $exptime ) {
+               return $this->expiryIsRelative( $exptime )
+                       ? (int)$exptime
+                       : max( $exptime - (int)$this->getCurrentTime(), 1 );
+       }
+
+       /**
+        * Check if a value is an integer
+        *
+        * @param mixed $value
+        * @return bool
+        */
+       final protected function isInteger( $value ) {
+               if ( is_int( $value ) ) {
+                       return true;
+               } elseif ( !is_string( $value ) ) {
+                       return false;
+               }
+
+               $integer = (int)$value;
+
+               return ( $value === (string)$integer );
+       }
+
+       /**
+        * Construct a cache key.
+        *
+        * @param string $keyspace
+        * @param array $args
+        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @since 1.27
+        */
+       public function makeKeyInternal( $keyspace, $args ) {
+               $key = $keyspace;
+               foreach ( $args as $arg ) {
+                       $key .= ':' . str_replace( ':', '%3A', $arg );
+               }
+               return strtr( $key, ' ', '_' );
+       }
+
+       /**
+        * Make a global cache key.
+        *
+        * @param string $class Key class
+        * @param string|null $component [optional] Key component (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @since 1.27
+        */
+       public function makeGlobalKey( $class, $component = null ) {
+               return $this->makeKeyInternal( 'global', func_get_args() );
+       }
+
+       /**
+        * Make a cache key, scoped to this instance's keyspace.
+        *
+        * @param string $class Key class
+        * @param string|null $component [optional] Key component (starting with a key collection name)
+        * @return string Colon-delimited list of $keyspace followed by escaped components of $args
+        * @since 1.27
+        */
+       public function makeKey( $class, $component = null ) {
+               return $this->makeKeyInternal( $this->keyspace, func_get_args() );
+       }
+
+       /**
+        * @param int $flag ATTR_* class constant
+        * @return int QOS_* class constant
+        * @since 1.28
+        */
+       public function getQoS( $flag ) {
+               return $this->attrMap[$flag] ?? self::QOS_UNKNOWN;
+       }
+
+       /**
+        * @return int|float The chunk size, in bytes, of segmented objects (INF for no limit)
+        * @since 1.34
+        */
+       public function getSegmentationSize() {
+               return $this->segmentationSize;
+       }
+
+       /**
+        * @return int|float Maximum total segmented object size in bytes (INF for no limit)
+        * @since 1.34
+        */
+       public function getSegmentedValueMaxSize() {
+               return $this->segmentedValueMaxSize;
+       }
+
+       /**
+        * @param mixed $value
+        * @return string|int String/integer representation
+        * @note Special handling is usually needed for integers so incr()/decr() work
+        */
+       protected function serialize( $value ) {
+               return is_int( $value ) ? $value : serialize( $value );
+       }
+
+       /**
+        * @param string|int $value
+        * @return mixed Original value or false on error
+        * @note Special handling is usually needed for integers so incr()/decr() work
+        */
+       protected function unserialize( $value ) {
+               return $this->isInteger( $value ) ? (int)$value : unserialize( $value );
+       }
+
+       /**
+        * @param string $text
+        */
+       protected function debug( $text ) {
+               if ( $this->debugMode ) {
+                       $this->logger->debug( "{class} debug: $text", [ 'class' => static::class ] );
+               }
+       }
+}
index f75e780..9f1c98a 100644 (file)
@@ -26,7 +26,7 @@
  *
  * @ingroup Cache
  */
-abstract class MemcachedBagOStuff extends BagOStuff {
+abstract class MemcachedBagOStuff extends MediumSpecificBagOStuff {
        function __construct( array $params ) {
                parent::__construct( $params );
 
index 221bc82..cc7ee2a 100644 (file)
  */
 class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
        /** @var Memcached */
-       protected $client;
+       protected $syncClient;
+       /** @var Memcached|null */
+       protected $asyncClient;
+
+       /** @var bool Whether the non-buffering client is locked from use */
+       protected $syncClientIsBuffering = false;
+       /** @var bool Whether the non-buffering client should be flushed before use */
+       protected $hasUnflushedChanges = false;
+
+       /** @var array Memcached options */
+       private static $OPTS_SYNC_WRITES = [
+               Memcached::OPT_NO_BLOCK => false, // async I/O (using TCP buffers)
+               Memcached::OPT_BUFFER_WRITES => false // libmemcached buffers
+       ];
+       /** @var array Memcached options */
+       private static $OPTS_ASYNC_WRITES = [
+               Memcached::OPT_NO_BLOCK => true, // async I/O (using TCP buffers)
+               Memcached::OPT_BUFFER_WRITES => true // libmemcached buffers
+       ];
 
        /**
         * Available parameters are:
@@ -63,15 +81,22 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                        // The Memcached object is essentially shared for each pool ID.
                        // We can only reuse a pool ID if we keep the config consistent.
                        $connectionPoolId = md5( serialize( $params ) );
-                       $client = new Memcached( $connectionPoolId );
-                       $this->initializeClient( $client, $params );
+                       $syncClient = new Memcached( "$connectionPoolId-sync" );
+                       // Avoid clobbering the main thread-shared Memcached instance
+                       $asyncClient = new Memcached( "$connectionPoolId-async" );
                } else {
-                       $client = new Memcached;
-                       $this->initializeClient( $client, $params );
+                       $syncClient = new Memcached();
+                       $asyncClient = null;
                }
 
-               $this->client = $client;
+               $this->initializeClient( $syncClient, $params, self::$OPTS_SYNC_WRITES );
+               if ( $asyncClient ) {
+                       $this->initializeClient( $asyncClient, $params, self::$OPTS_ASYNC_WRITES );
+               }
 
+               // Set the main client and any dedicated one for buffered writes
+               $this->syncClient = $syncClient;
+               $this->asyncClient = $asyncClient;
                // The compression threshold is an undocumented php.ini option for some
                // reason. There's probably not much harm in setting it globally, for
                // compatibility with the settings for the PHP client.
@@ -84,9 +109,10 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
         *
         * @param Memcached $client
         * @param array $params
+        * @param array $options Base options for Memcached::setOptions()
         * @throws RuntimeException
         */
-       private function initializeClient( Memcached $client, array $params ) {
+       private function initializeClient( Memcached $client, array $params, array $options ) {
                if ( $client->getServerList() ) {
                        $this->logger->debug( __METHOD__ . ": pre-initialized client instance." );
 
@@ -95,7 +121,9 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
                $this->logger->debug( __METHOD__ . ": initializing new client instance." );
 
-               $options = [
+               $options += [
+                       Memcached::OPT_NO_BLOCK => false,
+                       Memcached::OPT_BUFFER_WRITES => false,
                        // Network protocol (ASCII or binary)
                        Memcached::OPT_BINARY_PROTOCOL => $params['use_binary_protocol'],
                        // Set various network timeouts
@@ -150,10 +178,12 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
        protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $this->debug( "get($key)" );
+
+               $client = $this->acquireSyncClient();
                if ( defined( Memcached::class . '::GET_EXTENDED' ) ) { // v3.0.0
                        /** @noinspection PhpUndefinedClassConstantInspection */
                        $flags = Memcached::GET_EXTENDED;
-                       $res = $this->client->get( $this->validateKeyEncoding( $key ), null, $flags );
+                       $res = $client->get( $this->validateKeyEncoding( $key ), null, $flags );
                        if ( is_array( $res ) ) {
                                $result = $res['value'];
                                $casToken = $res['cas'];
@@ -162,62 +192,77 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                                $casToken = null;
                        }
                } else {
-                       $result = $this->client->get( $this->validateKeyEncoding( $key ), null, $casToken );
+                       $result = $client->get( $this->validateKeyEncoding( $key ), null, $casToken );
                }
-               $result = $this->checkResult( $key, $result );
-               return $result;
+
+               return $this->checkResult( $key, $result );
        }
 
        protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
                $this->debug( "set($key)" );
-               $result = $this->client->set(
+
+               $client = $this->acquireSyncClient();
+               $result = $client->set(
                        $this->validateKeyEncoding( $key ),
                        $value,
                        $this->fixExpiry( $exptime )
                );
-               if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTSTORED ) {
+
+               return ( $result === false && $client->getResultCode() === Memcached::RES_NOTSTORED )
                        // "Not stored" is always used as the mcrouter response with AllAsyncRoute
-                       return true;
-               }
-               return $this->checkResult( $key, $result );
+                       ? true
+                       : $this->checkResult( $key, $result );
        }
 
        protected function cas( $casToken, $key, $value, $exptime = 0, $flags = 0 ) {
                $this->debug( "cas($key)" );
-               $result = $this->client->cas( $casToken, $this->validateKeyEncoding( $key ),
-                       $value, $this->fixExpiry( $exptime ) );
+
+               $result = $this->acquireSyncClient()->cas(
+                       $casToken,
+                       $this->validateKeyEncoding( $key ),
+                       $value, $this->fixExpiry( $exptime )
+               );
+
                return $this->checkResult( $key, $result );
        }
 
        protected function doDelete( $key, $flags = 0 ) {
                $this->debug( "delete($key)" );
-               $result = $this->client->delete( $this->validateKeyEncoding( $key ) );
-               if ( $result === false && $this->client->getResultCode() === Memcached::RES_NOTFOUND ) {
+
+               $client = $this->acquireSyncClient();
+               $result = $client->delete( $this->validateKeyEncoding( $key ) );
+
+               return ( $result === false && $client->getResultCode() === Memcached::RES_NOTFOUND )
                        // "Not found" is counted as success in our interface
-                       return true;
-               }
-               return $this->checkResult( $key, $result );
+                       ? true
+                       : $this->checkResult( $key, $result );
        }
 
        public function add( $key, $value, $exptime = 0, $flags = 0 ) {
                $this->debug( "add($key)" );
-               $result = $this->client->add(
+
+               $result = $this->acquireSyncClient()->add(
                        $this->validateKeyEncoding( $key ),
                        $value,
                        $this->fixExpiry( $exptime )
                );
+
                return $this->checkResult( $key, $result );
        }
 
        public function incr( $key, $value = 1 ) {
                $this->debug( "incr($key)" );
-               $result = $this->client->increment( $key, $value );
+
+               $result = $this->acquireSyncClient()->increment( $key, $value );
+
                return $this->checkResult( $key, $result );
        }
 
        public function decr( $key, $value = 1 ) {
                $this->debug( "decr($key)" );
-               $result = $this->client->decrement( $key, $value );
+
+               $result = $this->acquireSyncClient()->decrement( $key, $value );
+
                return $this->checkResult( $key, $result );
        }
 
@@ -236,22 +281,25 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                if ( $result !== false ) {
                        return $result;
                }
-               switch ( $this->client->getResultCode() ) {
+
+               $client = $this->syncClient;
+               switch ( $client->getResultCode() ) {
                        case Memcached::RES_SUCCESS:
                                break;
                        case Memcached::RES_DATA_EXISTS:
                        case Memcached::RES_NOTSTORED:
                        case Memcached::RES_NOTFOUND:
-                               $this->debug( "result: " . $this->client->getResultMessage() );
+                               $this->debug( "result: " . $client->getResultMessage() );
                                break;
                        default:
-                               $msg = $this->client->getResultMessage();
+                               $msg = $client->getResultMessage();
                                $logCtx = [];
                                if ( $key !== false ) {
-                                       $server = $this->client->getServerByKey( $key );
+                                       $server = $client->getServerByKey( $key );
                                        $logCtx['memcached-server'] = "{$server['host']}:{$server['port']}";
                                        $logCtx['memcached-key'] = $key;
-                                       $msg = "Memcached error for key \"{memcached-key}\" on server \"{memcached-server}\": $msg";
+                                       $msg = "Memcached error for key \"{memcached-key}\" " .
+                                               "on server \"{memcached-server}\": $msg";
                                } else {
                                        $msg = "Memcached error: $msg";
                                }
@@ -263,41 +311,71 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
        protected function doGetMulti( array $keys, $flags = 0 ) {
                $this->debug( 'getMulti(' . implode( ', ', $keys ) . ')' );
+
                foreach ( $keys as $key ) {
                        $this->validateKeyEncoding( $key );
                }
-               $result = $this->client->getMulti( $keys ) ?: [];
+
+               // The PECL implementation uses "gets" which works as well as a pipeline
+               $result = $this->acquireSyncClient()->getMulti( $keys ) ?: [];
+
                return $this->checkResult( false, $result );
        }
 
        protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
                $this->debug( 'setMulti(' . implode( ', ', array_keys( $data ) ) . ')' );
+
+               $exptime = $this->fixExpiry( $exptime );
                foreach ( array_keys( $data ) as $key ) {
                        $this->validateKeyEncoding( $key );
                }
-               $result = $this->client->setMulti( $data, $this->fixExpiry( $exptime ) );
+
+               // The PECL implementation is a naïve for-loop so use async I/O to pipeline;
+               // https://github.com/php-memcached-dev/php-memcached/blob/master/php_memcached.c#L1852
+               if ( ( $flags & self::WRITE_BACKGROUND ) == self::WRITE_BACKGROUND ) {
+                       $client = $this->acquireAsyncClient();
+                       $result = $client->setMulti( $data, $exptime );
+                       $this->releaseAsyncClient( $client );
+               } else {
+                       $result = $this->acquireSyncClient()->setMulti( $data, $exptime );
+               }
+
                return $this->checkResult( false, $result );
        }
 
        protected function doDeleteMulti( array $keys, $flags = 0 ) {
                $this->debug( 'deleteMulti(' . implode( ', ', $keys ) . ')' );
+
                foreach ( $keys as $key ) {
                        $this->validateKeyEncoding( $key );
                }
-               $result = $this->client->deleteMulti( $keys ) ?: [];
-               $ok = true;
-               foreach ( $result as $code ) {
+
+               // The PECL implementation is a naïve for-loop so use async I/O to pipeline;
+               // https://github.com/php-memcached-dev/php-memcached/blob/7443d16d02fb73cdba2e90ae282446f80969229c/php_memcached.c#L1852
+               if ( ( $flags & self::WRITE_BACKGROUND ) == self::WRITE_BACKGROUND ) {
+                       $client = $this->acquireAsyncClient();
+                       $resultArray = $client->deleteMulti( $keys ) ?: [];
+                       $this->releaseAsyncClient( $client );
+               } else {
+                       $resultArray = $this->acquireSyncClient()->deleteMulti( $keys ) ?: [];
+               }
+
+               $result = true;
+               foreach ( $resultArray as $code ) {
                        if ( !in_array( $code, [ true, Memcached::RES_NOTFOUND ], true ) ) {
                                // "Not found" is counted as success in our interface
-                               $ok = false;
+                               $result = false;
                        }
                }
-               return $this->checkResult( false, $ok );
+
+               return $this->checkResult( false, $result );
        }
 
        protected function doChangeTTL( $key, $exptime, $flags ) {
                $this->debug( "touch($key)" );
-               $result = $this->client->touch( $key, $exptime );
+
+               $result = $this->acquireSyncClient()->touch( $key, $this->fixExpiry( $exptime ) );
+
                return $this->checkResult( $key, $result );
        }
 
@@ -306,7 +384,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                        return $value;
                }
 
-               $serializer = $this->client->getOption( Memcached::OPT_SERIALIZER );
+               $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
                if ( $serializer === Memcached::SERIALIZER_PHP ) {
                        return serialize( $value );
                } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
@@ -321,7 +399,7 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
                        return (int)$value;
                }
 
-               $serializer = $this->client->getOption( Memcached::OPT_SERIALIZER );
+               $serializer = $this->syncClient->getOption( Memcached::OPT_SERIALIZER );
                if ( $serializer === Memcached::SERIALIZER_PHP ) {
                        return unserialize( $value );
                } elseif ( $serializer === Memcached::SERIALIZER_IGBINARY ) {
@@ -330,4 +408,52 @@ class MemcachedPeclBagOStuff extends MemcachedBagOStuff {
 
                throw new UnexpectedValueException( __METHOD__ . ": got serializer '$serializer'." );
        }
+
+       /**
+        * @return Memcached
+        */
+       private function acquireSyncClient() {
+               if ( $this->syncClientIsBuffering ) {
+                       throw new RuntimeException( "The main (unbuffered I/O) client is locked" );
+               }
+
+               if ( $this->hasUnflushedChanges ) {
+                       // Force a synchronous flush of async writes so that their changes are visible
+                       $this->syncClient->fetch();
+                       if ( $this->asyncClient ) {
+                               $this->asyncClient->fetch();
+                       }
+                       $this->hasUnflushedChanges = false;
+               }
+
+               return $this->syncClient;
+       }
+
+       /**
+        * @return Memcached
+        */
+       private function acquireAsyncClient() {
+               if ( $this->asyncClient ) {
+                       return $this->asyncClient; // dedicated buffering instance
+               }
+
+               // Modify the main instance to temporarily buffer writes
+               $this->syncClientIsBuffering = true;
+               $this->syncClient->setOptions( self::$OPTS_ASYNC_WRITES );
+
+               return $this->syncClient;
+       }
+
+       /**
+        * @param Memcached $client
+        */
+       private function releaseAsyncClient( $client ) {
+               $this->hasUnflushedChanges = true;
+
+               if ( !$this->asyncClient ) {
+                       // This is the main instance; make it stop buffering writes again
+                       $client->setOptions( self::$OPTS_SYNC_WRITES );
+                       $this->syncClientIsBuffering = false;
+               }
+       }
 }
index f8b91bc..b1d5d29 100644 (file)
@@ -53,8 +53,9 @@ class MemcachedPhpBagOStuff extends MemcachedBagOStuff {
                $this->client->set_servers( $params['servers'] );
        }
 
-       public function setDebug( $debug ) {
-               $this->client->set_debug( $debug );
+       public function setDebug( $enabled ) {
+               parent::debug( $enabled );
+               $this->client->set_debug( $enabled );
        }
 
        protected function doGet( $key, $flags = 0, &$casToken = null ) {
index 8e791ba..6e9f387 100644 (file)
@@ -40,7 +40,8 @@ class MultiWriteBagOStuff extends BagOStuff {
        /** @var int[] List of all backing cache indexes */
        protected $cacheIndexes = [];
 
-       const UPGRADE_TTL = 3600; // TTL when a key is copied to a higher cache tier
+       /** @var int TTL when a key is copied to a higher cache tier */
+       private static $UPGRADE_TTL = 3600;
 
        /**
         * $params include:
@@ -97,9 +98,10 @@ class MultiWriteBagOStuff extends BagOStuff {
                $this->cacheIndexes = array_keys( $this->caches );
        }
 
-       public function setDebug( $debug ) {
+       public function setDebug( $enabled ) {
+               parent::setDebug( $enabled );
                foreach ( $this->caches as $cache ) {
-                       $cache->setDebug( $debug );
+                       $cache->setDebug( $enabled );
                }
        }
 
@@ -131,7 +133,7 @@ class MultiWriteBagOStuff extends BagOStuff {
                                $this->asyncWrites,
                                'set',
                                // @TODO: consider using self::WRITE_ALLOW_SEGMENTS here?
-                               [ $key, $value, self::UPGRADE_TTL ]
+                               [ $key, $value, self::$UPGRADE_TTL ]
                        );
                }
 
@@ -359,39 +361,15 @@ class MultiWriteBagOStuff extends BagOStuff {
                return $this->caches[0]->makeGlobalKey( ...func_get_args() );
        }
 
-       protected function doGet( $key, $flags = 0, &$casToken = null ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
+       public function addBusyCallback( callable $workCallback ) {
+               $this->caches[0]->addBusyCallback( $workCallback );
        }
 
-       protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doDelete( $key, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doChangeTTL( $key, $exptime, $flags ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doGetMulti( array $keys, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doSetMulti( array $keys, $exptime = 0, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doDeleteMulti( array $keys, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function serialize( $value ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function unserialize( $blob ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
+       public function setMockTime( &$time ) {
+               parent::setMockTime( $time );
+               foreach ( $this->caches as $cache ) {
+                       $cache->setMockTime( $time );
+                       $cache->setMockTime( $time );
+               }
        }
 }
index 2a12689..aa4a9b3 100644 (file)
@@ -44,7 +44,7 @@ use Psr\Log\LoggerInterface;
  * $wgSessionCacheType = 'sessions';
  * @endcode
  */
-class RESTBagOStuff extends BagOStuff {
+class RESTBagOStuff extends MediumSpecificBagOStuff {
        /**
         * Default connection timeout in seconds. The kernel retransmits the SYN
         * packet after 1 second, so 1.2 seconds allows for 1 retransmit without
index a72b3ff..87d26ef 100644 (file)
@@ -28,7 +28,7 @@
  * @ingroup Cache
  * @ingroup Redis
  */
-class RedisBagOStuff extends BagOStuff {
+class RedisBagOStuff extends MediumSpecificBagOStuff {
        /** @var RedisConnectionPool */
        protected $redisPool;
        /** @var array List of server names */
@@ -89,10 +89,12 @@ class RedisBagOStuff extends BagOStuff {
        protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
-               list( $server, $conn ) = $this->getConnection( $key );
+               $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
+
+               $e = null;
                try {
                        $value = $conn->get( $key );
                        $casToken = $value;
@@ -102,16 +104,20 @@ class RedisBagOStuff extends BagOStuff {
                        $this->handleException( $conn, $e );
                }
 
-               $this->logRequest( 'get', $key, $server, $result );
+               $this->logRequest( 'get', $key, $conn->getServer(), $e );
+
                return $result;
        }
 
        protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
-               list( $server, $conn ) = $this->getConnection( $key );
+               $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
+
                $ttl = $this->convertToRelative( $exptime );
+
+               $e = null;
                try {
                        if ( $ttl ) {
                                $result = $conn->setex( $key, $ttl, $this->serialize( $value ) );
@@ -123,52 +129,61 @@ class RedisBagOStuff extends BagOStuff {
                        $this->handleException( $conn, $e );
                }
 
-               $this->logRequest( 'set', $key, $server, $result );
+               $this->logRequest( 'set', $key, $conn->getServer(), $e );
+
                return $result;
        }
 
        protected function doDelete( $key, $flags = 0 ) {
-               list( $server, $conn ) = $this->getConnection( $key );
+               $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
+
+               $e = null;
                try {
-                       $conn->delete( $key );
-                       // Return true even if the key didn't exist
-                       $result = true;
+                       // Note that redis does not return false if the key was not there
+                       $result = ( $conn->delete( $key ) !== false );
                } catch ( RedisException $e ) {
                        $result = false;
                        $this->handleException( $conn, $e );
                }
 
-               $this->logRequest( 'delete', $key, $server, $result );
+               $this->logRequest( 'delete', $key, $conn->getServer(), $e );
+
                return $result;
        }
 
        protected function doGetMulti( array $keys, $flags = 0 ) {
-               $batches = [];
+               /** @var RedisConnRef[]|Redis[] $conns */
                $conns = [];
+               $batches = [];
                foreach ( $keys as $key ) {
-                       list( $server, $conn ) = $this->getConnection( $key );
-                       if ( !$conn ) {
-                               continue;
+                       $conn = $this->getConnection( $key );
+                       if ( $conn ) {
+                               $server = $conn->getServer();
+                               $conns[$server] = $conn;
+                               $batches[$server][] = $key;
                        }
-                       $conns[$server] = $conn;
-                       $batches[$server][] = $key;
                }
+
                $result = [];
                foreach ( $batches as $server => $batchKeys ) {
                        $conn = $conns[$server];
+
+                       $e = null;
                        try {
+                               // Avoid mget() to reduce CPU hogging from a single request
                                $conn->multi( Redis::PIPELINE );
                                foreach ( $batchKeys as $key ) {
                                        $conn->get( $key );
                                }
                                $batchResult = $conn->exec();
                                if ( $batchResult === false ) {
-                                       $this->debug( "multi request to $server failed" );
+                                       $this->logRequest( 'get', implode( ',', $batchKeys ), $server, true );
                                        continue;
                                }
+
                                foreach ( $batchResult as $i => $value ) {
                                        if ( $value !== false ) {
                                                $result[$batchKeys[$i]] = $this->unserialize( $value );
@@ -177,41 +192,47 @@ class RedisBagOStuff extends BagOStuff {
                        } catch ( RedisException $e ) {
                                $this->handleException( $conn, $e );
                        }
+
+                       $this->logRequest( 'get', implode( ',', $batchKeys ), $server, $e );
                }
 
-               $this->debug( "getMulti for " . count( $keys ) . " keys " .
-                       "returned " . count( $result ) . " results" );
                return $result;
        }
 
-       protected function doSetMulti( array $data, $expiry = 0, $flags = 0 ) {
-               $batches = [];
+       protected function doSetMulti( array $data, $exptime = 0, $flags = 0 ) {
+               /** @var RedisConnRef[]|Redis[] $conns */
                $conns = [];
+               $batches = [];
                foreach ( $data as $key => $value ) {
-                       list( $server, $conn ) = $this->getConnection( $key );
-                       if ( !$conn ) {
-                               continue;
+                       $conn = $this->getConnection( $key );
+                       if ( $conn ) {
+                               $server = $conn->getServer();
+                               $conns[$server] = $conn;
+                               $batches[$server][] = $key;
                        }
-                       $conns[$server] = $conn;
-                       $batches[$server][] = $key;
                }
 
-               $expiry = $this->convertToRelative( $expiry );
+               $ttl = $this->convertToRelative( $exptime );
+               $op = $ttl ? 'setex' : 'set';
+
                $result = true;
                foreach ( $batches as $server => $batchKeys ) {
                        $conn = $conns[$server];
+
+                       $e = null;
                        try {
+                               // Avoid mset() to reduce CPU hogging from a single request
                                $conn->multi( Redis::PIPELINE );
                                foreach ( $batchKeys as $key ) {
-                                       if ( $expiry ) {
-                                               $conn->setex( $key, $expiry, $this->serialize( $data[$key] ) );
+                                       if ( $ttl ) {
+                                               $conn->setex( $key, $ttl, $this->serialize( $data[$key] ) );
                                        } else {
                                                $conn->set( $key, $this->serialize( $data[$key] ) );
                                        }
                                }
                                $batchResult = $conn->exec();
                                if ( $batchResult === false ) {
-                                       $this->debug( "setMulti request to $server failed" );
+                                       $this->logRequest( $op, implode( ',', $batchKeys ), $server, true );
                                        continue;
                                }
                                $result = $result && !in_array( false, $batchResult, true );
@@ -219,48 +240,106 @@ class RedisBagOStuff extends BagOStuff {
                                $this->handleException( $conn, $e );
                                $result = false;
                        }
+
+                       $this->logRequest( $op, implode( ',', $batchKeys ), $server, $e );
                }
 
                return $result;
        }
 
        protected function doDeleteMulti( array $keys, $flags = 0 ) {
-               $batches = [];
+               /** @var RedisConnRef[]|Redis[] $conns */
                $conns = [];
+               $batches = [];
                foreach ( $keys as $key ) {
-                       list( $server, $conn ) = $this->getConnection( $key );
-                       if ( !$conn ) {
-                               continue;
+                       $conn = $this->getConnection( $key );
+                       if ( $conn ) {
+                               $server = $conn->getServer();
+                               $conns[$server] = $conn;
+                               $batches[$server][] = $key;
                        }
-                       $conns[$server] = $conn;
-                       $batches[$server][] = $key;
                }
 
                $result = true;
                foreach ( $batches as $server => $batchKeys ) {
                        $conn = $conns[$server];
+
+                       $e = null;
                        try {
+                               // Avoid delete() with array to reduce CPU hogging from a single request
                                $conn->multi( Redis::PIPELINE );
                                foreach ( $batchKeys as $key ) {
                                        $conn->delete( $key );
                                }
                                $batchResult = $conn->exec();
                                if ( $batchResult === false ) {
-                                       $this->debug( "deleteMulti request to $server failed" );
+                                       $this->logRequest( 'delete', implode( ',', $batchKeys ), $server, true );
                                        continue;
                                }
+                               // Note that redis does not return false if the key was not there
                                $result = $result && !in_array( false, $batchResult, true );
                        } catch ( RedisException $e ) {
                                $this->handleException( $conn, $e );
                                $result = false;
                        }
+
+                       $this->logRequest( 'delete', implode( ',', $batchKeys ), $server, $e );
+               }
+
+               return $result;
+       }
+
+       public function changeTTLMulti( array $keys, $exptime, $flags = 0 ) {
+               /** @var RedisConnRef[]|Redis[] $conns */
+               $conns = [];
+               $batches = [];
+               foreach ( $keys as $key ) {
+                       $conn = $this->getConnection( $key );
+                       if ( $conn ) {
+                               $server = $conn->getServer();
+                               $conns[$server] = $conn;
+                               $batches[$server][] = $key;
+                       }
+               }
+
+               $relative = $this->expiryIsRelative( $exptime );
+               $op = ( $exptime == 0 ) ? 'persist' : ( $relative ? 'expire' : 'expireAt' );
+
+               $result = true;
+               foreach ( $batches as $server => $batchKeys ) {
+                       $conn = $conns[$server];
+
+                       $e = null;
+                       try {
+                               $conn->multi( Redis::PIPELINE );
+                               foreach ( $batchKeys as $key ) {
+                                       if ( $exptime == 0 ) {
+                                               $conn->persist( $key );
+                                       } elseif ( $relative ) {
+                                               $conn->expire( $key, $this->convertToRelative( $exptime ) );
+                                       } else {
+                                               $conn->expireAt( $key, $this->convertToExpiry( $exptime ) );
+                                       }
+                               }
+                               $batchResult = $conn->exec();
+                               if ( $batchResult === false ) {
+                                       $this->logRequest( $op, implode( ',', $batchKeys ), $server, true );
+                                       continue;
+                               }
+                               $result = in_array( false, $batchResult, true ) ? false : $result;
+                       } catch ( RedisException $e ) {
+                               $this->handleException( $conn, $e );
+                               $result = false;
+                       }
+
+                       $this->logRequest( $op, implode( ',', $batchKeys ), $server, $e );
                }
 
                return $result;
        }
 
        public function add( $key, $value, $expiry = 0, $flags = 0 ) {
-               list( $server, $conn ) = $this->getConnection( $key );
+               $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
@@ -277,78 +356,35 @@ class RedisBagOStuff extends BagOStuff {
                        $this->handleException( $conn, $e );
                }
 
-               $this->logRequest( 'add', $key, $server, $result );
+               $this->logRequest( 'add', $key, $conn->getServer(), $result );
 
                return $result;
        }
 
        public function incr( $key, $value = 1 ) {
-               list( $server, $conn ) = $this->getConnection( $key );
-               if ( !$conn ) {
-                       return false;
-               }
-
-               try {
-                       $conn->watch( $key );
-                       if ( $conn->exists( $key ) ) {
-                               $conn->multi( Redis::MULTI );
-                               $conn->incrBy( $key, $value );
-                               $batchResult = $conn->exec();
-                               if ( $batchResult === false ) {
-                                       $result = false;
-                               } else {
-                                       $result = end( $batchResult );
-                               }
-                       } else {
-                               $result = false;
-                               $conn->unwatch();
-                       }
-               } catch ( RedisException $e ) {
-                       try {
-                               $conn->unwatch(); // sanity
-                       } catch ( RedisException $ex ) {
-                               // already errored
-                       }
-                       $result = false;
-                       $this->handleException( $conn, $e );
-               }
-
-               $this->logRequest( 'incr', $key, $server, $result );
-
-               return $result;
-       }
-
-       public function incrWithInit( $key, $exptime, $value = 1, $init = 1 ) {
-               list( $server, $conn ) = $this->getConnection( $key );
+               $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
 
-               $ttl = $this->convertToRelative( $exptime );
-               $preIncrInit = $init - $value;
                try {
-                       $conn->multi( Redis::MULTI );
-                       $conn->set( $key, $preIncrInit, $ttl ? [ 'nx', 'ex' => $ttl ] : [ 'nx' ] );
-                       $conn->incrBy( $key, $value );
-                       $batchResult = $conn->exec();
-                       if ( $batchResult === false ) {
-                               $result = false;
-                               $this->debug( "incrWithInit request to $server failed" );
-                       } else {
-                               $result = end( $batchResult );
+                       if ( !$conn->exists( $key ) ) {
+                               return false;
                        }
+                       // @FIXME: on races, the key may have a 0 TTL
+                       $result = $conn->incrBy( $key, $value );
                } catch ( RedisException $e ) {
                        $result = false;
                        $this->handleException( $conn, $e );
                }
 
-               $this->logRequest( 'incr', $key, $server, $result );
+               $this->logRequest( 'incr', $key, $conn->getServer(), $result );
 
                return $result;
        }
 
        protected function doChangeTTL( $key, $exptime, $flags ) {
-               list( $server, $conn ) = $this->getConnection( $key );
+               $conn = $this->getConnection( $key );
                if ( !$conn ) {
                        return false;
                }
@@ -357,13 +393,13 @@ class RedisBagOStuff extends BagOStuff {
                try {
                        if ( $exptime == 0 ) {
                                $result = $conn->persist( $key );
-                               $this->logRequest( 'persist', $key, $server, $result );
+                               $this->logRequest( 'persist', $key, $conn->getServer(), $result );
                        } elseif ( $relative ) {
                                $result = $conn->expire( $key, $this->convertToRelative( $exptime ) );
-                               $this->logRequest( 'expire', $key, $server, $result );
+                               $this->logRequest( 'expire', $key, $conn->getServer(), $result );
                        } else {
                                $result = $conn->expireAt( $key, $this->convertToExpiry( $exptime ) );
-                               $this->logRequest( 'expireAt', $key, $server, $result );
+                               $this->logRequest( 'expireAt', $key, $conn->getServer(), $result );
                        }
                } catch ( RedisException $e ) {
                        $result = false;
@@ -374,9 +410,8 @@ class RedisBagOStuff extends BagOStuff {
        }
 
        /**
-        * Get a Redis object with a connection suitable for fetching the specified key
         * @param string $key
-        * @return array (server, RedisConnRef) or (false, false)
+        * @return RedisConnRef|Redis|null Redis handle wrapper for the key or null on failure
         */
        protected function getConnection( $key ) {
                $candidates = array_keys( $this->serverTagMap );
@@ -402,7 +437,9 @@ class RedisBagOStuff extends BagOStuff {
                        // by now in such cases.
                        if ( $this->automaticFailover && $candidates ) {
                                try {
-                                       if ( $this->getMasterLinkStatus( $conn ) === 'down' ) {
+                                       /** @var string[] $info */
+                                       $info = $conn->info();
+                                       if ( ( $info['master_link_status'] ?? null ) === 'down' ) {
                                                // If the master cannot be reached, fail-over to the next server.
                                                // If masters are in data-center A, and replica DBs in data-center B,
                                                // this helps avoid the case were fail-over happens in A but not
@@ -411,28 +448,17 @@ class RedisBagOStuff extends BagOStuff {
                                        }
                                } catch ( RedisException $e ) {
                                        // Server is not accepting commands
-                                       $this->handleException( $conn, $e );
+                                       $this->redisPool->handleError( $conn, $e );
                                        continue;
                                }
                        }
 
-                       return [ $server, $conn ];
+                       return $conn;
                }
 
                $this->setLastError( BagOStuff::ERR_UNREACHABLE );
 
-               return [ false, false ];
-       }
-
-       /**
-        * Check the master link status of a Redis server that is configured as a replica DB.
-        * @param RedisConnRef $conn
-        * @return string|null Master link status (either 'up' or 'down'), or null
-        *  if the server is not a replica DB.
-        */
-       protected function getMasterLinkStatus( RedisConnRef $conn ) {
-               $info = $conn->info();
-               return $info['master_link_status'] ?? null;
+               return null;
        }
 
        /**
@@ -451,20 +477,19 @@ class RedisBagOStuff extends BagOStuff {
         * @param RedisConnRef $conn
         * @param RedisException $e
         */
-       protected function handleException( RedisConnRef $conn, $e ) {
+       protected function handleException( RedisConnRef $conn, RedisException $e ) {
                $this->setLastError( BagOStuff::ERR_UNEXPECTED );
                $this->redisPool->handleError( $conn, $e );
        }
 
        /**
         * Send information about a single request to the debug log
-        * @param string $method
-        * @param string $key
+        * @param string $op
+        * @param string $keys
         * @param string $server
-        * @param bool $result
+        * @param Exception|bool|null $e
         */
-       public function logRequest( $method, $key, $server, $result ) {
-               $this->debug( "$method $key on $server: " .
-                       ( $result === false ? "failure" : "success" ) );
+       public function logRequest( $op, $keys, $server, $e = null ) {
+               $this->debug( "$op($keys) on $server: " . ( $e ? "failure" : "success" ) );
        }
 }
index 295ec30..e49fa10 100644 (file)
@@ -69,9 +69,10 @@ class ReplicatedBagOStuff extends BagOStuff {
                $this->attrMap = $this->mergeFlagMaps( [ $this->readStore, $this->writeStore ] );
        }
 
-       public function setDebug( $debug ) {
-               $this->writeStore->setDebug( $debug );
-               $this->readStore->setDebug( $debug );
+       public function setDebug( $enabled ) {
+               parent::setDebug( $enabled );
+               $this->writeStore->setDebug( $enabled );
+               $this->readStore->setDebug( $enabled );
        }
 
        public function get( $key, $flags = 0 ) {
@@ -169,39 +170,13 @@ class ReplicatedBagOStuff extends BagOStuff {
                return $this->writeStore->makeGlobalKey( ...func_get_args() );
        }
 
-       protected function doGet( $key, $flags = 0, &$casToken = null ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
+       public function addBusyCallback( callable $workCallback ) {
+               $this->writeStore->addBusyCallback( $workCallback );
        }
 
-       protected function doSet( $key, $value, $exptime = 0, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doDelete( $key, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doChangeTTL( $key, $exptime, $flags ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doGetMulti( array $keys, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doSetMulti( array $keys, $exptime = 0, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function doDeleteMulti( array $keys, $flags = 0 ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function serialize( $value ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
-       }
-
-       protected function unserialize( $blob ) {
-               throw new LogicException( __METHOD__ . ': proxy class does not need this method.' );
+       public function setMockTime( &$time ) {
+               parent::setMockTime( $time );
+               $this->writeStore->setMockTime( $time );
+               $this->readStore->setMockTime( $time );
        }
 }
index 65059c8..2a8da89 100644 (file)
@@ -118,20 +118,21 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
        protected $cache;
        /** @var MapCacheLRU[] Map of group PHP instance caches */
        protected $processCaches = [];
+       /** @var LoggerInterface */
+       protected $logger;
+       /** @var StatsdDataFactoryInterface */
+       protected $stats;
+       /** @var callable|null Function that takes a WAN cache callback and runs it later */
+       protected $asyncHandler;
+
        /** @bar bool Whether to use mcrouter key prefixing for routing */
        protected $mcrouterAware;
        /** @var string Physical region for mcrouter use */
        protected $region;
        /** @var string Cache cluster name for mcrouter use */
        protected $cluster;
-       /** @var LoggerInterface */
-       protected $logger;
-       /** @var StatsdDataFactoryInterface */
-       protected $stats;
        /** @var bool Whether to use "interim" caching while keys are tombstoned */
        protected $useInterimHoldOffCaching = true;
-       /** @var callable|null Function that takes a WAN cache callback and runs it later */
-       protected $asyncHandler;
        /** @var float Unix timestamp of the oldest possible valid values */
        protected $epoch;
        /** @var string Stable secret used for hasing long strings into key components */
@@ -147,93 +148,104 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
        /** @var float|null */
        private $wallClockOverride;
 
-       /** Max time expected to pass between delete() and DB commit finishing */
+       /** @var int Max expected seconds to pass between delete() and DB commit finishing */
        const MAX_COMMIT_DELAY = 3;
-       /** Max replication+snapshot lag before applying TTL_LAGGED or disallowing set() */
+       /** @var int Max expected seconds of combined lag from replication and view snapshots */
        const MAX_READ_LAG = 7;
-       /** Seconds to tombstone keys on delete() */
-       const HOLDOFF_TTL = 11; // MAX_COMMIT_DELAY + MAX_READ_LAG + 1
-
-       /** Seconds to keep dependency purge keys around */
-       const CHECK_KEY_TTL = self::TTL_YEAR;
-       /** Seconds to keep interim value keys for tombstoned keys around */
-       const INTERIM_KEY_TTL = 1;
-
-       /** Seconds to keep lock keys around */
-       const LOCK_TTL = 10;
-       /** Seconds to no-op key set() calls to avoid large blob I/O stampedes */
-       const COOLOFF_TTL = 1;
-       /** Default remaining TTL at which to consider pre-emptive regeneration */
+       /** @var int Seconds to tombstone keys on delete() and treat as volatile after invalidation */
+       const HOLDOFF_TTL = self::MAX_COMMIT_DELAY + self::MAX_READ_LAG + 1;
+
+       /** @var int Idiom for getWithSetCallback() meaning "do not store the callback result" */
+       const TTL_UNCACHEABLE = -1;
+
+       /** @var int Consider regeneration if the key will expire within this many seconds */
        const LOW_TTL = 30;
-       /** Max TTL to store keys when a data sourced is lagged */
+       /** @var int Max TTL, in seconds, to store keys when a data sourced is lagged */
        const TTL_LAGGED = 30;
 
-       /** Never consider performing "popularity" refreshes until a key reaches this age */
-       const AGE_NEW = 60;
-       /** The time length of the "popularity" refresh window for hot keys */
+       /** @var int Expected time-till-refresh, in seconds, if the key is accessed once per second */
        const HOT_TTR = 900;
-       /** Hits/second for a refresh to be expected within the "popularity" window */
-       const HIT_RATE_HIGH = 1;
-       /** Seconds to ramp up to the "popularity" refresh chance after a key is no longer new */
-       const RAMPUP_TTL = 30;
+       /** @var int Minimum key age, in seconds, for expected time-till-refresh to be considered */
+       const AGE_NEW = 60;
 
-       /** Idiom for getWithSetCallback() meaning "do not store the callback result" */
-       const TTL_UNCACHEABLE = -1;
-       /** Idiom for getWithSetCallback() meaning "no regeneration mutex based on key hotness" */
+       /** @var int Idiom for getWithSetCallback() meaning "no cache stampede mutex required" */
        const TSE_NONE = -1;
-       /** Idiom for set()/getWithSetCallback() meaning "no post-expiration persistence" */
+
+       /** @var int Idiom for set()/getWithSetCallback() meaning "no post-expiration persistence" */
        const STALE_TTL_NONE = 0;
-       /** Idiom for set()/getWithSetCallback() meaning "no post-expiration grace period" */
+       /** @var int Idiom for set()/getWithSetCallback() meaning "no post-expiration grace period" */
        const GRACE_TTL_NONE = 0;
-       /** Idiom for delete()/touchCheckKey() meaning "no hold-off period for cache writes" */
-       const HOLDOFF_NONE = 0;
+       /** @var int Idiom for delete()/touchCheckKey() meaning "no hold-off period" */
+       const HOLDOFF_TTL_NONE = 0;
+       /** @var int Alias for HOLDOFF_TTL_NONE (b/c) (deprecated since 1.34) */
+       const HOLDOFF_NONE = self::HOLDOFF_TTL_NONE;
 
-       /** Idiom for getWithSetCallback() meaning "no minimum required as-of timestamp" */
+       /** @var float Idiom for getWithSetCallback() meaning "no minimum required as-of timestamp" */
        const MIN_TIMESTAMP_NONE = 0.0;
-       /** @var int One second into the UNIX timestamp epoch */
-       const EPOCH_UNIX_ONE_SECOND = 1.0;
-
-       /** Tiny negative float to use when CTL comes up >= 0 due to clock skew */
-       const TINY_NEGATIVE = -0.000001;
-       /** Tiny positive float to use when using "minTime" to assert an inequality */
-       const TINY_POSTIVE = 0.000001;
 
-       /** Milliseconds of delay after get() where set() storms are a consideration with "lockTSE" */
-       const SET_DELAY_HIGH_MS = 50;
-       /** Min millisecond set() backoff for keys in hold-off (far less than INTERIM_KEY_TTL) */
-       const RECENT_SET_LOW_MS = 50;
-       /** Max millisecond set() backoff for keys in hold-off (far less than INTERIM_KEY_TTL) */
-       const RECENT_SET_HIGH_MS = 100;
+       /** @var string Default process cache name and max key count */
+       const PC_PRIMARY = 'primary:1000';
 
-       /** @var int Seconds needed for value generation considered slow */
-       const GENERATION_SLOW_SEC = 3;
-
-       /** Parameter to get()/getMulti() to return extra information by reference */
+       /** @var int Idion for get()/getMulti() to return extra information by reference */
        const PASS_BY_REF = -1;
 
-       /** Cache format version number */
-       const VERSION = 1;
-
-       const FLD_FORMAT_VERSION = 0; // key to WAN cache version number
-       const FLD_VALUE = 1; // key to the cached value
-       const FLD_TTL = 2; // key to the original TTL
-       const FLD_TIME = 3; // key to the cache timestamp
-       const FLD_FLAGS = 4; // key to the flags bitfield (reserved number)
-       const FLD_VALUE_VERSION = 5; // key to collection cache version number
-       const FLD_GENERATION_TIME = 6; // key to how long it took to generate the value
-
-       const PURGE_TIME = 0; // key to the tombstone entry timestamp
-       const PURGE_HOLDOFF = 1; // key to the tombstone entry hold-off TTL
-
-       const VALUE_KEY_PREFIX = 'WANCache:v:';
-       const INTERIM_KEY_PREFIX = 'WANCache:i:';
-       const TIME_KEY_PREFIX = 'WANCache:t:';
-       const MUTEX_KEY_PREFIX = 'WANCache:m:';
-       const COOLOFF_KEY_PREFIX = 'WANCache:c:';
-
-       const PURGE_VAL_PREFIX = 'PURGED:';
-
-       const PC_PRIMARY = 'primary:1000'; // process cache name and max key count
+       /** @var int Seconds to keep dependency purge keys around */
+       private static $CHECK_KEY_TTL = self::TTL_YEAR;
+       /** @var int Seconds to keep interim value keys for tombstoned keys around */
+       private static $INTERIM_KEY_TTL = 1;
+
+       /** @var int Seconds to keep lock keys around */
+       private static $LOCK_TTL = 10;
+       /** @var int Seconds to no-op key set() calls to avoid large blob I/O stampedes */
+       private static $COOLOFF_TTL = 1;
+       /** @var int Seconds to ramp up the chance of regeneration due to expected time-till-refresh */
+       private static $RAMPUP_TTL = 30;
+
+       /** @var float Tiny negative float to use when CTL comes up >= 0 due to clock skew */
+       private static $TINY_NEGATIVE = -0.000001;
+       /** @var float Tiny positive float to use when using "minTime" to assert an inequality */
+       private static $TINY_POSTIVE = 0.000001;
+
+       /** @var int Milliseconds of key fetch/validate/regenerate delay prone to set() stampedes */
+       private static $SET_DELAY_HIGH_MS = 50;
+       /** @var int Min millisecond set() backoff during hold-off (far less than INTERIM_KEY_TTL) */
+       private static $RECENT_SET_LOW_MS = 50;
+       /** @var int Max millisecond set() backoff during hold-off (far less than INTERIM_KEY_TTL) */
+       private static $RECENT_SET_HIGH_MS = 100;
+
+       /** @var int Consider value generation slow if it takes more than this many seconds */
+       private static $GENERATION_SLOW_SEC = 3;
+
+       /** @var int Key to the tombstone entry timestamp */
+       private static $PURGE_TIME = 0;
+       /** @var int Key to the tombstone entry hold-off TTL */
+       private static $PURGE_HOLDOFF = 1;
+
+       /** @var int Cache format version number */
+       private static $VERSION = 1;
+
+       /** @var int Key to WAN cache version number */
+       private static $FLD_FORMAT_VERSION = 0;
+       /** @var int Key to the cached value */
+       private static $FLD_VALUE = 1;
+       /** @var int Key to the original TTL */
+       private static $FLD_TTL = 2;
+       /** @var int Key to the cache timestamp */
+       private static $FLD_TIME = 3;
+       /** @var int Key to the flags bit field (reserved number) */
+       private static /** @noinspection PhpUnusedPrivateFieldInspection */ $FLD_FLAGS = 4;
+       /** @var int Key to collection cache version number */
+       private static $FLD_VALUE_VERSION = 5;
+       /** @var int Key to how long it took to generate the value */
+       private static $FLD_GENERATION_TIME = 6;
+
+       private static $VALUE_KEY_PREFIX = 'WANCache:v:';
+       private static $INTERIM_KEY_PREFIX = 'WANCache:i:';
+       private static $TIME_KEY_PREFIX = 'WANCache:t:';
+       private static $MUTEX_KEY_PREFIX = 'WANCache:m:';
+       private static $COOLOFF_KEY_PREFIX = 'WANCache:c:';
+
+       private static $PURGE_VAL_PREFIX = 'PURGED:';
 
        /**
         * @param array $params
@@ -265,7 +277,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                $this->region = $params['region'] ?? 'main';
                $this->cluster = $params['cluster'] ?? 'wan-main';
                $this->mcrouterAware = !empty( $params['mcrouterAware'] );
-               $this->epoch = $params['epoch'] ?? self::EPOCH_UNIX_ONE_SECOND;
+               $this->epoch = $params['epoch'] ?? 0;
                $this->secret = $params['secret'] ?? (string)$this->epoch;
 
                $this->setLogger( $params['logger'] ?? new NullLogger() );
@@ -392,14 +404,14 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                $curTTLs = [];
                $infoByKey = [];
 
-               $vPrefixLen = strlen( self::VALUE_KEY_PREFIX );
-               $valueKeys = self::prefixCacheKeys( $keys, self::VALUE_KEY_PREFIX );
+               $vPrefixLen = strlen( self::$VALUE_KEY_PREFIX );
+               $valueKeys = self::prefixCacheKeys( $keys, self::$VALUE_KEY_PREFIX );
 
                $checkKeysForAll = [];
                $checkKeysByKey = [];
                $checkKeysFlat = [];
                foreach ( $checkKeys as $i => $checkKeyGroup ) {
-                       $prefixed = self::prefixCacheKeys( (array)$checkKeyGroup, self::TIME_KEY_PREFIX );
+                       $prefixed = self::prefixCacheKeys( (array)$checkKeyGroup, self::$TIME_KEY_PREFIX );
                        $checkKeysFlat = array_merge( $checkKeysFlat, $prefixed );
                        // Are these check keys for a specific cache key, or for all keys being fetched?
                        if ( is_int( $i ) ) {
@@ -445,11 +457,11 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
 
                        $lastCKPurge = null; // timestamp of the highest check key
                        foreach ( $purgeValues as $purge ) {
-                               $lastCKPurge = max( $purge[self::PURGE_TIME], $lastCKPurge );
-                               $safeTimestamp = $purge[self::PURGE_TIME] + $purge[self::PURGE_HOLDOFF];
+                               $lastCKPurge = max( $purge[self::$PURGE_TIME], $lastCKPurge );
+                               $safeTimestamp = $purge[self::$PURGE_TIME] + $purge[self::$PURGE_HOLDOFF];
                                if ( $value !== false && $safeTimestamp >= $keyInfo['asOf'] ) {
                                        // How long ago this value was invalidated by *this* check key
-                                       $ago = min( $purge[self::PURGE_TIME] - $now, self::TINY_NEGATIVE );
+                                       $ago = min( $purge[self::$PURGE_TIME] - $now, self::$TINY_NEGATIVE );
                                        // How long ago this value was invalidated by *any* known check key
                                        $keyInfo['curTTL'] = min( $keyInfo['curTTL'], $ago );
                                }
@@ -489,7 +501,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                        if ( $purge === false ) {
                                // Key is not set or malformed; regenerate
                                $newVal = $this->makePurgeValue( $now, self::HOLDOFF_TTL );
-                               $this->cache->add( $timeKey, $newVal, self::CHECK_KEY_TTL );
+                               $this->cache->add( $timeKey, $newVal, self::$CHECK_KEY_TTL );
                                $purge = $this->parsePurgeValue( $newVal );
                        }
                        $purgeValues[] = $purge;
@@ -649,10 +661,10 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                $storeTTL = $ttl + $staleTTL;
 
                if ( $creating ) {
-                       $ok = $this->cache->add( self::VALUE_KEY_PREFIX . $key, $wrapped, $storeTTL );
+                       $ok = $this->cache->add( self::$VALUE_KEY_PREFIX . $key, $wrapped, $storeTTL );
                } else {
                        $ok = $this->cache->merge(
-                               self::VALUE_KEY_PREFIX . $key,
+                               self::$VALUE_KEY_PREFIX . $key,
                                function ( $cache, $key, $cWrapped ) use ( $wrapped ) {
                                        // A string value means that it is a tombstone; do nothing in that case
                                        return ( is_string( $cWrapped ) ) ? false : $wrapped;
@@ -716,7 +728,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         *
         * The $ttl parameter can be used when purging values that have not actually changed
         * recently. For example, a cleanup script to purge cache entries does not really need
-        * a hold-off period, so it can use HOLDOFF_NONE. Likewise for user-requested purge.
+        * a hold-off period, so it can use HOLDOFF_TTL_NONE. Likewise for user-requested purge.
         * Note that $ttl limits the effective range of 'lockTSE' for getWithSetCallback().
         *
         * If called twice on the same key, then the last hold-off TTL takes precedence. For
@@ -729,10 +741,10 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
        final public function delete( $key, $ttl = self::HOLDOFF_TTL ) {
                if ( $ttl <= 0 ) {
                        // Publish the purge to all datacenters
-                       $ok = $this->relayDelete( self::VALUE_KEY_PREFIX . $key );
+                       $ok = $this->relayDelete( self::$VALUE_KEY_PREFIX . $key );
                } else {
                        // Publish the purge to all datacenters
-                       $ok = $this->relayPurge( self::VALUE_KEY_PREFIX . $key, $ttl, self::HOLDOFF_NONE );
+                       $ok = $this->relayPurge( self::$VALUE_KEY_PREFIX . $key, $ttl, self::HOLDOFF_TTL_NONE );
                }
 
                $kClass = $this->determineKeyClassForStats( $key );
@@ -828,7 +840,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
        final public function getMultiCheckKeyTime( array $keys ) {
                $rawKeys = [];
                foreach ( $keys as $key ) {
-                       $rawKeys[$key] = self::TIME_KEY_PREFIX . $key;
+                       $rawKeys[$key] = self::$TIME_KEY_PREFIX . $key;
                }
 
                $rawValues = $this->cache->getMulti( $rawKeys );
@@ -838,14 +850,14 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                foreach ( $rawKeys as $key => $rawKey ) {
                        $purge = $this->parsePurgeValue( $rawValues[$rawKey] );
                        if ( $purge !== false ) {
-                               $time = $purge[self::PURGE_TIME];
+                               $time = $purge[self::$PURGE_TIME];
                        } else {
                                // Casting assures identical floats for the next getCheckKeyTime() calls
                                $now = (string)$this->getCurrentTime();
                                $this->cache->add(
                                        $rawKey,
                                        $this->makePurgeValue( $now, self::HOLDOFF_TTL ),
-                                       self::CHECK_KEY_TTL
+                                       self::$CHECK_KEY_TTL
                                );
                                $time = (float)$now;
                        }
@@ -887,12 +899,12 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         * @see WANObjectCache::resetCheckKey()
         *
         * @param string $key Cache key
-        * @param int $holdoff HOLDOFF_TTL or HOLDOFF_NONE constant
+        * @param int $holdoff HOLDOFF_TTL or HOLDOFF_TTL_NONE constant
         * @return bool True if the item was purged or not found, false on failure
         */
        final public function touchCheckKey( $key, $holdoff = self::HOLDOFF_TTL ) {
                // Publish the purge to all datacenters
-               $ok = $this->relayPurge( self::TIME_KEY_PREFIX . $key, self::CHECK_KEY_TTL, $holdoff );
+               $ok = $this->relayPurge( self::$TIME_KEY_PREFIX . $key, self::$CHECK_KEY_TTL, $holdoff );
 
                $kClass = $this->determineKeyClassForStats( $key );
                $this->stats->increment( "wanobjectcache.$kClass.ck_touch." . ( $ok ? 'ok' : 'error' ) );
@@ -929,7 +941,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         */
        final public function resetCheckKey( $key ) {
                // Publish the purge to all datacenters
-               $ok = $this->relayDelete( self::TIME_KEY_PREFIX . $key );
+               $ok = $this->relayDelete( self::$TIME_KEY_PREFIX . $key );
 
                $kClass = $this->determineKeyClassForStats( $key );
                $this->stats->increment( "wanobjectcache.$kClass.ck_reset." . ( $ok ? 'ok' : 'error' ) );
@@ -1374,30 +1386,25 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                        // This avoids stampedes on eviction or preemptive regeneration taking too long.
                        ( $busyValue !== null && $possValue === false );
 
-               // If a regeneration lock is required, threads that do not get the lock will use any
-               // available stale or volatile value. If there is none, then the cheap/placeholder
-               // value from $busyValue will be used if provided; failing that, all threads will try
-               // to regenerate the value and ignore the lock.
-               if ( $useRegenerationLock ) {
-                       $hasLock = $this->cache->add( self::MUTEX_KEY_PREFIX . $key, 1, self::LOCK_TTL );
-                       if ( !$hasLock ) {
-                               if ( $this->isValid( $possValue, $possInfo['asOf'], $minAsOf ) ) {
-                                       $this->stats->increment( "wanobjectcache.$kClass.hit.stale" );
-
-                                       return [ $possValue, $possInfo['version'], $curInfo['asOf'] ];
-                               } elseif ( $busyValue !== null ) {
-                                       $miss = is_infinite( $minAsOf ) ? 'renew' : 'miss';
-                                       $this->stats->increment( "wanobjectcache.$kClass.$miss.busy" );
-
-                                       return [
-                                               is_callable( $busyValue ) ? $busyValue() : $busyValue,
-                                               $version,
-                                               $curInfo['asOf']
-                                       ];
-                               }
+               // If a regeneration lock is required, threads that do not get the lock will try to use
+               // the stale value, the interim value, or the $busyValue placeholder, in that order. If
+               // none of those are set then all threads will bypass the lock and regenerate the value.
+               $hasLock = $useRegenerationLock && $this->claimStampedeLock( $key );
+               if ( $useRegenerationLock && !$hasLock ) {
+                       if ( $this->isValid( $possValue, $possInfo['asOf'], $minAsOf ) ) {
+                               $this->stats->increment( "wanobjectcache.$kClass.hit.stale" );
+
+                               return [ $possValue, $possInfo['version'], $curInfo['asOf'] ];
+                       } elseif ( $busyValue !== null ) {
+                               $miss = is_infinite( $minAsOf ) ? 'renew' : 'miss';
+                               $this->stats->increment( "wanobjectcache.$kClass.$miss.busy" );
+
+                               return [
+                                       is_callable( $busyValue ) ? $busyValue() : $busyValue,
+                                       $version,
+                                       $curInfo['asOf']
+                               ];
                        }
-               } else {
-                       $hasLock = false;
                }
 
                // Generate the new value given any prior value with a matching version
@@ -1447,9 +1454,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                        }
                }
 
-               if ( $hasLock ) {
-                       $this->cache->changeTTL( self::MUTEX_KEY_PREFIX . $key, (int)$initialTime - 60 );
-               }
+               $this->yieldStampedeLock( $key, $hasLock );
 
                $miss = is_infinite( $minAsOf ) ? 'renew' : 'miss';
                $this->stats->increment( "wanobjectcache.$kClass.$miss.compute" );
@@ -1457,12 +1462,34 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                return [ $value, $version, $curInfo['asOf'] ];
        }
 
+       /**
+        * @param string $key
+        * @return bool Success
+        */
+       private function claimStampedeLock( $key ) {
+               // Note that locking is not bypassed due to I/O errors; this avoids stampedes
+               return $this->cache->add( self::$MUTEX_KEY_PREFIX . $key, 1, self::$LOCK_TTL );
+       }
+
+       /**
+        * @param string $key
+        * @param bool $hasLock
+        */
+       private function yieldStampedeLock( $key, $hasLock ) {
+               if ( $hasLock ) {
+                       // The backend might be a mcrouter proxy set to broadcast DELETE to *all* the local
+                       // datacenter cache servers via OperationSelectorRoute (for increased consistency).
+                       // Since that would be excessive for these locks, use TOUCH to expire the key.
+                       $this->cache->changeTTL( self::$MUTEX_KEY_PREFIX . $key, $this->getCurrentTime() - 60 );
+               }
+       }
+
        /**
         * @param float $age Age of volatile/interim key in seconds
         * @return bool Whether the age of a volatile value is negligible
         */
        private function isVolatileValueAgeNegligible( $age ) {
-               return ( $age < mt_rand( self::RECENT_SET_LOW_MS, self::RECENT_SET_HIGH_MS ) / 1e3 );
+               return ( $age < mt_rand( self::$RECENT_SET_LOW_MS, self::$RECENT_SET_HIGH_MS ) / 1e3 );
        }
 
        /**
@@ -1484,13 +1511,13 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                // consistent hashing).
                if ( $lockTSE < 0 || $hasLock ) {
                        return true; // either not a priori hot or thread has the lock
-               } elseif ( $elapsed <= self::SET_DELAY_HIGH_MS * 1e3 ) {
+               } elseif ( $elapsed <= self::$SET_DELAY_HIGH_MS * 1e3 ) {
                        return true; // not enough time for threads to pile up
                }
 
                $this->cache->clearLastError();
                if (
-                       !$this->cache->add( self::COOLOFF_KEY_PREFIX . $key, 1, self::COOLOFF_TTL ) &&
+                       !$this->cache->add( self::$COOLOFF_KEY_PREFIX . $key, 1, self::$COOLOFF_TTL ) &&
                        // Don't treat failures due to I/O errors as the key being in cooloff
                        $this->cache->getLastError() === BagOStuff::ERR_NONE
                ) {
@@ -1517,7 +1544,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
 
                $touched = $touchedCallback( $value );
                if ( $touched !== null && $touched >= $curInfo['asOf'] ) {
-                       $curTTL = min( $curTTL, self::TINY_NEGATIVE, $curInfo['asOf'] - $touched );
+                       $curTTL = min( $curTTL, self::$TINY_NEGATIVE, $curInfo['asOf'] - $touched );
                }
 
                return [ $curTTL, max( $curInfo['tombAsOf'], $curInfo['lastCKPurge'], $touched ) ];
@@ -1545,7 +1572,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                $now = $this->getCurrentTime();
 
                if ( $this->useInterimHoldOffCaching ) {
-                       $wrapped = $this->cache->get( self::INTERIM_KEY_PREFIX . $key );
+                       $wrapped = $this->cache->get( self::$INTERIM_KEY_PREFIX . $key );
 
                        list( $value, $keyInfo ) = $this->unwrap( $wrapped, $now );
                        if ( $this->isValid( $value, $keyInfo['asOf'], $minAsOf ) ) {
@@ -1564,11 +1591,11 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         * @param float $walltime How long it took to generate the value in seconds
         */
        private function setInterimValue( $key, $value, $ttl, $version, $walltime ) {
-               $ttl = max( self::INTERIM_KEY_TTL, (int)$ttl );
+               $ttl = max( self::$INTERIM_KEY_TTL, (int)$ttl );
 
                $wrapped = $this->wrap( $value, $ttl, $version, $this->getCurrentTime(), $walltime );
                $this->cache->merge(
-                       self::INTERIM_KEY_PREFIX . $key,
+                       self::$INTERIM_KEY_PREFIX . $key,
                        function () use ( $wrapped ) {
                                return $wrapped;
                        },
@@ -1810,12 +1837,12 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         */
        final public function reap( $key, $purgeTimestamp, &$isStale = false ) {
                $minAsOf = $purgeTimestamp + self::HOLDOFF_TTL;
-               $wrapped = $this->cache->get( self::VALUE_KEY_PREFIX . $key );
-               if ( is_array( $wrapped ) && $wrapped[self::FLD_TIME] < $minAsOf ) {
+               $wrapped = $this->cache->get( self::$VALUE_KEY_PREFIX . $key );
+               if ( is_array( $wrapped ) && $wrapped[self::$FLD_TIME] < $minAsOf ) {
                        $isStale = true;
                        $this->logger->warning( "Reaping stale value key '$key'." );
                        $ttlReap = self::HOLDOFF_TTL; // avoids races with tombstone creation
-                       $ok = $this->cache->changeTTL( self::VALUE_KEY_PREFIX . $key, $ttlReap );
+                       $ok = $this->cache->changeTTL( self::$VALUE_KEY_PREFIX . $key, $ttlReap );
                        if ( !$ok ) {
                                $this->logger->error( "Could not complete reap of key '$key'." );
                        }
@@ -1838,11 +1865,11 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         * @since 1.28
         */
        final public function reapCheckKey( $key, $purgeTimestamp, &$isStale = false ) {
-               $purge = $this->parsePurgeValue( $this->cache->get( self::TIME_KEY_PREFIX . $key ) );
-               if ( $purge && $purge[self::PURGE_TIME] < $purgeTimestamp ) {
+               $purge = $this->parsePurgeValue( $this->cache->get( self::$TIME_KEY_PREFIX . $key ) );
+               if ( $purge && $purge[self::$PURGE_TIME] < $purgeTimestamp ) {
                        $isStale = true;
                        $this->logger->warning( "Reaping stale check key '$key'." );
-                       $ok = $this->cache->changeTTL( self::TIME_KEY_PREFIX . $key, self::TTL_SECOND );
+                       $ok = $this->cache->changeTTL( self::$TIME_KEY_PREFIX . $key, self::TTL_SECOND );
                        if ( !$ok ) {
                                $this->logger->error( "Could not complete reap of check key '$key'." );
                        }
@@ -2175,14 +2202,14 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                        // Wildcards select all matching routes, e.g. the WAN cluster on all DCs
                        $ok = $this->cache->set(
                                "/*/{$this->cluster}/{$key}",
-                               $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
+                               $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_TTL_NONE ),
                                $ttl
                        );
                } else {
                        // This handles the mcrouter and the single-DC case
                        $ok = $this->cache->set(
                                $key,
-                               $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_NONE ),
+                               $this->makePurgeValue( $this->getCurrentTime(), self::HOLDOFF_TTL_NONE ),
                                $ttl
                        );
                }
@@ -2314,17 +2341,18 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                        return false;
                }
 
+               $popularHitsPerSec = 1;
                // Lifecycle is: new, ramp-up refresh chance, full refresh chance.
-               // Note that the "expected # of refreshes" for the ramp-up time range is half of what it
-               // would be if P(refresh) was at its full value during that time range.
-               $refreshWindowSec = max( $timeTillRefresh - $ageNew - self::RAMPUP_TTL / 2, 1 );
+               // Note that the "expected # of refreshes" for the ramp-up time range is half
+               // of what it would be if P(refresh) was at its full value during that time range.
+               $refreshWindowSec = max( $timeTillRefresh - $ageNew - self::$RAMPUP_TTL / 2, 1 );
                // P(refresh) * (# hits in $refreshWindowSec) = (expected # of refreshes)
-               // P(refresh) * ($refreshWindowSec * $popularHitsPerSec) = 1
+               // P(refresh) * ($refreshWindowSec * $popularHitsPerSec) = 1 (by definition)
                // P(refresh) = 1/($refreshWindowSec * $popularHitsPerSec)
-               $chance = 1 / ( self::HIT_RATE_HIGH * $refreshWindowSec );
+               $chance = 1 / ( $popularHitsPerSec * $refreshWindowSec );
 
                // Ramp up $chance from 0 to its nominal value over RAMPUP_TTL seconds to avoid stampedes
-               $chance *= ( $timeOld <= self::RAMPUP_TTL ) ? $timeOld / self::RAMPUP_TTL : 1;
+               $chance *= ( $timeOld <= self::$RAMPUP_TTL ) ? $timeOld / self::$RAMPUP_TTL : 1;
 
                return mt_rand( 1, 1e9 ) <= 1e9 * $chance;
        }
@@ -2340,7 +2368,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         */
        protected function isValid( $value, $asOf, $minAsOf, $purgeTime = null ) {
                // Avoid reading any key not generated after the latest delete() or touch
-               $safeMinAsOf = max( $minAsOf, $purgeTime + self::TINY_POSTIVE );
+               $safeMinAsOf = max( $minAsOf, $purgeTime + self::$TINY_POSTIVE );
 
                if ( $value === false ) {
                        return false;
@@ -2363,16 +2391,16 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                // Returns keys in ascending integer order for PHP7 array packing:
                // https://nikic.github.io/2014/12/22/PHPs-new-hashtable-implementation.html
                $wrapped = [
-                       self::FLD_FORMAT_VERSION => self::VERSION,
-                       self::FLD_VALUE => $value,
-                       self::FLD_TTL => $ttl,
-                       self::FLD_TIME => $now
+                       self::$FLD_FORMAT_VERSION => self::$VERSION,
+                       self::$FLD_VALUE => $value,
+                       self::$FLD_TTL => $ttl,
+                       self::$FLD_TIME => $now
                ];
                if ( $version !== null ) {
-                       $wrapped[self::FLD_VALUE_VERSION] = $version;
+                       $wrapped[self::$FLD_VALUE_VERSION] = $version;
                }
-               if ( $walltime >= self::GENERATION_SLOW_SEC ) {
-                       $wrapped[self::FLD_GENERATION_TIME] = $walltime;
+               if ( $walltime >= self::$GENERATION_SLOW_SEC ) {
+                       $wrapped[self::$FLD_GENERATION_TIME] = $walltime;
                }
 
                return $wrapped;
@@ -2395,20 +2423,20 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                if ( is_array( $wrapped ) ) {
                        // Entry expected to be a cached value; validate it
                        if (
-                               ( $wrapped[self::FLD_FORMAT_VERSION] ?? null ) === self::VERSION &&
-                               $wrapped[self::FLD_TIME] >= $this->epoch
+                               ( $wrapped[self::$FLD_FORMAT_VERSION] ?? null ) === self::$VERSION &&
+                               $wrapped[self::$FLD_TIME] >= $this->epoch
                        ) {
-                               if ( $wrapped[self::FLD_TTL] > 0 ) {
+                               if ( $wrapped[self::$FLD_TTL] > 0 ) {
                                        // Get the approximate time left on the key
-                                       $age = $now - $wrapped[self::FLD_TIME];
-                                       $curTTL = max( $wrapped[self::FLD_TTL] - $age, 0.0 );
+                                       $age = $now - $wrapped[self::$FLD_TIME];
+                                       $curTTL = max( $wrapped[self::$FLD_TTL] - $age, 0.0 );
                                } else {
                                        // Key had no TTL, so the time left is unbounded
                                        $curTTL = INF;
                                }
-                               $value = $wrapped[self::FLD_VALUE];
-                               $info['version'] = $wrapped[self::FLD_VALUE_VERSION] ?? null;
-                               $info['asOf'] = $wrapped[self::FLD_TIME];
+                               $value = $wrapped[self::$FLD_VALUE];
+                               $info['version'] = $wrapped[self::$FLD_VALUE_VERSION] ?? null;
+                               $info['asOf'] = $wrapped[self::$FLD_TIME];
                                $info['curTTL'] = $curTTL;
                        }
                } else {
@@ -2416,8 +2444,8 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                        $purge = $this->parsePurgeValue( $wrapped );
                        if ( $purge !== false ) {
                                // Tombstoned keys should always have a negative current $ttl
-                               $info['curTTL'] = min( $purge[self::PURGE_TIME] - $now, self::TINY_NEGATIVE );
-                               $info['tombAsOf'] = $purge[self::PURGE_TIME];
+                               $info['curTTL'] = min( $purge[self::$PURGE_TIME] - $now, self::$TINY_NEGATIVE );
+                               $info['tombAsOf'] = $purge[self::$PURGE_TIME];
                        }
                }
 
@@ -2459,8 +2487,10 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                }
 
                $segments = explode( ':', $value, 3 );
-               if ( !isset( $segments[0] ) || !isset( $segments[1] )
-                       || "{$segments[0]}:" !== self::PURGE_VAL_PREFIX
+               if (
+                       !isset( $segments[0] ) ||
+                       !isset( $segments[1] ) ||
+                       "{$segments[0]}:" !== self::$PURGE_VAL_PREFIX
                ) {
                        return false;
                }
@@ -2476,8 +2506,8 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                }
 
                return [
-                       self::PURGE_TIME => (float)$segments[1],
-                       self::PURGE_HOLDOFF => (int)$segments[2],
+                       self::$PURGE_TIME => (float)$segments[1],
+                       self::$PURGE_HOLDOFF => (int)$segments[2],
                ];
        }
 
@@ -2487,7 +2517,7 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
         * @return string Wrapped purge value
         */
        private function makePurgeValue( $timestamp, $holdoff ) {
-               return self::PURGE_VAL_PREFIX . (float)$timestamp . ':' . (int)$holdoff;
+               return self::$PURGE_VAL_PREFIX . (float)$timestamp . ':' . (int)$holdoff;
        }
 
        /**
@@ -2547,18 +2577,18 @@ class WANObjectCache implements IExpiringStore, IStoreKeyEncoder, LoggerAwareInt
                $keysWarmUp = [];
                // Get all the value keys to fetch...
                foreach ( $keys as $key ) {
-                       $keysWarmUp[] = self::VALUE_KEY_PREFIX . $key;
+                       $keysWarmUp[] = self::$VALUE_KEY_PREFIX . $key;
                }
                // Get all the check keys to fetch...
                foreach ( $checkKeys as $i => $checkKeyOrKeys ) {
                        if ( is_int( $i ) ) {
                                // Single check key that applies to all value keys
-                               $keysWarmUp[] = self::TIME_KEY_PREFIX . $checkKeyOrKeys;
+                               $keysWarmUp[] = self::$TIME_KEY_PREFIX . $checkKeyOrKeys;
                        } else {
                                // List of check keys that apply to value key $i
                                $keysWarmUp = array_merge(
                                        $keysWarmUp,
-                                       self::prefixCacheKeys( $checkKeyOrKeys, self::TIME_KEY_PREFIX )
+                                       self::prefixCacheKeys( $checkKeyOrKeys, self::$TIME_KEY_PREFIX )
                                );
                        }
                }
index d75b344..0e4e3fb 100644 (file)
  *
  * @ingroup Cache
  */
-class WinCacheBagOStuff extends BagOStuff {
+class WinCacheBagOStuff extends MediumSpecificBagOStuff {
        protected function doGet( $key, $flags = 0, &$casToken = null ) {
                $casToken = null;
 
                $blob = wincache_ucache_get( $key );
-               if ( !is_string( $blob ) ) {
+               if ( !is_string( $blob ) && !is_int( $blob ) ) {
                        return false;
                }
 
index b477855..eb645cc 100644 (file)
@@ -99,10 +99,6 @@ class RedisConnectionPool implements LoggerAwareInterface {
                $this->id = $id;
        }
 
-       /**
-        * @param LoggerInterface $logger
-        * @return null
-        */
        public function setLogger( LoggerInterface $logger ) {
                $this->logger = $logger;
        }
@@ -170,10 +166,13 @@ class RedisConnectionPool implements LoggerAwareInterface {
         * @param string $server A hostname/port combination or the absolute path of a UNIX socket.
         *                       If a hostname is specified but no port, port 6379 will be used.
         * @param LoggerInterface|null $logger PSR-3 logger intance. [optional]
-        * @return RedisConnRef|bool Returns false on failure
+        * @return RedisConnRef|Redis|bool Returns false on failure
         * @throws MWException
         */
        public function getConnection( $server, LoggerInterface $logger = null ) {
+               // The above @return also documents 'Redis' for convenience with IDEs.
+               // RedisConnRef uses PHP magic methods, which wouldn't be recognised.
+
                $logger = $logger ?: $this->logger;
                // Check the listing "dead" servers which have had a connection errors.
                // Servers are marked dead for a limited period of time, to
index 048b567..3bc19ff 100644 (file)
@@ -23,6 +23,8 @@
  * @since 1.22
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * This class formats delete log entries.
  *
@@ -280,10 +282,10 @@ class DeleteLogFormatter extends LogFormatter {
                        ];
 
                        static $fields = [
-                               Revision::DELETED_TEXT => 'content',
-                               Revision::DELETED_COMMENT => 'comment',
-                               Revision::DELETED_USER => 'user',
-                               Revision::DELETED_RESTRICTED => 'restricted',
+                               RevisionRecord::DELETED_TEXT => 'content',
+                               RevisionRecord::DELETED_COMMENT => 'comment',
+                               RevisionRecord::DELETED_USER => 'user',
+                               RevisionRecord::DELETED_RESTRICTED => 'restricted',
                        ];
 
                        if ( isset( $rawParams['6::ofield'] ) ) {
index 7e5a8a4..9226875 100644 (file)
@@ -36,7 +36,7 @@ use Wikimedia\WaitConditionLoop;
  *
  * @ingroup Cache
  */
-class SqlBagOStuff extends BagOStuff {
+class SqlBagOStuff extends MediumSpecificBagOStuff {
        /** @var array[] (server index => server config) */
        protected $serverInfos;
        /** @var string[] (server index => tag/host name) */
@@ -55,8 +55,6 @@ class SqlBagOStuff extends BagOStuff {
        protected $tableName = 'objectcache';
        /** @var bool */
        protected $replicaOnly = false;
-       /** @var int */
-       protected $syncTimeout = 3;
 
        /** @var LoadBalancer|null */
        protected $separateMainLB;
@@ -159,9 +157,6 @@ class SqlBagOStuff extends BagOStuff {
                if ( isset( $params['shards'] ) ) {
                        $this->shards = intval( $params['shards'] );
                }
-               if ( isset( $params['syncTimeout'] ) ) {
-                       $this->syncTimeout = $params['syncTimeout'];
-               }
                // Backwards-compatibility for < 1.34
                $this->replicaOnly = $params['replicaOnly'] ?? ( $params['slaveOnly'] ?? false );
        }
index 4e28085..f158e4d 100644 (file)
@@ -454,7 +454,9 @@ class Article implements Page {
                $this->mRevIdFetched = $this->mRevision->getId();
                $this->fetchResult = Status::newGood( $this->mRevision );
 
-               if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $this->getContext()->getUser() ) ) {
+               if (
+                       !$this->mRevision->userCan( RevisionRecord::DELETED_TEXT, $this->getContext()->getUser() )
+               ) {
                        wfDebug( __METHOD__ . " failed to retrieve content of revision " .
                                $this->mRevision->getId() . "\n" );
 
@@ -466,7 +468,7 @@ class Article implements Page {
 
                if ( Hooks::isRegistered( 'ArticleAfterFetchContentObject' ) ) {
                        $contentObject = $this->mRevision->getContent(
-                               Revision::FOR_THIS_USER,
+                               RevisionRecord::FOR_THIS_USER,
                                $this->getContext()->getUser()
                        );
 
@@ -489,7 +491,7 @@ class Article implements Page {
 
                // For B/C only
                $this->mContentObject = $this->mRevision->getContent(
-                       Revision::FOR_THIS_USER,
+                       RevisionRecord::FOR_THIS_USER,
                        $this->getContext()->getUser()
                );
 
@@ -1481,7 +1483,7 @@ class Article implements Page {
         * @return bool True if the view is allowed, false if not.
         */
        public function showDeletedRevisionHeader() {
-               if ( !$this->mRevision->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( !$this->mRevision->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        // Not deleted
                        return true;
                }
@@ -1489,7 +1491,7 @@ class Article implements Page {
                $outputPage = $this->getContext()->getOutput();
                $user = $this->getContext()->getUser();
                // If the user is not allowed to see it...
-               if ( !$this->mRevision->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( !$this->mRevision->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                        $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                'rev-deleted-text-permission' );
 
@@ -1499,7 +1501,7 @@ class Article implements Page {
                        # Give explanation and add a link to view the revision...
                        $oldid = intval( $this->getOldID() );
                        $link = $this->getTitle()->getFullURL( "oldid={$oldid}&unhide=1" );
-                       $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
+                       $msg = $this->mRevision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
                                'rev-suppressed-text-unhide' : 'rev-deleted-text-unhide';
                        $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
                                [ $msg, $link ] );
@@ -1507,7 +1509,7 @@ class Article implements Page {
                        return false;
                // We are allowed to see...
                } else {
-                       $msg = $this->mRevision->isDeleted( Revision::DELETED_RESTRICTED ) ?
+                       $msg = $this->mRevision->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
                                'rev-suppressed-text-view' : 'rev-deleted-text-view';
                        $outputPage->wrapWikiMsg( "<div class='mw-warning plainlinks'>\n$1\n</div>\n", $msg );
 
@@ -2412,7 +2414,7 @@ class Article implements Page {
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getComment
         */
-       public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                return $this->mPage->getComment( $audience, $user );
        }
 
@@ -2444,7 +2446,7 @@ class Article implements Page {
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getCreator
         */
-       public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                return $this->mPage->getCreator( $audience, $user );
        }
 
@@ -2556,7 +2558,7 @@ class Article implements Page {
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getUser
         */
-       public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                return $this->mPage->getUser( $audience, $user );
        }
 
@@ -2564,7 +2566,7 @@ class Article implements Page {
         * Call to WikiPage function for backwards compatibility.
         * @see WikiPage::getUserText
         */
-       public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                return $this->mPage->getUserText( $audience, $user );
        }
 
index fdba6fb..173fdc6 100644 (file)
@@ -813,7 +813,7 @@ class WikiPage implements Page, IDBAccessObject {
         *
         * @since 1.21
         */
-       public function getContent( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getContent( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                $this->loadLastEdit();
                if ( $this->mLastRevision ) {
                        return $this->mLastRevision->getContent( $audience, $user );
@@ -851,7 +851,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   to the $audience parameter
         * @return int User ID for the user that made the last article revision
         */
-       public function getUser( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getUser( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                $this->loadLastEdit();
                if ( $this->mLastRevision ) {
                        return $this->mLastRevision->getUser( $audience, $user );
@@ -870,7 +870,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   to the $audience parameter
         * @return User|null
         */
-       public function getCreator( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getCreator( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                $revision = $this->getOldestRevision();
                if ( $revision ) {
                        $userName = $revision->getUserText( $audience, $user );
@@ -889,7 +889,7 @@ class WikiPage implements Page, IDBAccessObject {
         *   to the $audience parameter
         * @return string Username of the user that made the last article revision
         */
-       public function getUserText( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getUserText( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                $this->loadLastEdit();
                if ( $this->mLastRevision ) {
                        return $this->mLastRevision->getUserText( $audience, $user );
@@ -908,7 +908,7 @@ class WikiPage implements Page, IDBAccessObject {
         * @return string|null Comment stored for the last article revision, or null if the specified
         *  audience does not have access to the comment.
         */
-       public function getComment( $audience = Revision::FOR_PUBLIC, User $user = null ) {
+       public function getComment( $audience = RevisionRecord::FOR_PUBLIC, User $user = null ) {
                $this->loadLastEdit();
                if ( $this->mLastRevision ) {
                        return $this->mLastRevision->getComment( $audience, $user );
@@ -1176,7 +1176,7 @@ class WikiPage implements Page, IDBAccessObject {
                $conds[] = 'NOT(' . $actorMigration->getWhere( $dbr, 'rev_user', $user )['conds'] . ')';
 
                // Username hidden?
-               $conds[] = "{$dbr->bitAnd( 'rev_deleted', Revision::DELETED_USER )} = 0";
+               $conds[] = "{$dbr->bitAnd( 'rev_deleted', RevisionRecord::DELETED_USER )} = 0";
 
                $jconds = [
                        'user' => [ 'LEFT JOIN', $actorQuery['fields']['rev_user'] . ' = user_id' ],
@@ -2695,7 +2695,7 @@ class WikiPage implements Page, IDBAccessObject {
                // we need to remember the old content so we can use it to generate all deletion updates.
                $revision = $this->getRevision();
                try {
-                       $content = $this->getContent( Revision::RAW );
+                       $content = $this->getContent( RevisionRecord::RAW );
                } catch ( Exception $ex ) {
                        wfLogWarning( __METHOD__ . ': failed to load content during deletion! '
                                . $ex->getMessage() );
@@ -2844,7 +2844,7 @@ class WikiPage implements Page, IDBAccessObject {
 
                // Bitfields to further suppress the content
                if ( $suppress ) {
-                       $bitfield = Revision::SUPPRESSED_ALL;
+                       $bitfield = RevisionRecord::SUPPRESSED_ALL;
                        $revQuery['fields'] = array_diff( $revQuery['fields'], [ 'rev_deleted' ] );
                }
 
@@ -3768,7 +3768,7 @@ class WikiPage implements Page, IDBAccessObject {
                        $slotContent = [ SlotRecord::MAIN => $rev ];
                } else {
                        $slotContent = array_map( function ( SlotRecord $slot ) {
-                               return $slot->getContent( Revision::RAW );
+                               return $slot->getContent( RevisionRecord::RAW );
                        }, $rev->getSlots()->getSlots() );
                }
 
index 7fece00..5aa1a69 100644 (file)
@@ -823,7 +823,7 @@ class CoreParserFunctions {
                }
 
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $title );
+               $rev = self::getCachedRevisionObject( $parser, $title, 'vary-revision-sha1' );
                $length = $rev ? $rev->getSize() : 0;
                if ( $length === null ) {
                        // We've had bugs where rev_len was not being recorded for empty pages, see T135414
@@ -1126,41 +1126,56 @@ class CoreParserFunctions {
         *
         * @param Parser $parser
         * @param Title $title
+        * @param string $vary ParserOuput vary-* flag
         * @return Revision
         * @since 1.23
         */
-       private static function getCachedRevisionObject( $parser, $title = null ) {
-               if ( is_null( $title ) ) {
+       private static function getCachedRevisionObject( $parser, $title, $vary ) {
+               if ( !$title ) {
                        return null;
                }
 
-               // Use the revision from the parser itself, when param is the current page
-               // and the revision is the current one
-               if ( $title->equals( $parser->getTitle() ) ) {
-                       $parserRev = $parser->getRevisionObject();
-                       if ( $parserRev && $parserRev->isCurrent() ) {
-                               // force reparse after edit with vary-revision flag
-                               $parser->getOutput()->setFlag( 'vary-revision' );
-                               wfDebug( __METHOD__ . ": use current revision from parser, setting vary-revision...\n" );
-                               return $parserRev;
+               $revision = null;
+
+               $isSelfReferential = $title->equals( $parser->getTitle() );
+               if ( $isSelfReferential ) {
+                       // Revision is for the same title that is currently being parsed. Only use the last
+                       // saved revision, regardless of Parser::getRevisionId() or fake revision injection
+                       // callbacks against the current title.
+                       $parserRevision = $parser->getRevisionObject();
+                       if ( $parserRevision && $parserRevision->isCurrent() ) {
+                               $revision = $parserRevision;
+                               wfDebug( __METHOD__ . ": used current revision, setting $vary" );
                        }
                }
 
-               // Normalize name for cache
-               $page = $title->getPrefixedDBkey();
-
-               if ( !( $parser->currentRevisionCache && $parser->currentRevisionCache->has( $page ) )
-                       && !$parser->incrementExpensiveFunctionCount() ) {
-                       return null;
+               $parserOutput = $parser->getOutput();
+               if ( !$revision ) {
+                       if (
+                               !$parser->isCurrentRevisionOfTitleCached( $title ) &&
+                               !$parser->incrementExpensiveFunctionCount()
+                       ) {
+                               return null; // not allowed
+                       }
+                       // Get the current revision, ignoring Parser::getRevisionId() being null/old
+                       $revision = $parser->fetchCurrentRevisionOfTitle( $title );
+                       // Register dependency in templatelinks
+                       $parserOutput->addTemplate(
+                               $title,
+                               $revision ? $revision->getPage() : 0,
+                               $revision ? $revision->getId() : 0
+                       );
                }
-               $rev = $parser->fetchCurrentRevisionOfTitle( $title );
-               $pageID = $rev ? $rev->getPage() : 0;
-               $revID = $rev ? $rev->getId() : 0;
 
-               // Register dependency in templatelinks
-               $parser->getOutput()->addTemplate( $title, $pageID, $revID );
+               if ( $isSelfReferential ) {
+                       // Upon page save, the result of the parser function using this might change
+                       $parserOutput->setFlag( $vary );
+                       if ( $vary === 'vary-revision-sha1' && $revision ) {
+                               $parserOutput->setRevisionUsedSha1Base36( $revision->getSha1() );
+                       }
+               }
 
-               return $rev;
+               return $revision;
        }
 
        /**
@@ -1221,7 +1236,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-id' );
                return $rev ? $rev->getId() : '';
        }
 
@@ -1238,7 +1253,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-timestamp' );
                return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'j' ) : '';
        }
 
@@ -1255,7 +1270,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-timestamp' );
                return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'd' ) : '';
        }
 
@@ -1272,7 +1287,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-timestamp' );
                return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'm' ) : '';
        }
 
@@ -1289,7 +1304,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-timestamp' );
                return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'n' ) : '';
        }
 
@@ -1306,7 +1321,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-timestamp' );
                return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'Y' ) : '';
        }
 
@@ -1323,7 +1338,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-revision-timestamp' );
                return $rev ? MWTimestamp::getLocalInstance( $rev->getTimestamp() )->format( 'YmdHis' ) : '';
        }
 
@@ -1340,7 +1355,7 @@ class CoreParserFunctions {
                        return '';
                }
                // fetch revision from cache/database and return the value
-               $rev = self::getCachedRevisionObject( $parser, $t );
+               $rev = self::getCachedRevisionObject( $parser, $t, 'vary-user' );
                return $rev ? $rev->getUserText() : '';
        }
 
index a2c5eec..0721446 100644 (file)
@@ -3693,6 +3693,18 @@ class Parser {
                return $this->currentRevisionCache->get( $cacheKey );
        }
 
+       /**
+        * @param Title $title
+        * @return bool
+        * @since 1.34
+        */
+       public function isCurrentRevisionOfTitleCached( $title ) {
+               return (
+                       $this->currentRevisionCache &&
+                       $this->currentRevisionCache->has( $title->getPrefixedText() )
+               );
+       }
+
        /**
         * Wrapper around Revision::newFromTitle to allow passing additional parameters
         * without passing them on to it.
@@ -3727,8 +3739,7 @@ class Parser {
                        foreach ( $stuff['deps'] as $dep ) {
                                $this->mOutput->addTemplate( $dep['title'], $dep['page_id'], $dep['rev_id'] );
                                if ( $dep['title']->equals( $this->getTitle() ) ) {
-                                       // If we transclude ourselves, the final result
-                                       // will change based on the new version of the page
+                                       // Self-transclusion; final result may change based on the new page version
                                        $this->mOutput->setFlag( 'vary-revision' );
                                        wfDebug( __METHOD__ . ": self transclusion, setting vary-revision" );
                                }
index c8113f3..23e5911 100644 (file)
@@ -216,6 +216,9 @@ class ParserOutput extends CacheTime {
        /** @var int|null Assumed rev timestamp for {{REVISIONTIMESTAMP}} if no revision is set */
        private $revisionTimestampUsed;
 
+       /** @var string|null SHA-1 base 36 hash of any self-transclusion */
+       private $revisionUsedSha1Base36;
+
        /** string CSS classes to use for the wrapping div, stored in the array keys.
         * If no class is given, no wrapper is added.
         */
@@ -464,6 +467,33 @@ class ParserOutput extends CacheTime {
                return $this->revisionTimestampUsed;
        }
 
+       /**
+        * @param string $hash Lowercase SHA-1 base 36 hash
+        * @since 1.34
+        */
+       public function setRevisionUsedSha1Base36( $hash ) {
+               if ( $hash === null ) {
+                       return; // e.g. RevisionRecord::getSha1() returned null
+               }
+
+               if (
+                       $this->revisionUsedSha1Base36 !== null &&
+                       $this->revisionUsedSha1Base36 !== $hash
+               ) {
+                       $this->revisionUsedSha1Base36 = ''; // mismatched
+               } else {
+                       $this->revisionUsedSha1Base36 = $hash;
+               }
+       }
+
+       /**
+        * @return string|null Lowercase SHA-1 base 36 hash, null if unused, or "" on inconsistency
+        * @since 1.34
+        */
+       public function getRevisionUsedSha1Base36() {
+               return $this->revisionUsedSha1Base36;
+       }
+
        public function &getLanguageLinks() {
                return $this->mLanguageLinks;
        }
index 457648a..fca06c9 100644 (file)
@@ -177,7 +177,7 @@ class MessageBlobStore implements LoggerAwareInterface {
                // - This global check key invalidates message blobs for all modules for all wikis
                //   in cache contexts (e.g. languages, skins). Setting a hold-off on this key could
                //   cause a cache stampede since no values would be stored for several seconds.
-               $cache->touchCheckKey( $cache->makeGlobalKey( __CLASS__ ), $cache::HOLDOFF_NONE );
+               $cache->touchCheckKey( $cache->makeGlobalKey( __CLASS__ ), $cache::HOLDOFF_TTL_NONE );
        }
 
        /**
index fbc59fe..af30313 100644 (file)
@@ -1159,11 +1159,12 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                                throw new MWException( __METHOD__ . ": invalid versionCallback for file" .
                                                        " \"{$fileInfo['name']}\" in module \"{$this->getName()}\"" );
                                        }
-                                       $expanded['definitionSummary'] = ( $fileInfo['versionCallback'] )( $context );
+                                       $expanded['definitionSummary'] =
+                                               ( $fileInfo['versionCallback'] )( $context, $this->getConfig() );
                                        // Don't invoke 'callback' here as it may be expensive (T223260).
                                        $expanded['callback'] = $fileInfo['callback'];
                                } else {
-                                       $expanded['content'] = ( $fileInfo['callback'] )( $context );
+                                       $expanded['content'] = ( $fileInfo['callback'] )( $context, $this->getConfig() );
                                }
                        } elseif ( isset( $fileInfo['config'] ) ) {
                                if ( $type !== 'data' ) {
@@ -1240,7 +1241,7 @@ class ResourceLoaderFileModule extends ResourceLoaderModule {
                                $fileInfo['content'] = $content;
                                unset( $fileInfo['filePath'] );
                        } elseif ( isset( $fileInfo['callback'] ) ) {
-                               $fileInfo['content'] = ( $fileInfo['callback'] )( $context );
+                               $fileInfo['content'] = ( $fileInfo['callback'] )( $context, $this->getConfig() );
                                unset( $fileInfo['callback'] );
                        }
 
index d37c31b..b104073 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Assert\Assert;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
@@ -220,7 +221,7 @@ class ResourceLoaderWikiModule extends ResourceLoaderModule {
                        if ( !$revision ) {
                                return null;
                        }
-                       $content = $revision->getContent( Revision::RAW );
+                       $content = $revision->getContent( RevisionRecord::RAW );
 
                        if ( !$content ) {
                                $this->getLogger()->error(
index 00e40a0..ab9830f 100644 (file)
@@ -19,6 +19,8 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Item class for a filearchive table row
  */
@@ -109,8 +111,8 @@ class RevDelArchivedFileItem extends RevDelFileItem {
                        'width' => $file->getWidth(),
                        'height' => $file->getHeight(),
                        'size' => $file->getSize(),
-                       'userhidden' => (bool)$file->isDeleted( Revision::DELETED_USER ),
-                       'commenthidden' => (bool)$file->isDeleted( Revision::DELETED_COMMENT ),
+                       'userhidden' => (bool)$file->isDeleted( RevisionRecord::DELETED_USER ),
+                       'commenthidden' => (bool)$file->isDeleted( RevisionRecord::DELETED_COMMENT ),
                        'contenthidden' => (bool)$this->isDeleted(),
                ];
                if ( $this->canViewContent() ) {
@@ -124,13 +126,13 @@ class RevDelArchivedFileItem extends RevDelFileItem {
                                ),
                        ];
                }
-               if ( $file->userCan( Revision::DELETED_USER, $user ) ) {
+               if ( $file->userCan( RevisionRecord::DELETED_USER, $user ) ) {
                        $ret += [
                                'userid' => $file->getUser( 'id' ),
                                'user' => $file->getUser( 'text' ),
                        ];
                }
-               if ( $file->userCan( Revision::DELETED_COMMENT, $user ) ) {
+               if ( $file->userCan( RevisionRecord::DELETED_COMMENT, $user ) ) {
                        $ret += [
                                'comment' => $file->getRawDescription(),
                        ];
index c7941b7..8c080ba 100644 (file)
@@ -19,6 +19,8 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Item class for an oldimage table row
  */
@@ -164,14 +166,14 @@ class RevDelFileItem extends RevDelItem {
         * @return string HTML
         */
        protected function getUserTools() {
-               if ( $this->file->userCan( Revision::DELETED_USER, $this->list->getUser() ) ) {
+               if ( $this->file->userCan( RevisionRecord::DELETED_USER, $this->list->getUser() ) ) {
                        $uid = $this->file->getUser( 'id' );
                        $name = $this->file->getUser( 'text' );
                        $link = Linker::userLink( $uid, $name ) . Linker::userToolLinks( $uid, $name );
                } else {
                        $link = $this->list->msg( 'rev-deleted-user' )->escaped();
                }
-               if ( $this->file->isDeleted( Revision::DELETED_USER ) ) {
+               if ( $this->file->isDeleted( RevisionRecord::DELETED_USER ) ) {
                        return '<span class="history-deleted">' . $link . '</span>';
                }
 
@@ -217,8 +219,8 @@ class RevDelFileItem extends RevDelItem {
                        'width' => $file->getWidth(),
                        'height' => $file->getHeight(),
                        'size' => $file->getSize(),
-                       'userhidden' => (bool)$file->isDeleted( Revision::DELETED_USER ),
-                       'commenthidden' => (bool)$file->isDeleted( Revision::DELETED_COMMENT ),
+                       'userhidden' => (bool)$file->isDeleted( RevisionRecord::DELETED_USER ),
+                       'commenthidden' => (bool)$file->isDeleted( RevisionRecord::DELETED_COMMENT ),
                        'contenthidden' => (bool)$this->isDeleted(),
                ];
                if ( !$this->isDeleted() ) {
@@ -236,13 +238,13 @@ class RevDelFileItem extends RevDelItem {
                                ),
                        ];
                }
-               if ( $file->userCan( Revision::DELETED_USER, $user ) ) {
+               if ( $file->userCan( RevisionRecord::DELETED_USER, $user ) ) {
                        $ret += [
                                'userid' => $file->user,
                                'user' => $file->user_text,
                        ];
                }
-               if ( $file->userCan( Revision::DELETED_COMMENT, $user ) ) {
+               if ( $file->userCan( RevisionRecord::DELETED_COMMENT, $user ) ) {
                        $ret += [
                                'comment' => $file->description,
                        ];
index 221359d..680ae8e 100644 (file)
@@ -20,6 +20,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Abstract base class for a list of deletable items. The list class
@@ -195,7 +196,7 @@ abstract class RevDelList extends RevisionListBase {
                                $status->failCount++;
                                continue;
                        // Cannot just "hide from Sysops" without hiding any fields
-                       } elseif ( $newBits == Revision::DELETED_RESTRICTED ) {
+                       } elseif ( $newBits == RevisionRecord::DELETED_RESTRICTED ) {
                                $itemStatus->warning(
                                        'revdelete-only-restricted', $item->formatDate(), $item->formatTime() );
                                $status->failCount++;
index 54a715d..edb86da 100644 (file)
@@ -19,6 +19,8 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Item class for a logging table row
  */
@@ -44,7 +46,9 @@ class RevDelLogItem extends RevDelItem {
        }
 
        public function canView() {
-               return LogEventsList::userCan( $this->row, Revision::DELETED_RESTRICTED, $this->list->getUser() );
+               return LogEventsList::userCan(
+                       $this->row, RevisionRecord::DELETED_RESTRICTED, $this->list->getUser()
+               );
        }
 
        public function canViewContent() {
index b26fffd..fcdcb9a 100644 (file)
@@ -19,6 +19,7 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IDatabase;
 
 /**
@@ -91,7 +92,7 @@ class RevDelLogList extends RevDelList {
        }
 
        public function getSuppressBit() {
-               return Revision::DELETED_RESTRICTED;
+               return RevisionRecord::DELETED_RESTRICTED;
        }
 
        public function getLogAction() {
index 6eb0b37..a5859e5 100644 (file)
@@ -19,6 +19,8 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Item class for a live revision table row
  */
@@ -63,11 +65,15 @@ class RevDelRevisionItem extends RevDelItem {
        }
 
        public function canView() {
-               return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->list->getUser() );
+               return $this->revision->userCan(
+                       RevisionRecord::DELETED_RESTRICTED, $this->list->getUser()
+               );
        }
 
        public function canViewContent() {
-               return $this->revision->userCan( Revision::DELETED_TEXT, $this->list->getUser() );
+               return $this->revision->userCan(
+                       RevisionRecord::DELETED_TEXT, $this->list->getUser()
+               );
        }
 
        public function getBits() {
@@ -108,11 +114,11 @@ class RevDelRevisionItem extends RevDelItem {
        }
 
        public function isDeleted() {
-               return $this->revision->isDeleted( Revision::DELETED_TEXT );
+               return $this->revision->isDeleted( RevisionRecord::DELETED_TEXT );
        }
 
        public function isHideCurrentOp( $newBits ) {
-               return ( $newBits & Revision::DELETED_TEXT )
+               return ( $newBits & RevisionRecord::DELETED_TEXT )
                        && $this->list->getCurrent() == $this->getId();
        }
 
@@ -203,19 +209,19 @@ class RevDelRevisionItem extends RevDelItem {
                $ret = [
                        'id' => $rev->getId(),
                        'timestamp' => wfTimestamp( TS_ISO_8601, $rev->getTimestamp() ),
-                       'userhidden' => (bool)$rev->isDeleted( Revision::DELETED_USER ),
-                       'commenthidden' => (bool)$rev->isDeleted( Revision::DELETED_COMMENT ),
-                       'texthidden' => (bool)$rev->isDeleted( Revision::DELETED_TEXT ),
+                       'userhidden' => (bool)$rev->isDeleted( RevisionRecord::DELETED_USER ),
+                       'commenthidden' => (bool)$rev->isDeleted( RevisionRecord::DELETED_COMMENT ),
+                       'texthidden' => (bool)$rev->isDeleted( RevisionRecord::DELETED_TEXT ),
                ];
-               if ( $rev->userCan( Revision::DELETED_USER, $user ) ) {
+               if ( $rev->userCan( RevisionRecord::DELETED_USER, $user ) ) {
                        $ret += [
-                               'userid' => $rev->getUser( Revision::FOR_THIS_USER ),
-                               'user' => $rev->getUserText( Revision::FOR_THIS_USER ),
+                               'userid' => $rev->getUser( RevisionRecord::FOR_THIS_USER ),
+                               'user' => $rev->getUserText( RevisionRecord::FOR_THIS_USER ),
                        ];
                }
-               if ( $rev->userCan( Revision::DELETED_COMMENT, $user ) ) {
+               if ( $rev->userCan( RevisionRecord::DELETED_COMMENT, $user ) ) {
                        $ret += [
-                               'comment' => $rev->getComment( Revision::FOR_THIS_USER ),
+                               'comment' => $rev->getComment( RevisionRecord::FOR_THIS_USER ),
                        ];
                }
 
index 07362c4..0705503 100644 (file)
@@ -19,6 +19,7 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\FakeResultWrapper;
 use Wikimedia\Rdbms\IDatabase;
 
@@ -48,7 +49,7 @@ class RevDelRevisionList extends RevDelList {
        }
 
        public static function getRevdelConstant() {
-               return Revision::DELETED_TEXT;
+               return RevisionRecord::DELETED_TEXT;
        }
 
        public static function suggestTarget( $target, array $ids ) {
@@ -167,7 +168,7 @@ class RevDelRevisionList extends RevDelList {
        }
 
        public function getSuppressBit() {
-               return Revision::DELETED_RESTRICTED;
+               return RevisionRecord::DELETED_RESTRICTED;
        }
 
        public function doPreCommitUpdates() {
index f7f7e89..5644b95 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IDatabase;
 
 /**
@@ -52,13 +53,13 @@ class RevisionDeleteUser {
                        $dbw = wfGetDB( DB_MASTER );
                }
 
-               # To suppress, we OR the current bitfields with Revision::DELETED_USER
+               # To suppress, we OR the current bitfields with RevisionRecord::DELETED_USER
                # to put a 1 in the username *_deleted bit. To unsuppress we AND the
-               # current bitfields with the inverse of Revision::DELETED_USER. The
+               # current bitfields with the inverse of RevisionRecord::DELETED_USER. The
                # username bit is made to 0 (x & 0 = 0), while others are unchanged (x & 1 = x).
                # The same goes for the sysop-restricted *_deleted bit.
-               $delUser = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
-               $delAction = LogPage::DELETED_ACTION | Revision::DELETED_RESTRICTED;
+               $delUser = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
+               $delAction = LogPage::DELETED_ACTION | RevisionRecord::DELETED_RESTRICTED;
                if ( $op === '&' ) {
                        $delUser = $dbw->bitNot( $delUser );
                        $delAction = $dbw->bitNot( $delAction );
index 7b2147a..3ab96cb 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup RevisionDelete
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * General controller for RevDel, used by both SpecialRevisiondelete and
  * ApiRevisionDelete.
@@ -129,14 +131,14 @@ class RevisionDeleter {
                $ret = [ 0 => [], 1 => [], 2 => [] ];
                // Build bitfield changes in language
                self::checkItem( 'revdelete-content',
-                       Revision::DELETED_TEXT, $diff, $n, $ret );
+                       RevisionRecord::DELETED_TEXT, $diff, $n, $ret );
                self::checkItem( 'revdelete-summary',
-                       Revision::DELETED_COMMENT, $diff, $n, $ret );
+                       RevisionRecord::DELETED_COMMENT, $diff, $n, $ret );
                self::checkItem( 'revdelete-uname',
-                       Revision::DELETED_USER, $diff, $n, $ret );
+                       RevisionRecord::DELETED_USER, $diff, $n, $ret );
                // Restriction application to sysops
-               if ( $diff & Revision::DELETED_RESTRICTED ) {
-                       if ( $n & Revision::DELETED_RESTRICTED ) {
+               if ( $diff & RevisionRecord::DELETED_RESTRICTED ) {
+                       if ( $n & RevisionRecord::DELETED_RESTRICTED ) {
                                $ret[2][] = 'revdelete-restricted';
                        } else {
                                $ret[2][] = 'revdelete-unrestricted';
index faf8d82..bf90c06 100644 (file)
@@ -20,6 +20,8 @@
  * @file
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Item class for a live revision table row
  */
@@ -53,15 +55,19 @@ class RevisionItem extends RevisionItemBase {
        }
 
        public function canView() {
-               return $this->revision->userCan( Revision::DELETED_RESTRICTED, $this->context->getUser() );
+               return $this->revision->userCan(
+                       RevisionRecord::DELETED_RESTRICTED, $this->context->getUser()
+               );
        }
 
        public function canViewContent() {
-               return $this->revision->userCan( Revision::DELETED_TEXT, $this->context->getUser() );
+               return $this->revision->userCan(
+                       RevisionRecord::DELETED_TEXT, $this->context->getUser()
+               );
        }
 
        public function isDeleted() {
-               return $this->revision->isDeleted( Revision::DELETED_TEXT );
+               return $this->revision->isDeleted( RevisionRecord::DELETED_TEXT );
        }
 
        /**
index 101570f..65cd2d2 100644 (file)
@@ -429,7 +429,7 @@ abstract class AuthManagerSpecialPage extends SpecialPage {
                                // accidentally returning it so best check and fix
                                $status = Status::wrap( $status );
                        } elseif ( is_string( $status ) ) {
-                               $status = Status::newFatal( new RawMessage( '$1', $status ) );
+                               $status = Status::newFatal( new RawMessage( '$1', [ $status ] ) );
                        } elseif ( is_array( $status ) ) {
                                if ( is_string( reset( $status ) ) ) {
                                        $status = Status::newFatal( ...$status );
index c47d87b..cf9da49 100644 (file)
@@ -49,14 +49,15 @@ class FewestrevisionsPage extends QueryPage {
                                'namespace' => 'page_namespace',
                                'title' => 'page_title',
                                'value' => 'COUNT(*)',
-                               'redirect' => 'page_is_redirect'
                        ],
                        'conds' => [
                                'page_namespace' => MediaWikiServices::getInstance()->getNamespaceInfo()->
                                        getContentNamespaces(),
-                               'page_id = rev_page' ],
+                               'page_id = rev_page',
+                               'page_is_redirect = 0',
+                       ],
                        'options' => [
-                               'GROUP BY' => [ 'page_namespace', 'page_title', 'page_is_redirect' ]
+                               'GROUP BY' => [ 'page_namespace', 'page_title' ]
                        ]
                ];
        }
index 89eb410..5b77d5a 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Special page allowing users with the appropriate permissions to
  * merge article histories, with some restrictions
@@ -293,12 +295,12 @@ class SpecialMergeHistory extends SpecialPage {
                        [],
                        [ 'oldid' => $rev->getId() ]
                );
-               if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $pageLink = '<span class="history-deleted">' . $pageLink . '</span>';
                }
 
                # Last link
-               if ( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                        $last = $this->msg( 'last' )->escaped();
                } elseif ( isset( $this->prevId[$row->rev_id] ) ) {
                        $last = $linkRenderer->makeKnownLink(
index 4f34785..f3ae31a 100644 (file)
@@ -28,6 +28,8 @@ use MediaWiki\Preferences\MultiUsernameFilter;
  */
 class SpecialMute extends FormSpecialPage {
 
+       const PAGE_NAME = 'Mute';
+
        /** @var User */
        private $target;
 
@@ -51,7 +53,7 @@ class SpecialMute extends FormSpecialPage {
 
                $this->centralIdLookup = CentralIdLookup::factory();
 
-               parent::__construct( 'Mute', '', false );
+               parent::__construct( self::PAGE_NAME, '', false );
        }
 
        /**
@@ -66,7 +68,7 @@ class SpecialMute extends FormSpecialPage {
                parent::execute( $par );
 
                $out = $this->getOutput();
-               $out->addModules( 'mediawiki.special.pageLanguage' );
+               $out->addModules( 'mediawiki.misc-authed-ooui' );
        }
 
        /**
@@ -97,10 +99,12 @@ class SpecialMute extends FormSpecialPage {
         * @return bool
         */
        public function onSubmit( array $data, HTMLForm $form = null ) {
-               if ( !empty( $data['MuteEmail'] ) ) {
-                       $this->muteEmailsFromTarget();
-               } else {
-                       $this->unmuteEmailsFromTarget();
+               foreach ( $data as $userOption => $value ) {
+                       if ( $value ) {
+                               $this->muteTarget( $userOption );
+                       } else {
+                               $this->unmuteTarget( $userOption );
+                       }
                }
 
                return true;
@@ -114,10 +118,12 @@ class SpecialMute extends FormSpecialPage {
        }
 
        /**
-        * Un-mute emails from target
+        * Un-mute target
+        *
+        * @param string $userOption up_property key that holds the blacklist
         */
-       private function unmuteEmailsFromTarget() {
-               $blacklist = $this->getBlacklist();
+       private function unmuteTarget( $userOption ) {
+               $blacklist = $this->getBlacklist( $userOption );
 
                $key = array_search( $this->targetCentralId, $blacklist );
                if ( $key !== false ) {
@@ -125,24 +131,25 @@ class SpecialMute extends FormSpecialPage {
                        $blacklist = implode( "\n", $blacklist );
 
                        $user = $this->getUser();
-                       $user->setOption( 'email-blacklist', $blacklist );
+                       $user->setOption( $userOption, $blacklist );
                        $user->saveSettings();
                }
        }
 
        /**
-        * Mute emails from target
+        * Mute target
+        * @param string $userOption up_property key that holds the blacklist
         */
-       private function muteEmailsFromTarget() {
+       private function muteTarget( $userOption ) {
                // avoid duplicates just in case
-               if ( !$this->isTargetBlacklisted() ) {
-                       $blacklist = $this->getBlacklist();
+               if ( !$this->isTargetBlacklisted( $userOption ) ) {
+                       $blacklist = $this->getBlacklist( $userOption );
 
                        $blacklist[] = $this->targetCentralId;
                        $blacklist = implode( "\n", $blacklist );
 
                        $user = $this->getUser();
-                       $user->setOption( 'email-blacklist', $blacklist );
+                       $user->setOption( $userOption, $blacklist );
                        $user->saveSettings();
                }
        }
@@ -150,30 +157,38 @@ class SpecialMute extends FormSpecialPage {
        /**
         * @inheritDoc
         */
-       protected function alterForm( HTMLForm $form ) {
+       protected function getForm() {
+               $form = parent::getForm();
                $form->setId( 'mw-specialmute-form' );
                $form->setHeaderText( $this->msg( 'specialmute-header', $this->target )->parse() );
                $form->setSubmitTextMsg( 'specialmute-submit' );
                $form->setSubmitID( 'save' );
+
+               return $form;
        }
 
        /**
         * @inheritDoc
         */
        protected function getFormFields() {
-               if ( !$this->enableUserEmailBlacklist || !$this->enableUserEmail ) {
-                       throw new ErrorPageError( 'specialmute', 'specialmute-error-email-blacklist-disabled' );
+               $fields = [];
+               if (
+                       $this->enableUserEmailBlacklist &&
+                       $this->enableUserEmail &&
+                       $this->getUser()->getEmailAuthenticationTimestamp()
+               ) {
+                       $fields['email-blacklist'] = [
+                               'type' => 'check',
+                               'label-message' => 'specialmute-label-mute-email',
+                               'default' => $this->isTargetBlacklisted( 'email-blacklist' ),
+                       ];
                }
 
-               if ( !$this->getUser()->getEmailAuthenticationTimestamp() ) {
-                       throw new ErrorPageError( 'specialmute', 'specialmute-error-email-preferences' );
-               }
+               Hooks::run( 'SpecialMuteModifyFormFields', [ $this, &$fields ] );
 
-               $fields['MuteEmail'] = [
-                       'type' => 'check',
-                       'label-message' => 'specialmute-label-mute-email',
-                       'default' => $this->isTargetBlacklisted(),
-               ];
+               if ( count( $fields ) == 0 ) {
+                       throw new ErrorPageError( 'specialmute', 'specialmute-error-no-options' );
+               }
 
                return $fields;
        }
@@ -192,18 +207,20 @@ class SpecialMute extends FormSpecialPage {
        }
 
        /**
+        * @param string $userOption
         * @return bool
         */
-       private function isTargetBlacklisted() {
-               $blacklist = $this->getBlacklist();
-               return in_array( $this->targetCentralId, $blacklist );
+       public function isTargetBlacklisted( $userOption ) {
+               $blacklist = $this->getBlacklist( $userOption );
+               return in_array( $this->targetCentralId, $blacklist, true );
        }
 
        /**
+        * @param string $userOption
         * @return array
         */
-       private function getBlacklist() {
-               $blacklist = $this->getUser()->getOption( 'email-blacklist' );
+       private function getBlacklist( $userOption ) {
+               $blacklist = $this->getUser()->getOption( $userOption );
                if ( !$blacklist ) {
                        return [];
                }
index 682bceb..7444225 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup SpecialPage
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 /**
  * Special page allowing users with the appropriate permissions to view
  * and hide revisions. Log items can also be hidden.
@@ -197,12 +199,12 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        [ $this->typeLabels['check-label'], 'wpHidePrimary',
                                RevisionDeleter::getRevdelConstant( $this->typeName )
                        ],
-                       [ 'revdelete-hide-comment', 'wpHideComment', Revision::DELETED_COMMENT ],
-                       [ 'revdelete-hide-user', 'wpHideUser', Revision::DELETED_USER ]
+                       [ 'revdelete-hide-comment', 'wpHideComment', RevisionRecord::DELETED_COMMENT ],
+                       [ 'revdelete-hide-user', 'wpHideUser', RevisionRecord::DELETED_USER ]
                ];
                if ( $user->isAllowed( 'suppressrevision' ) ) {
                        $this->checks[] = [ 'revdelete-hide-restricted',
-                               'wpHideRestricted', Revision::DELETED_RESTRICTED ];
+                               'wpHideRestricted', RevisionRecord::DELETED_RESTRICTED ];
                }
 
                # Either submit or create our form
@@ -530,7 +532,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                                        $bitfield & $field
                                );
 
-                               if ( $field == Revision::DELETED_RESTRICTED ) {
+                               if ( $field == RevisionRecord::DELETED_RESTRICTED ) {
                                        $innerHTML = "<b>$innerHTML</b>";
                                }
 
@@ -561,7 +563,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                                $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, 0, $selected == 0 ) . '</td>';
                                $line .= '<td class="mw-revdel-checkbox">' . Xml::radio( $name, 1, $selected == 1 ) . '</td>';
                                $label = $this->msg( $message )->escaped();
-                               if ( $field == Revision::DELETED_RESTRICTED ) {
+                               if ( $field == RevisionRecord::DELETED_RESTRICTED ) {
                                        $label = "<b>$label</b>";
                                }
                                $line .= "<td>$label</td>";
@@ -599,7 +601,7 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                                . $this->otherReason;
                }
                # Can the user set this field?
-               if ( $bitParams[Revision::DELETED_RESTRICTED] == 1
+               if ( $bitParams[RevisionRecord::DELETED_RESTRICTED] == 1
                        && !$this->getUser()->isAllowed( 'suppressrevision' )
                ) {
                        throw new PermissionsError( 'suppressrevision' );
@@ -662,8 +664,8 @@ class SpecialRevisionDelete extends UnlistedSpecialPage {
                        }
                        $bitfield[$field] = $val;
                }
-               if ( !isset( $bitfield[Revision::DELETED_RESTRICTED] ) ) {
-                       $bitfield[Revision::DELETED_RESTRICTED] = 0;
+               if ( !isset( $bitfield[RevisionRecord::DELETED_RESTRICTED] ) ) {
+                       $bitfield[RevisionRecord::DELETED_RESTRICTED] = 0;
                }
 
                return $bitfield;
index 31e4836..9a16a72 100644 (file)
@@ -387,11 +387,11 @@ class SpecialUndelete extends SpecialPage {
                        return;
                }
 
-               if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
-                       if ( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
+                       if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                                $out->wrapWikiMsg(
                                        "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
-                               $rev->isDeleted( Revision::DELETED_RESTRICTED ) ?
+                               $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
                                        'rev-suppressed-text-permission' : 'rev-deleted-text-permission'
                                );
 
@@ -400,7 +400,7 @@ class SpecialUndelete extends SpecialPage {
 
                        $out->wrapWikiMsg(
                                "<div class='mw-warning plainlinks'>\n$1\n</div>\n",
-                               $rev->isDeleted( Revision::DELETED_RESTRICTED ) ?
+                               $rev->isDeleted( RevisionRecord::DELETED_RESTRICTED ) ?
                                        'rev-suppressed-text-view' : 'rev-deleted-text-view'
                        );
                        $out->addHTML( '<br />' );
@@ -932,7 +932,7 @@ class SpecialUndelete extends SpecialPage {
                if ( $this->mCanView ) {
                        $titleObj = $this->getPageTitle();
                        # Last link
-                       if ( !$rev->userCan( Revision::DELETED_TEXT, $this->getUser() ) ) {
+                       if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $this->getUser() ) ) {
                                $pageLink = htmlspecialchars( $this->getLanguage()->userTimeAndDate( $ts, $user ) );
                                $last = $this->msg( 'diff' )->escaped();
                        } elseif ( $remaining > 0 || ( $earliestLiveTime && $ts > $earliestLiveTime ) ) {
@@ -1055,7 +1055,7 @@ class SpecialUndelete extends SpecialPage {
                $user = $this->getUser();
                $time = $this->getLanguage()->userTimeAndDate( $ts, $user );
 
-               if ( !$rev->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( !$rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                        return '<span class="history-deleted">' . $time . '</span>';
                }
 
@@ -1069,7 +1069,7 @@ class SpecialUndelete extends SpecialPage {
                        ]
                );
 
-               if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $link = '<span class="history-deleted">' . $link . '</span>';
                }
 
index 139f92e..1cb78b8 100644 (file)
@@ -24,6 +24,7 @@
  * @ingroup Pager
  */
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\FakeResultWrapper;
 use Wikimedia\Rdbms\IDatabase;
@@ -347,10 +348,13 @@ class ContribsPager extends RangeChronologicalPager {
 
                // Paranoia: avoid brute force searches (T19342)
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                       $queryInfo['conds'][] = $this->mDb->bitAnd( 'rev_deleted', Revision::DELETED_USER ) . ' = 0';
+                       $queryInfo['conds'][] = $this->mDb->bitAnd(
+                               'rev_deleted', RevisionRecord::DELETED_USER
+                               ) . ' = 0';
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                       $queryInfo['conds'][] = $this->mDb->bitAnd( 'rev_deleted', Revision::SUPPRESSED_USER ) .
-                               ' != ' . Revision::SUPPRESSED_USER;
+                       $queryInfo['conds'][] = $this->mDb->bitAnd(
+                               'rev_deleted', RevisionRecord::SUPPRESSED_USER
+                               ) . ' != ' . RevisionRecord::SUPPRESSED_USER;
                }
 
                // $this->getIndexField() must be in the result rows, as reallyDoQuery() tries to access it.
@@ -647,7 +651,7 @@ class ContribsPager extends RangeChronologicalPager {
                                }
                        }
                        # Is there a visible previous revision?
-                       if ( $rev->userCan( Revision::DELETED_TEXT, $user ) && $rev->getParentId() !== 0 ) {
+                       if ( $rev->userCan( RevisionRecord::DELETED_TEXT, $user ) && $rev->getParentId() !== 0 ) {
                                $difftext = $linkRenderer->makeKnownLink(
                                        $page,
                                        new HtmlArmor( $this->messages['diff'] ),
@@ -697,7 +701,7 @@ class ContribsPager extends RangeChronologicalPager {
                        # Note that only unprivileged users have rows with hidden user names excluded.
                        # When querying for an IP range, we want to always show user and user talk links.
                        $userlink = '';
-                       if ( ( $this->contribs == 'newbie' && !$rev->isDeleted( Revision::DELETED_USER ) )
+                       if ( ( $this->contribs == 'newbie' && !$rev->isDeleted( RevisionRecord::DELETED_USER ) )
                                || $this->isQueryableRange( $this->target ) ) {
                                $userlink = ' <span class="mw-changeslist-separator"></span> '
                                        . $lang->getDirMark()
@@ -757,7 +761,7 @@ class ContribsPager extends RangeChronologicalPager {
                        ];
 
                        # Denote if username is redacted for this edit
-                       if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
+                       if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
                                $templateParams['rev-deleted-user-contribs'] =
                                        $this->msg( 'rev-deleted-user-contribs' )->escaped();
                        }
index 11a8532..88e1ea8 100644 (file)
@@ -23,6 +23,7 @@
  * @ingroup Pager
  */
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IDatabase;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\FakeResultWrapper;
@@ -88,10 +89,10 @@ class DeletedContribsPager extends IndexPager {
                $user = $this->getUser();
                // Paranoia: avoid brute force searches (T19792)
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                       $conds[] = $this->mDb->bitAnd( 'ar_deleted', Revision::DELETED_USER ) . ' = 0';
+                       $conds[] = $this->mDb->bitAnd( 'ar_deleted', RevisionRecord::DELETED_USER ) . ' = 0';
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                       $conds[] = $this->mDb->bitAnd( 'ar_deleted', Revision::SUPPRESSED_USER ) .
-                               ' != ' . Revision::SUPPRESSED_USER;
+                       $conds[] = $this->mDb->bitAnd( 'ar_deleted', RevisionRecord::SUPPRESSED_USER ) .
+                               ' != ' . RevisionRecord::SUPPRESSED_USER;
                }
 
                $commentQuery = CommentStore::getStore()->getJoin( 'ar_comment' );
@@ -337,7 +338,7 @@ class DeletedContribsPager extends IndexPager {
                $comment = Linker::revComment( $rev );
                $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $user );
 
-               if ( !$user->isAllowed( 'undelete' ) || !$rev->userCan( Revision::DELETED_TEXT, $user ) ) {
+               if ( !$user->isAllowed( 'undelete' ) || !$rev->userCan( RevisionRecord::DELETED_TEXT, $user ) ) {
                        $link = htmlspecialchars( $date ); // unusable link
                } else {
                        $link = $linkRenderer->makeKnownLink(
@@ -351,7 +352,7 @@ class DeletedContribsPager extends IndexPager {
                        );
                }
                // Style deleted items
-               if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_TEXT ) ) {
                        $link = '<span class="history-deleted">' . $link . '</span>';
                }
 
@@ -384,7 +385,7 @@ class DeletedContribsPager extends IndexPager {
                $ret = "{$del}{$link} {$tools} {$separator} {$mflag} {$pagelink} {$comment}";
 
                # Denote if username is redacted for this edit
-               if ( $rev->isDeleted( Revision::DELETED_USER ) ) {
+               if ( $rev->isDeleted( RevisionRecord::DELETED_USER ) ) {
                        $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>";
                }
 
index 41c42ce..5b15e82 100644 (file)
@@ -379,13 +379,6 @@ abstract class UploadBase {
                        return $result;
                }
 
-               $error = '';
-               if ( !Hooks::run( 'UploadVerification',
-                       [ $this->mDestName, $this->mTempPath, &$error ], '1.28' )
-               ) {
-                       return [ 'status' => self::HOOK_ABORTED, 'error' => $error ];
-               }
-
                return [ 'status' => self::OK ];
        }
 
@@ -1129,6 +1122,8 @@ abstract class UploadBase {
         * @throws UploadStashNotLoggedInException
         */
        public function stashFile( User $user = null ) {
+               wfDeprecated( __METHOD__, '1.28' );
+
                return $this->doStashFile( $user );
        }
 
@@ -1146,29 +1141,6 @@ abstract class UploadBase {
                return $file;
        }
 
-       /**
-        * Stash a file in a temporary directory, returning a key which can be used
-        * to find the file again. See stashFile().
-        *
-        * @deprecated since 1.28
-        * @return string File key
-        */
-       public function stashFileGetKey() {
-               wfDeprecated( __METHOD__, '1.28' );
-               return $this->doStashFile()->getFileKey();
-       }
-
-       /**
-        * alias for stashFileGetKey, for backwards compatibility
-        *
-        * @deprecated since 1.28
-        * @return string File key
-        */
-       public function stashSession() {
-               wfDeprecated( __METHOD__, '1.28' );
-               return $this->doStashFile()->getFileKey();
-       }
-
        /**
         * If we've modified the upload file we need to manually remove it
         * on exit to clean up.
index 1bd99c1..cc527e7 100644 (file)
@@ -86,30 +86,9 @@ class UploadFromChunks extends UploadFromFile {
         */
        public function stashFile( User $user = null ) {
                wfDeprecated( __METHOD__, '1.28' );
-               $this->verifyChunk();
-               return parent::stashFile( $user );
-       }
 
-       /**
-        * @inheritDoc
-        * @throws UploadChunkVerificationException
-        * @deprecated since 1.28
-        */
-       public function stashFileGetKey() {
-               wfDeprecated( __METHOD__, '1.28' );
                $this->verifyChunk();
-               return parent::stashFileGetKey();
-       }
-
-       /**
-        * @inheritDoc
-        * @throws UploadChunkVerificationException
-        * @deprecated since 1.28
-        */
-       public function stashSession() {
-               wfDeprecated( __METHOD__, '1.28' );
-               $this->verifyChunk();
-               return parent::stashSession();
+               return parent::stashFile( $user );
        }
 
        /**
index f6ad623..df5ea70 100644 (file)
@@ -1,6 +1,7 @@
 <?php
 
 use MediaWiki\Linker\LinkTarget;
+use MediaWiki\Storage\RevisionRecord;
 use MediaWiki\User\UserIdentity;
 use Wikimedia\Assert\Assert;
 use Wikimedia\Rdbms\IDatabase;
@@ -566,9 +567,9 @@ class WatchedItemQueryService {
                // Avoid brute force searches (T19342)
                $bitmask = 0;
                if ( !$user->isAllowed( 'deletedhistory' ) ) {
-                       $bitmask = Revision::DELETED_USER;
+                       $bitmask = RevisionRecord::DELETED_USER;
                } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) {
-                       $bitmask = Revision::DELETED_USER | Revision::DELETED_RESTRICTED;
+                       $bitmask = RevisionRecord::DELETED_USER | RevisionRecord::DELETED_RESTRICTED;
                }
                if ( $bitmask ) {
                        $conds[] = $db->bitAnd( 'rc_deleted', $bitmask ) . " != $bitmask";
index 9fc7d73..61a967d 100644 (file)
@@ -21,6 +21,7 @@
 use MediaWiki\MediaWikiServices;
 
 use MediaWiki\Logger\LoggerFactory;
+use MediaWiki\Storage\RevisionRecord;
 
 /**
  * Base class for language conversion.
@@ -1043,7 +1044,7 @@ class LanguageConverter {
                                $revision = Revision::newFromTitle( $title );
                                if ( $revision ) {
                                        if ( $revision->getContentModel() == CONTENT_MODEL_WIKITEXT ) {
-                                               $txt = $revision->getContent( Revision::RAW )->getText();
+                                               $txt = $revision->getContent( RevisionRecord::RAW )->getText();
                                        }
 
                                        // @todo in the future, use a specialized content model, perhaps based on json!
index adead9a..99dbc27 100644 (file)
        "autoblockedtext": "مُنِع عنوان آيبيك تلقائيا لأن مستخدما آخرا منعه $1 استخدمه.\nالسبب المعطى هو التالي:\n\n:<em>$2</em>\n\n* بداية المنع: $8\n* انتهاء المنع: $6\n* الممنوع المقصود: $7\n\nيمكنك أن تتصل ب $1 أو أحد [[{{MediaWiki:Grouppage-sysop}}|الإداريين]] الآخرين لمناقشة المنع.\n\nلاحظ أنه لا يمكنك استخدام خاصية \"{{int:emailuser}}\" إلا لو كان لديك عنوان بريد إلكتروني صحيح مسجل في [[Special:Preferences|تفضيلاتك]] ولم يتم منعك من استخدامه.\n\nعنوان آيبيك الحالي $3، ورقم المنع #$5.\nمن فضلك اذكر كل التفاصيل بالأعلى في أي استعلامات تقوم بها.",
        "systemblockedtext": "اسم المستخدم أو عنوان الأيبي الخاص بك تم منعه تلقائيا بواسطة ميدياويكي.\nالسبب المعطى هو:\n\n:<em>$2</em>\n\n* بداية المنع: $8\n* نهاية المنع: $6\n* المقصود بالمنع: $7\n\nعنوان الأيبي الحالي الخاص بك هو $3.\nمن فضلك ضمن كل التفاصيل بالأعلى في أي استعلام تقوم به.",
        "blockednoreason": "لا سبب معطى",
-       "blockedtext-composite": "<strong>تم منع اسم المستخدم أو عنوان الآيبي الخاص بك.</strong>\n\nالسبب المعطى هو:\n\n:<em>$2</em>.\n\n* بداية المنع: $8\n*  نهاية صلاحية أطول منع: $6\n\nعنوان الآيبي الحالي الخاص بك هو $3.\nيُرجَى تضمين جميع التفاصيل أعلاه في أية استفسارات تقوم بها.",
+       "blockedtext-composite": "<strong>تم منع اسم المستخدم أو عنوان الآيبي الخاص بك.</strong>\n\nالسبب المعطى هو:\n\n:<em>$2</em>.\n\n* بداية المنع: $8\n*  نهاية صلاحية أطول منع: $6\n\n* $5\n\nعنوان الآيبي الحالي الخاص بك هو $3.\nيُرجَى تضمين جميع التفاصيل أعلاه في أية استفسارات تقوم بها.",
+       "blockedtext-composite-ids": "معرفات المنع ذات الصلة: $1 (قد يتم أيضا إدراج عنوان الآيبي الخاص بك في القائمة السوداء)",
+       "blockedtext-composite-no-ids": "يظهر عنوان الآيبي الخاص بك في العديد من القوائم السوداء",
        "blockedtext-composite-reason": "هناك عدة عمليات منع ضد حسابك و/أو عنوان الآيبي الخاص بك",
        "whitelistedittext": "يجب عليك $1 لتتمكن من تعديل الصفحات.",
        "confirmedittext": "يجب عليك تأكيد بريدك الإلكتروني قبل تعديل الصفحات.\nمن فضلك اكتب وأكد بريدك الإلكتروني من خلال [[Special:Preferences|تفضيلاتك]].",
        "specialmute-error-email-preferences": "يجب تأكيد عنوان بريدك الإلكتروني قبل أن تتمكن من كتم صوت المستخدم، يمكنك القيام بذلك من [[Special:Preferences]].",
        "specialmute-email-footer": "لإدارة تفضيلات البريد الإلكتروني لـ{{BIDI:$2}}؛ تُرجَى زيارة <$1>",
        "specialmute-login-required": "يُرجَى تسجيل الدخول لتغيير تفضيلات الصمت الخاصة بك.",
+       "mute-preferences": "كتم صوت التفضيلات",
        "revid": "المراجعة $1",
        "pageid": "معرف الصفحة $1",
        "interfaceadmin-info": "$1\n\nتم فصل صلاحيات تحرير ملفات CSS/JS/JSON على مستوى الموقع مؤخرً من صلاحية  <code>editinterface</code>، إذا لم تفهم سبب حصولك على هذا الخطأ، فراجع  [[mw:MediaWiki_1.32/interface-admin]].",
index b3707a6..f2a7a95 100644 (file)
        "allowemail": "Permitir qu'otros usuarios m'unvien correos",
        "email-allow-new-users-label": "Permitir los correos de los usuarios nuevos",
        "email-blacklist-label": "Torgar qu'estos usuarios m'unvien correos electrónicos:",
-       "prefs-searchoptions": "Buscar",
+       "prefs-searchoptions": "Gueta",
        "prefs-namespaces": "Espacios de nome",
        "default": "predetermináu",
        "prefs-files": "Ficheros",
index 7ca94c7..c54899f 100644 (file)
@@ -84,7 +84,7 @@
        "february": "پبرݔر",
        "march": "مارچ",
        "april": "آپرݔل",
-       "may_long": "می",
+       "may_long": "بلکݔں",
        "june": "جۏن",
        "july": "جۏلی",
        "august": "آگوست",
        "feb": "پبر",
        "mar": "مارچ",
        "apr": "آپریل",
-       "may": "می",
+       "may": "بلکݔں",
        "jun": "جۏن",
        "jul": "جۏل",
        "aug": "آگو",
        "print": "چهاپ",
        "view": "دیستین",
        "view-foreign": "بگیند بی $1",
-       "edit": "ایڈیٹ",
-       "edit-local": "لوکال دسکریپشنء ایڈیٹ کورتین",
+       "edit": "ٹگلݔنگ",
+       "edit-local": "گوشتانکانء ٹگلݔنگ",
        "create": "جوڑ کورتین",
        "create-local": "لوکال دسکریپشنء اڈ کورتین",
        "delete": "پاک کورتین",
        "unprotect": "پروتکشنء ٹگل بدئ",
        "newpage": "نۏکݔں تاک",
        "talkpagelinktext": "گپ کن",
-       "specialpage": "Ù\87اسÛ\8cÙ\86 Ø¯Û\8cÙ\85",
+       "specialpage": "Ù\88تÛ\8cÚ¯Û\8c ØªØ§Ú©",
        "personaltools": "شخصی وسایل",
        "talk": "گپ",
        "views": "چارگان",
        "edithelp": "ٹگلݔنگء پئیم",
        "helppage-top-gethelp": "کومک",
        "mainpage": "بُنیادی دیم",
-       "mainpage-description": "بُنیادی دیم",
+       "mainpage-description": "بُنتاک",
        "policy-url": "Project:سیاست",
        "portal": "دیوانءِ درگت",
        "portal-url": "Project:پرتال انجمن",
        "newmessageslinkplural": "{{PLURAL:$1|نوکین کله|999=نوکین کله هان}}",
        "newmessagesdifflinkplural": "$1 {{PLURAL:$1|ٹگل داتن|پهکین ٹگل}}",
        "youhavenewmessagesmulti": "شما را نوکین کوله یان هست ته   $1",
-       "editsection": "ایڈیٹ",
-       "editold": "ایڈیٹ",
+       "editsection": "ٹگلݔنگ",
+       "editold": "ٹگلݔنگ",
        "viewsourceold": "به گند منبع ا",
-       "editlink": "ایڈیٹ",
+       "editlink": "ٹگلݔنگ",
        "viewsourcelink": "سرچمّگء چارگ",
        "editsectionhint": ": $1اصلاح انتخاب",
        "toc": "محتوا",
        "showtoc": "پیش دار",
        "hidetoc": "چیهر داتین",
        "collapsible-collapse": "چیهر داتین",
-       "collapsible-expand": "تچک کتن",
+       "collapsible-expand": "تچک کنگ",
        "confirmable-confirm": "آیا {{GENDER:$1|شما}} مطمعین ویت?",
        "confirmable-yes": "هان",
        "confirmable-no": "نه",
        "externaldberror": "یک حطا دیتابیس تصدیق هویت دراییگی هست یا شما را اجازت نیست وتی حساب درایی په روچ کنیت.",
        "login": "ورود",
        "nav-login-createaccount": "ورود/شرکتن حساب",
-       "logout": "در بوتین",
-       "userlogout": "در بوتین",
+       "logout": "در بییگ",
+       "userlogout": "در بییگ",
        "notloggedin": "وارد نهت",
        "userlogin-noaccount": "شما رء اکانت نه انت؟",
        "userlogin-joinproject": "مان {{SITENAME}} ناملکی بکن ات!",
        "hr_tip": "خط افقی",
        "summary": "کمݔں:",
        "subject": "موضوع/سرخط:",
-       "minoredit": "ای شی یک هوردین اصلاحیت",
+       "minoredit": "اݔشی یک ھُردݔں ٹگلے",
        "watchthis": "اے تاکا بہ چار",
        "savearticle": "تاکدیمِ ذخیره کورتین",
        "preview": "دیستین",
        "showpreview": "بازبین پیش دار",
-       "showdiff": "تغییرات پیش دار",
+       "showdiff": "ٹگلاں سۏج دئ",
        "blankarticle": "<strong>هشتار:</strong> شما بی یک هالی دیمی جوڑ\t کورتینا ئالاتا ویت.\nاگه \"$1\" ها پدا کلیک کنیت، بدون مئتواین دیمی جوڑ\tا بیت .",
        "anoneditwarning": "<strong>هشتار:</strong> شما لوگین نه بوته ایت . شمی آی پی نشانی په موچان دیستینی وڑ\tا بیت اگه هر \tٹگلی جوڑ\t بداریت . اگه <strong>[$1 لوگین بئیت]</strong> یا <strong>[$2 یک کاربرین حسابی جوڑ بکنیت]</strong>، شمی ایڈیت بی شمی کاربرین نام نسبت داته آ بینت، لوڑ گون دیگرین مزایایان .",
        "anonpreviewwarning": "''شما مان سیستمء لاگین نکتگ. ذخیره کتنء حالت شمئی آی‌پیء نشانیگ مان ائ پیجء هاپزگ سبت بیت انت.''",
        "lineno": "خط$1:",
        "compareselectedversions": "مقایسه انتخاب بوتگین نسخه یان",
        "showhideselectedversions": "سۏج/جوان کنگ اے ورژنئی",
-       "editundo": "خنثی کتن",
+       "editundo": "چھر دیگ",
        "diff-empty": "(بئ پرک)",
        "diff-multi-sameuser": "({{PLURAL:$1|یک میانجیگین نسخگ|$1 میانجیگین نسخگ}} گون همجندیء کاربر که پیش دارگ نه بوتگ انت)",
        "diff-multi-otherusers": "({{PLURAL:$1|یک میانجیگین نسخگ$1 میانجیگین نسخگ ان}} گون {{PLURAL:$2|دگرین کاربر|$2 کاربران}} پیش دارگ نه بوتگ انت)",
        "prefs-help-email": "ایمیلء ادرس ایهتیاری انت، بلئی پسوردء واتر کتن اگان وتی پسوردء شموش بکن ات ممکن بیت کنت انت.",
        "prefs-help-email-others": "شما هنچوشء توان ات اینتهاب بکن ات که کاربران بتوان انت چه شمئی بنتاکء لینک یانکه تران تاکء پر شما ایمیل دیم بده انت.\nشمئی ایمیل ادرس وهدء که ادگر شهسء پر شما ایمیل بجنت پیداگ نه بیت.",
        "prefs-help-email-required": "آدرس ایمیل نیازنت.",
-       "prefs-info": "اولگین اطلاعات",
+       "prefs-info": "اولیگݔں زانشتان",
        "prefs-i18n": "بین المللی کتن",
        "prefs-signature": "دسنام",
        "prefs-dateformat": "تاریح داب",
        "right-move-categorypages": "انتقال مان کتگوریء پیج ان",
        "right-movefile": "جاه په جاه کتن فایلان",
        "right-suppressredirect": "شر نه کتن یک غیر مستقیم چه کهنگین نام وهدی که یک صفحه ای جاه په جاه بیت",
-       "right-upload": "آپلود فایلان",
+       "right-upload": "پایلانء آپلود",
        "right-reupload": "هستین فایلی اوری بنویس",
        "right-reupload-own": "بنویس هستین فایلی که یک نفری آپلود بوتگین",
        "right-reupload-shared": "بنویس فایلانی که ته انبار میدیا شریکی ملکی انت",
        "action-move-rootuserpages": "جاه په جاه کتن صفحات کاربری اصلی",
        "action-move-categorypages": "انتقال مان کتگوریء پیج ان",
        "action-movefile": "ای فایلءَ جاه په جاه کن",
-       "action-upload": "آپلود کتن ای قایل",
+       "action-upload": "آپلود کنگ",
        "action-reupload": "نویسگ سر ای موجودن فایل",
        "action-reupload-shared": "باطل کتن ای فایل ته مخزن شریکی",
        "action-upload_by_url": "آیلود کتن ای فایل چه یک آدرس اینترنتیء",
        "nchanges": "$1 {{PLURAL:$1|تغییر|تغییرات}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|چه آهریگین چارگ}}",
        "enhancedrc-history": "وھدگ",
-       "recentchanges": "نوکین تغییرات",
+       "recentchanges": "نۏکݔں ٹگلاں",
        "recentchanges-legend": "گزینه ی نوکین تغییرات",
        "recentchanges-summary": "رندگر نوکترین تغییرات ته ویکی تی ای صفحه.",
        "recentchanges-noresult": "هچ تغییرء مان درچتگین درگتء گون ائ معیاران همگرنچ نه انت.",
        "rc-enhanced-expand": "جزئیاتء پیس دارگ",
        "rc-enhanced-hide": "پناه کتن جزییات",
        "rc-old-title": "اڈ بیتگ گون «$1»",
-       "recentchangeslinked": "مربوطین تغییرات",
+       "recentchangeslinked": "امبندݔں ٹگلاں",
        "recentchangeslinked-feed": "امبندݔں ٹگلاں",
        "recentchangeslinked-toolbox": "امبندݔں ٹگلاں",
        "recentchangeslinked-title": "تغییراتی مربوط په \"$1\"",
        "recentchangeslinked-summary": "شی یک لیستی چه تغییراتی هستنت که نوکی اعمال بوتگنت په صفحاتی که چه یک صفحه خاصی لینک بوته( یا په اعضای یک خاصین دسته).\nصفحات ته [[Special:Watchlist| شمی لیست چارگ]] '''' پررنگنت''''",
        "recentchangeslinked-page": "تاکدیمِ نام:",
        "recentchangeslinked-to": "پیش دار تغییرات په صفحاتی که لینک بوتگنت به جاه داتگین صفحه",
-       "upload": "آپلود کتن فایل",
+       "upload": "آپلود کنگ",
        "uploadbtn": "پایلء آپلود",
        "reuploaddesc": "کنسل آپلودء و ترر په فرم آپلود",
        "upload-tryagain": "فایلء ٹگا وارتگین توضیحاتء راهیگ بکن ات",
        "filename": "نام فایل",
        "filedesc": "کمݔں",
        "fileuploadsummary": "پائلء باروا:",
-       "filereuploadsummary": "تغییرات فایل:",
+       "filereuploadsummary": "پایلء ٹگلاں:",
        "filestatus": "وضعیت حق کپی:",
        "filesource": "منبع:",
        "ignorewarning": "هوژاری شموش و هرداب منته فایل ذخیره کن",
        "fileexists-shared-forbidden": "یک فایلی گون ای نام الان ته منبع مشترک فایل هستن.\nلطفا برگردیت و ای فایل گون نوکین نامی آپلود کنیت.[[File:$1|انگشتی|مرکز|$1]]",
        "file-exists-duplicate": "ای فایل کپیء چه جهلیگین  {{PLURAL:$1|فایل|فایلان}}:",
        "file-deleted-duplicate": "یک فایلی په داب ای فایل ([[:$1]]) پیسرتر حذف بوتگت. شما بایدن تاریح حذف آ فایلء دگه بچاریت",
-       "uploadwarning": "هوژاری آپلود",
+       "uploadwarning": "آپلودء ھُژاری",
        "savefile": "ذخیره فایل",
        "uploaddisabled": "آپ.د غبر فعال انت",
        "copyuploaddisabled": "بارگذاری از طریق نشانی اینترنتی غیرفعال است.",
        "protectedtitles": "عناوین محافظتی",
        "protectedtitles-summary": "این صفحه فهرست صفحات موجود است که در حال حاضر محافظت از ساخت شده‌اند. برای فهرست عنوان‌هایی که محافظت از ویرایش شده‌اند، به [[{{#special:ProtectedPages}}|{{int:protectedpages}}]] مراجعه کنید.",
        "protectedtitlesempty": "هچ عنوانی هنو گو ای پارامتران محافظت نهنت.",
-       "listusers": "لیست کاربر",
+       "listusers": "کارزورۏکء لیست",
        "listusers-editsonly": "فقط کاربرانی که اصلاحاتش هست پیش بدار",
        "listusers-creationsort": "ترتیب په اساس تاریح",
        "usereditcount": "$1 {{PLURAL:$1|اصلاح|اصلاحات}}",
        "noemailtext": "ای کاربر یک آدرس ایمیل معتبری مشخص نه کتت.",
        "nowikiemailtext": "ای کاربر نه لوٹیت چه دگه کاربران ایمیل بگیرت.",
        "emailusername": "کار زوروکی نام:",
-       "emailusernamesubmit": "دیم دی",
+       "emailusernamesubmit": "دݔم دئ",
        "email-legend": "یک ایمیلی په دگه کاربر {{SITENAME}} دیم دی",
        "emailfrom": "شه:",
        "emailto": "بی:",
        "emailsubject": "موضوع:",
        "emailmessage": "کوله:",
-       "emailsend": "دیم دی",
+       "emailsend": "دݔم دئ",
        "emailccme": "یک کپی چه منی کوله په من وت ایمیل کن.",
        "emailccsubject": "کپی چه شمی کوله په $1: $2",
        "emailsent": "ایمیل دیم دهگ بوت",
        "tooltip-pt-watchlist": "لیست صفحیانی که شما تغییرات آیانا رند گرگیت",
        "tooltip-pt-mycontris": "لیست شمی مشارکتان",
        "tooltip-pt-login": "شر ترنت که وارد بیت، بله شی اجبار نهنت",
-       "tooltip-pt-logout": "در بیگ",
+       "tooltip-pt-logout": "در بییگ",
        "tooltip-ca-talk": "بحث دباره محتوای صفحه",
        "tooltip-ca-edit": "شما تونیت ای صفحه یا اصلاح کنیت. لطفا چه بازبین دکمه پیش چه ذخیره کتن استفاده کنیت.",
        "tooltip-ca-addsection": "یک نوکین بخشی شر کن",
        "tooltip-ca-nstab-help": "صفحه کمک بچار",
        "tooltip-ca-nstab-category": "دسته صفحه ی بچار",
        "tooltip-minoredit": "شی آ په داب یک اصلاح جزی نشان بل",
-       "tooltip-save": "وتی تغییرات ذخیره کن",
+       "tooltip-save": "وتی ٹگلاں سپت کن",
        "tooltip-preview": "بازبین کن وتی تغییراتا، لطفا پیش چه ذخیره کتن شیا استفاده کن.",
        "tooltip-diff": "پیش دار تغییراتی که شما په نوشته دات.",
        "tooltip-compareselectedversions": "بچار تفاوتان بین دو انتخاب بوتگین نسخه یان این صفحه",
index 21c661f..08ac863 100644 (file)
        "autoblockedtext": "Ваш IP-адрас быў аўтаматычна заблякаваны, таму што ён ужываўся іншым удзельнікам, які быў заблякаваны $1.\nПрычына гэтага:\n\n:<em>$2</em>\n\n* Блякаваньне пачалося: $8\n* Блякаваньне скончыцца: $6\n* Быў заблякаваны: $7\n\nВы можаце скантактавацца з $1 ці з адным зь іншых [[{{MediaWiki:Grouppage-sysop}}|адміністратараў]], каб абмеркаваць блякаваньне.\n\nЗаўважце, што вы ня зможаце ўжываць магчымасьць «{{int:emailuser}}», пакуль ня будзе пазначаны дзейны адрас электроннай пошты ў вашых [[Special:Preferences|наладах удзельніка]], і калі гэта вам не было забаронена.\n\nВаш цяперашні IP-адрас — $3, ідэнтыфікатар блякаваньня — #$5.\nКалі ласка, улучайце ўсю вышэйпададзеную інфармацыю ва ўсе запыты, што вы будзеце рабіць.",
        "systemblockedtext": "Вашае імя ўдзельніка ці IP-адрас былі аўтаматычна заблякаваныя MediaWiki.\nЗ наступнай прычыны:\n\n:<em>$2</em>\n\n* Пачатак блякаваньня: $8\n* Сканчэньне блякаваньня: $6\n* Мэта блякаваньня: $7\n\nВаш цяперашні IP-адрас — $3.\nКалі ласка, уключайце ўсе пададзеныя вышэй дэталі ва ўсе запыты, што вы робіце.",
        "blockednoreason": "прычына не пазначана",
-       "blockedtext-composite": "<strong>Вашае імя ўдзельніка ці IP-адрас былі заблякаваныя.</strong>\n\nПададзеная прычына:\n\n:<em>$2</em>.\n\n* Пачатак блякаваньня: $8\n* Сканчэньне найдаўжэйшага з блякаваньняў: $6\n\nВаш цяперашні IP-адрас — $3.\nКалі ласка, дадайце ўсе падрабязнасьці, прыведзеныя вышэй, у запыты, што вы будзеце рабіць.",
+       "blockedtext-composite": "<strong>Вашае імя ўдзельніка ці IP-адрас былі заблякаваныя.</strong>\n\nПададзеная прычына:\n\n:<em>$2</em>.\n\n* Пачатак блякаваньня: $8\n* Сканчэньне найдаўжэйшага з блякаваньняў: $6\n\n* $5\n\nВаш цяперашні IP-адрас — $3.\nКалі ласка, дадайце ўсе падрабязнасьці, прыведзеныя вышэй, у запыты, што вы будзеце рабіць.",
        "blockedtext-composite-reason": "Маецца некалькі блякаваньняў вашага рахунку і/ці IP-адрасу",
        "whitelistedittext": "Вам трэба $1, каб рэдагаваць старонкі.",
        "confirmedittext": "Вы мусіце пацьвердзіць Ваш адрас электроннай пошты перад рэдагаваньнем старонак. Калі ласка, пазначце і пацьвердзіце адрас электроннай пошты праз Вашы [[Special:Preferences|налады]].",
        "specialmute-error-email-preferences": "Вы мусіце пацьвердзіць ваш адрас электроннай пошты перад тым, як зможаце заглушыць удзельніка. Вы можаце зрабіць гэта ў [[Special:Preferences|наладах]].",
        "specialmute-email-footer": "Для кіраваньня наладамі электроннай пошты для {{BIDI:$2}}, калі ласка, наведайце <$1>.",
        "specialmute-login-required": "Калі ласка, увайдзіце, каб зьмяніць вашыя налады заглушэньня.",
+       "mute-preferences": "Налады заглушэньня",
        "revid": "вэрсія $1",
        "pageid": "Ідэнтыфікатар старонкі $1",
        "interfaceadmin-info": "$1\n\nДазволы на рэдагаваньне агульнасайтавых CSS/JS/JSON-файлаў былі нядаўна вылучаныя з права <code>editinterface</code>. Калі вы не разумееце, чаму атрымліваеце гэтую памылку, глядзіце [[mw:MediaWiki_1.32/interface-admin]].",
index 248b975..50e7aa8 100644 (file)
        "history": "Гісторыя старонкі",
        "history_short": "Гісторыя",
        "history_small": "гісторыя",
-       "updatedmarker": "абноÑ\9eлена Ð°Ð´ Ñ\87аÑ\81Ñ\83 апошняга наведвання",
+       "updatedmarker": "абноÑ\9eлена Ð¿Ð°Ñ\81лÑ\8f Ð²Ð°Ñ\88ага апошняга наведвання",
        "printableversion": "Для друку",
        "permalink": "Нязменная спасылка",
        "print": "Друкаваць",
        "virus-scanfailed": "не ўдалося праверыць (код $1)",
        "virus-unknownscanner": "невядомы антывірус:",
        "logouttext": "<strong>Вы выйшлі з сістэмы.</strong>\n\nЗаўважце, што некаторыя старонкі могуць паказвацца так, быццам вы яшчэ не выйшлі; у такім разе трэба ачысціць кэш вашага браўзера.",
+       "logout-failed": "Немагчыма выйсці зараз: $1",
        "cannotlogoutnow-title": "Зараз немагчыма выйсці",
        "cannotlogoutnow-text": "Пры выкарыстанні $1 выхад з сістэмы немагчымы.",
        "welcomeuser": "Вітаем, $1!",
        "page_first": "перш.",
        "page_last": "апошн.",
        "histlegend": "Выбар розніцы: адзначце радыё-боксы версій, якія трэба параўнаць і націсніце enter або кнопку, што ўнізе.<br /> Тлумачэнне: (з актуальн.) = розніца з актуальнай версіяй, (з папярэд.) = розніца з папярэдняй версіяй, д = дробная праўка.",
-       "history-fieldset-title": "Ð\9fÑ\80аглÑ\8fд Ð³Ñ\96Ñ\81Ñ\82оÑ\80Ñ\8bі",
+       "history-fieldset-title": "ФÑ\96лÑ\8cÑ\82Ñ\80аваÑ\86Ñ\8c Ð¿Ñ\80аÑ\9eкі",
        "history-show-deleted": "Толькі выдаленыя",
        "histfirst": "найстарэйшыя",
        "histlast": "найноўшыя",
        "action-changetags": "дадаваць і выдаляць адвольныя біркі да асобных версій і запісаў у журнале падзей",
        "action-deletechangetags": "выдаляць біркі з базы даных",
        "action-purge": "ачысціць кэш гэтай старонкі",
+       "action-apihighlimits": "карыстацца вышэйшымі лімітамі ў API-зваротах",
+       "action-autoconfirmed": "не падпарадкоўвацца абмежаванням хуткасці, накладзеным на IP",
+       "action-bigdelete": "выдаляць старонкі з вялікімі гісторыямі",
+       "action-blockemail": "забараняць удзельніку адсыланне эл.пошты",
+       "action-bot": "лічыцца аўтаматычным працэсам",
+       "action-editprotected": "правіць старонкі пад аховай \"{{int:protect-level-sysop}}\"",
+       "action-editsemiprotected": "правіць старонкі, што пад аховай \"{{int:protect-level-autoconfirmed}}\"",
+       "action-editinterface": "правіць карыстальніцкі інтэрфейс",
+       "action-editusercss": "правіць CSS-файлы іншых удзельнікаў",
+       "action-edituserjson": "правіць JSON-файлы іншых удзельнікаў",
+       "action-edituserjs": "правіць JavaScript-файлы іншых удзельнікаў",
+       "action-editmyusercss": "правіць уласныя CSS-файлы",
+       "action-editmyuserjson": "правіць уласныя JSON-файлы",
+       "action-editmyuserjs": "правіць уласныя JavaScript-файлы",
+       "action-viewsuppressed": "бачыць версіі, схаваныя ад усіх удзельнікаў",
+       "action-hideuser": "забараняць імя ўдзельніка і рабіць яго нябачным",
+       "action-ipblock-exempt": "перамагаць забароны на IP, аўта- і дыяпазонныя забароны",
+       "action-unblockself": "разблакаваць сябе",
+       "action-noratelimit": "не падпарадкоўвацца абмежаванням хуткасці",
+       "action-reupload-own": "перазапісваць уласныя існуючыя файлы",
+       "action-nominornewtalk": "не паведамляць пра новыя паведамленні ў адказ на дробныя праўкі размоўных старонак",
+       "action-markbotedits": "меціць адкочаныя праўкі як праўкі робатаў",
+       "action-patrolmarks": "бачыць меткі ўхвалення нядаўніх змяненняў",
+       "action-override-export-depth": "экспартаваць старонкі, у тым ліку звязаныя, да глыбіні спасылак 5.",
+       "action-suppressredirect": "не пакідаць перасылку са старой назвы пасля пераносу старонкі",
        "nchanges": "$1 {{PLURAL:$1|змена|змены|змен}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|з часу апошняга наведвання}}",
        "enhancedrc-history": "гісторыя",
        "rcfilters-savedqueries-already-saved": "Гэтыя фільтры ўжо захаваны. Змяніце свае настройкі, каб стварыць новы Захаваны фільтр.",
        "rcfilters-restore-default-filters": "Аднавіць фільтры па змоўчанні",
        "rcfilters-clear-all-filters": "Ачысціць усе фільтры",
-       "rcfilters-show-new-changes": "Паказаць навейшыя змяненні",
+       "rcfilters-show-new-changes": "Паказаць навейшыя змяненні з $1",
        "rcfilters-search-placeholder": "Змяненні фільтра (выкарыстоўвайце меню ці шукайце па назве фільтра)",
        "rcfilters-invalid-filter": "Недапушчальны фільтр",
        "rcfilters-empty-filter": "Няма актыўных фільтраў. Паказваюцца ўсе праўкі.",
        "rcfilters-filterlist-title": "Фільтры",
        "rcfilters-filterlist-whatsthis": "Як гэта працуе?",
-       "rcfilters-filterlist-feedbacklink": "Паведаміце нам сваё меркаванне аб гэтых (новых) інструментах фільтрацыі",
+       "rcfilters-filterlist-feedbacklink": "Паведаміце нам сваё меркаванне аб гэтых інструментах фільтрацыі",
        "rcfilters-highlightbutton-title": "Выдзеліць вынікі",
        "rcfilters-highlightmenu-title": "Выбраць колер",
        "rcfilters-highlightmenu-help": "Выбраць колер для падсвечвання уласцівасці",
        "imagelinks": "Выкарыстанне файла",
        "linkstoimage": "{{PLURAL:$1|Наступная $1 старонка выкарыстоўвае|Наступныя $1 старонкі выкарыстоўваюць|Наступныя $1 старонак выкарыстоўваюць}} гэты файл:",
        "linkstoimage-more": "На гэты файл існуюць спасылкі з больш як $1 {{PLURAL:$1|старонкі|старонак}}.\nНаступны пералік паказвае толькі {{PLURAL:$1|першую спасылку|першыя $1 з іх}}.\nТаксама ёсць [[Special:WhatLinksHere/$2|поўны пералік]].",
-       "nolinkstoimage": "Ð\9dÑ\8fма Ñ\81Ñ\82аÑ\80онак, Ñ\8fкÑ\96Ñ\8f Ð± Ñ\81паÑ\81Ñ\8bлалÑ\96Ñ\81Ñ\8f Ð½Ð° файл.",
+       "nolinkstoimage": "Ð\9dÑ\8fма Ñ\81Ñ\82аÑ\80онак, Ñ\8fкÑ\96Ñ\8f Ð²Ñ\8bкаÑ\80Ñ\8bÑ\81Ñ\82оÑ\9eваÑ\8eÑ\86Ñ\8c Ð³Ñ\8dÑ\82Ñ\8b файл.",
        "morelinkstoimage": "Паказаць [[Special:WhatLinksHere/$1|больш спасылак]] на гэты файл.",
        "linkstoimage-redirect": "$1 (файл-перанакіраванне) $2",
        "duplicatesoffile": "Наступн{{PLURAL:$1|ы файл з’яўляецца дублікатам|ыя $1 файлы з’яўляюцца дублікатамі}} гэтага файла ([[Special:FileDuplicateSearch/$2|падрабязна]]):",
        "apisandbox-submit": "Зрабіць запыт",
        "apisandbox-reset": "Ачысціць",
        "apisandbox-retry": "Паўтарыць",
+       "apisandbox-loading": "Загрузка інфармацыі для API-модуля «$1»...",
        "apisandbox-no-parameters": "Гэты модуль API не мае параметраў.",
        "apisandbox-helpurls": "Спасылкі на даведку",
        "apisandbox-examples": "Прыклады",
        "activeusers-count": "$1 {{PLURAL:$1|дзеянне|дзеянні|дзеянняў}} за апошні{{PLURAL:$3| дзень|я $3 дні|я $3 дзён}}",
        "activeusers-from": "Паказ, пачынаючы з:",
        "activeusers-groups": "Паказаць удзельнікаў, якія належаць да груп:",
+       "activeusers-excludegroups": "Выключыць удзельнікаў, якія належаць да груп:",
        "activeusers-noresult": "Няма такіх удзельнікаў.",
        "activeusers-submit": "Паказаць актыўных удзельнікаў",
        "listgrouprights": "Дазволы для груп удзельнікаў",
        "watchlistanontext": "Каб паглядзець ці змяніць спіс назірання, трэба прадставіцца сістэме.",
        "watchnologin": "Без прадстаўлення",
        "addwatch": "Дадаць у спіс назірання",
-       "addedwatchtext": "Старонка \"[[:$1]]\" была дададзена да [[Special:Watchlist|назіраных]] вамі.\nЗмяненні, якія адбудуцца з гэтай старонкай і з размовамі пра яе, будуць паказвацца ў спісе назірання.",
+       "addedwatchtext": "Старонка \"[[:$1]]\" і яе размовная старонка былі дададзены да [[Special:Watchlist|спісу назірання]].",
+       "addedwatchtext-talk": "Старонка \"[[:$1]]\" і яе звязаная старонка былі дададзены да [[Special:Watchlist|спісу назірання]].",
        "addedwatchtext-short": "Старонка \"$1\" была дададзена ў ваш спіс назірання.",
        "removewatch": "Выдаліць са спісу назірання",
        "removedwatchtext": "Старонка \"[[:$1]]\" была вынята з Вашага [[Special:Watchlist|спісу назірання]].",
        "mycontris": "Уклад",
        "anoncontribs": "Уклад",
        "contribsub2": "Для $1 ($2)",
+       "contributions-subtitle": "Для {{GENDER:$3|$1}}",
        "contributions-userdoesnotexist": "Уліковы запіс удзельніка \"$1\" не зарэгістраваны.",
        "nocontribs": "Не знойдзена змен, адпаведных зададзеным параметрам.",
        "uctop": "апошн.",
        "ipbreason": "Прычына:",
        "ipbreason-dropdown": "*Звычайныя прычыны блоку\n** Упісванне несапраўднай інфармацыі\n** Выдаленне інфармацыі са старонак\n** Спамавыя спасылкі на вонкавыя сайты\n** Упісванне бессэнсоўнай інфармацыі\n** Некультурныя паводзіны/пераследаванне\n** Злоўжыванне некалькімі рахункамі\n** Недапушчальнае імя ўдзельніка",
        "ipb-hardblock": "Забараніць зарэгістраваным удзельнікам рэдагаванне з гэтага IP-адраса",
-       "ipbcreateaccount": "Ð\9dе Ð´Ð°Ð·Ð²Ð°Ð»Ñ\8fÑ\86Ñ\8c Ñ\81Ñ\82ваÑ\80Ñ\8dнне Ñ\80аÑ\85Ñ\83нка",
-       "ipbemailban": "Ð\9dе Ð´Ð°Ð·Ð²Ð°Ð»Ñ\8fÑ\86Ñ\8c Ñ\83дзелÑ\8cнÑ\96кÑ\83 Ñ\81лаÑ\86Ñ\8c Ñ\8dл.поÑ\88Ñ\82Ñ\83",
+       "ipbcreateaccount": "СÑ\82ваÑ\80Ñ\8dнне Ñ\80аÑ\85Ñ\83нкаÑ\9e",
+       "ipbemailban": "Ð\90дпÑ\80аÑ\9eка Ñ\8dл. Ð¿Ð¾Ñ\88Ñ\82Ñ\8b",
        "ipbenableautoblock": "Аўтаматычна блакаваць адрас IP, якім удзельнік карыстаўся апошнім разам, і ўсе наступныя адрасы IP, з-пад якіх ён паспрабуе рабіць праўкі",
        "ipbsubmit": "Заблакаваць удзельніка",
        "ipbother": "Іншы час",
        "ipboptions": "2 гадзіны:2 hours,1 дзень:1 day,3 дні:3 days,1 тыдзень:1 week,2 тыдні:2 weeks,1 месяц:1 month,3 месяцы:3 months,6 месяцаў:6 months,1 год:1 year,назаўсёды:infinite",
        "ipbhidename": "Не паказваць імя ўдзельніка ў гісторыях правак і ў спісах",
        "ipbwatchuser": "Назіраць уласную і размоўную старонкі гэтага ўдзельніка",
-       "ipb-disableusertalk": "Ð\97абаÑ\80анÑ\96Ñ\86Ñ\8c Ð³Ñ\8dÑ\82амÑ\83 Ñ\9eдзелÑ\8cнÑ\96кÑ\83 Ñ\80Ñ\8dдагаваÑ\86Ñ\8c Ñ\81ваÑ\8e Ñ\81Ñ\82аÑ\80онкÑ\83 Ñ\80азмоÑ\9e Ð¿Ð°Ð´Ñ\87аÑ\81 Ð±Ð»Ð°ÐºÑ\96Ñ\80оÑ\9eкÑ\96",
+       "ipb-disableusertalk": "РÑ\8dдагаванне Ñ\81ваÑ\91й Ñ\81Ñ\82аÑ\80онкÑ\96 Ñ\80азмоÑ\9e",
        "ipb-change-block": "Змяніць настройкі блакіравання ўдзельніка",
        "ipb-confirm": "Пацвердзіць блакіроўку",
        "ipb-sitewide": "Ва ўсім праекце",
        "block-actions": "Дзеянні для блакіроўкі:",
        "block-expiry": "Згасае:",
        "block-options": "Дадатковыя опцыі:",
+       "block-prevent-edit": "Праўка",
+       "block-reason": "Прычына:",
        "block-target": "Удзельнік або адрас IP:",
        "unblockip": "Зняць блок з удзельніка",
        "unblockiptext": "З дапамогай формы ніжэй можна вярнуць дазвол на праўкі для раней заблакіраванага IP-адраса або ўдзельніка.",
        "autoblocklist": "Аўтаблакіроўкі",
        "autoblocklist-submit": "Шукаць",
        "autoblocklist-legend": "Спіс аўтаблакіровак",
+       "autoblocklist-localblocks": "{{PLURAL:$1|Лакальная аўтаблакіроўка|Лакальныя аўтаблакіроўкі}}",
        "autoblocklist-empty": "Спіс аўтаблакіровак пусты.",
+       "autoblocklist-otherblocks": "{{PLURAL:$1|Іншая аўтаблакіроўка|Іншыя аўтаблакіроўкі}}",
        "ipblocklist": "Заблакаваныя ўдзельнікі",
        "ipblocklist-legend": "Знайсці заблакаванага ўдзельніка",
        "blocklist-userblocks": "Схаваць блакіроўкі ўліковых запісаў",
        "blocklist-tempblocks": "Схаваць часавыя блакіроўкі",
        "blocklist-addressblocks": "Схаваць блакаванні асобных IP",
+       "blocklist-type": "Тып:",
+       "blocklist-type-opt-all": "Усе",
+       "blocklist-type-opt-sitewide": "Ва ўсім праекце",
+       "blocklist-type-opt-partial": "Частковая",
        "blocklist-rangeblocks": "Схаваць блакіроўкі дыяпазонаў",
        "blocklist-timestamp": "Дата/час",
        "blocklist-target": "Мэта",
        "createaccountblock": "стварэнне рахунка заблакавана",
        "emailblock": "эл.пошта заблакавана",
        "blocklist-nousertalk": "не мае дазволу правіць уласную старонку размоў",
+       "blocklist-editing": "праўка",
+       "blocklist-editing-sitewide": "праўка (ва ўсім праекце)",
+       "blocklist-editing-page": "старонкі",
+       "blocklist-editing-ns": "прасторы назваў",
        "ipblocklist-empty": "Спіс блокаў зараз пусты.",
        "ipblocklist-no-results": "Няма блокаў на такі адрас IP або на такое імя ўдзельніка.",
        "blocklink": "заблакаваць",
        "pageinfo-display-title": "Паказаная назва",
        "pageinfo-default-sort": "Прадвызначаны ключ парадкавання",
        "pageinfo-length": "Аб'ём старонкі (у байтах)",
+       "pageinfo-namespace": "Прастора назваў",
        "pageinfo-article-id": "Ідэнтыфікатар старонкі",
        "pageinfo-language": "Мова змесціва старонкі",
        "pageinfo-language-change": "змяніць",
        "previousdiff": "← Папярэдняя праўка",
        "nextdiff": "Наступная праўка →",
        "mediawarning": "'''Увага''': у гэтым тыпе файлаў бывае зламысны код, выкананне якога можа паставіць пад небяспеку вашу сістэму.",
-       "imagemaxsize": "Ð\9cÑ\8fжа Ð¿Ð°Ð¼ÐµÑ\80Ñ\83 Ð²Ñ\8bÑ\8fваÑ\9e:<br />''(на Ñ\82лÑ\83маÑ\87алÑ\8cнÑ\8bÑ\85 Ñ\81Ñ\82аÑ\80онкаÑ\85)''",
+       "imagemaxsize": "Ð\90бмежаванне Ð¿Ð°Ð¼ÐµÑ\80Ñ\83 Ð²Ñ\8bÑ\8fваÑ\9e Ð½Ð° Ñ\82лÑ\83маÑ\87алÑ\8cнÑ\8bÑ\85 Ñ\81Ñ\82аÑ\80онкаÑ\85 Ñ\84айлаÑ\9e:",
        "thumbsize": "Памеры драбніцы:",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|старонка|старонак}}",
        "file-info": "аб'ём файла: $1, тып MIME: $2",
        "confirmemail_invalidated": "Пацверджанне эл.пошты скасаванае",
        "invalidateemail": "Адмовіцца ад пацверджання эл.пошты",
        "notificationemail_subject_changed": "Адрас электроннай пошты на пляцоўцы {{SITENAME}} зменены",
+       "notificationemail_subject_removed": "Адрас электроннай пошты на пляцоўцы {{SITENAME}} выдалены",
        "scarytranscludedisabled": "[Устаўлянне з іншых вікі не дазволена]",
        "scarytranscludefailed": "[Не ўдалося атрымаць шаблон для $1]",
        "scarytranscludefailed-httpstatus": "[Не ўдалося атрымаць шаблон для $1: HTTP $2]",
        "confirm-unwatch-top": "Выняць гэту старонку з Вашага спісу назірання?",
        "confirm-rollback-button": "Добра",
        "confirm-rollback-top": "Адкаціць праўкі гэтай старонкі?",
+       "confirm-mcrrestore-title": "Аднавіць версію",
+       "confirm-mcrundo-title": "Адмяніць змяненне",
+       "mcrundofailed": "Адмяніць не атрымалася",
+       "mcrundo-missingparam": "У запыце адсутнічаюць абавязковыя параметры.",
        "quotation-marks": "«$1»",
        "imgmultipageprev": "← папярэдняя старонка",
        "imgmultipagenext": "наступная старонка →",
        "version-specialpages": "Адмысловыя старонкі",
        "version-parserhooks": "Хукі парсера",
        "version-variables": "Зменныя",
+       "version-editors": "Рэдактары",
        "version-antispam": "Абарона ад спаму",
        "version-other": "Рознае",
        "version-mediahandlers": "Апрацоўнікі мультымедый",
        "compare-invalid-title": "Абраная вамі назва недапушчальная.",
        "compare-title-not-exists": "Не існуе названай вамі назвы.",
        "compare-revision-not-exists": "Паказанай вамі версіі не існуе.",
-       "diff-form": "'''фармуляр'''",
+       "diff-form": "Адрозненні",
        "diff-form-oldid": "Стары ідэнтыфікатар версіі (неабавязкова)",
        "diff-form-submit": "Паказаць адрозненні",
        "permanentlink": "Пастаянная спасылка",
        "logentry-block-block": "$1 заблакірава{{GENDER:$2|ў|ла}} {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|разблакаваў|разблакавала}} {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|памяняў|памяняла}} настройкі блакіроўкі {{GENDER:$4|$3}} на перыяд $5 $6",
+       "logentry-partialblock-block-page": "{{PLURAL:$1|старонка|старонкі}} $2",
        "logentry-suppress-block": "$1 {{GENDER:$2|заблакіраваў|заблакіравала}} {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-suppress-reblock": "$1 {{GENDER:$2|памяняў|памяняла}} параметры блакіроўкі {{GENDER:$4|$3}} на перыяд $5 $6",
        "logentry-import-upload": "$1 {{GENDER:$2|імпартаваў|імпартавала}} $3 праз укладанне файла",
        "logentry-protect-protect-cascade": "$1 {{GENDER:$2|абараніў|абараніла}} $3 $4 [каскадна]",
        "logentry-protect-modify": "$1 {{GENDER:$2|змяніў узровень|змяніла ўзровень}} аховы $3 $4",
        "logentry-protect-modify-cascade": "$1 {{GENDER:$2|змяніў узровень|змяніла ўзровень}} аховы $3 $4 [каскадна]",
-       "logentry-rights-rights": "$1 {{GENDER:$2|змяніў|змяніла}} прыналежнасць $3 да групы з $4 на $5",
+       "logentry-rights-rights": "$1 {{GENDER:$2|змяніў|змяніла}} прыналежнасць {{GENDER:$6|$3}} да групы з $4 на $5",
        "logentry-rights-rights-legacy": "$1 {{GENDER:$2|змяніў|змяніла}} прыналежнасць $3 да групы",
        "logentry-rights-autopromote": "$1 аўтаматычна {{GENDER:$2|пераведзены|пераведзена}} з $4 у $5",
        "logentry-upload-upload": "$1 {{GENDER:$2|уклаў|уклала}} $3",
        "pagelang-select-lang": "Выберыце мову",
        "pagelang-reason": "Прычына",
        "pagelang-submit": "Адправіць",
+       "pagelang-nonexistent-page": "Старонка $1 не існуе.",
        "right-pagelang": "Змяняць мову старонкі",
        "action-pagelang": "змяняць мову старонкі",
        "log-name-pagelang": "Журнал змянення мовы",
        "mw-widgets-abandonedit": "Вы сапраўды жадаеце выйсці з рэжыму рэдагавання, не захаваўшы змены?",
        "mw-widgets-abandonedit-discard": "Адмяніць змены",
        "mw-widgets-abandonedit-keep": "Працягнуць рэдагаванне",
+       "mw-widgets-abandonedit-title": "Вы ўпэўненыя?",
+       "mw-widgets-copytextlayout-copy": "Капіраваць",
        "mw-widgets-dateinput-no-date": "Дата не выбрана",
        "mw-widgets-mediasearch-input-placeholder": "Пошук мультымедыя",
+       "mw-widgets-mediasearch-noresults": "Нічога не знойдзена.",
        "mw-widgets-titleinput-description-new-page": "старонка яшчэ не існуе",
        "mw-widgets-titleinput-description-redirect": "перанакіраванне на $1",
+       "mw-widgets-categoryselector-add-category-placeholder": "Дадаць катэгорыю...",
        "mw-widgets-usersmultiselect-placeholder": "Дадаць яшчэ...",
+       "mw-widgets-titlesmultiselect-placeholder": "Дадаць яшчэ...",
        "date-range-from": "Ад даты:",
        "date-range-to": "Да даты:",
        "randomrootpage": "Выпадковая карэнная старонка",
        "log-action-filter-import": "Тып імпарту:",
        "log-action-filter-managetags": "Дзеянне па кіраванні біркамі:",
        "log-action-filter-move": "Тып пераносу:",
+       "log-action-filter-newusers": "Тып стварэння рахунку:",
        "log-action-filter-patrol": "Тып догляду:",
        "log-action-filter-protect": "Тып аховы:",
        "log-action-filter-rights": "Тып змены правоў:",
+       "log-action-filter-suppress": "Тып хавання:",
        "log-action-filter-upload": "Тып загрузкі:",
        "log-action-filter-all": "Усе",
        "log-action-filter-block-block": "заблакаваць",
        "log-action-filter-block-reblock": "Змена блакіроўкі",
        "log-action-filter-block-unblock": "Разблакаваць",
-       "log-action-filter-contentmodel-change": "Змяненне Contentmodel",
-       "log-action-filter-contentmodel-new": "Стварэнне старонкі з нестандартным Contentmodel",
+       "log-action-filter-contentmodel-change": "Змяненне мадэлі змесціва",
+       "log-action-filter-contentmodel-new": "Стварэнне старонкі з нестандартнай мадэллю змесціва",
        "log-action-filter-delete-delete": "Выдаленне старонкі",
        "log-action-filter-delete-restore": "Узнаўленне старонкі",
        "log-action-filter-delete-event": "Выдаленне лога",
        "userjsispublic": "Звярніце ўвагу: укладзеныя JavaScript-старонкі не павінны ўтрымліваць канфідэнцыйныя звесткі, бо іх могуць бачыць іншыя ўдзельнікі.",
        "usercssispublic": "Звярніце ўвагу: укладзеныя CSS-старонкі не павінны ўтрымліваць канфідэнцыйныя звесткі, бо іх могуць бачыць іншыя ўдзельнікі.",
        "restrictionsfield-badip": "Няслушны IP-адрас ці дыяпазон: $1",
-       "restrictionsfield-label": "Дазволеныя дыяпазоны IP-адрасоў:"
+       "restrictionsfield-label": "Дазволеныя дыяпазоны IP-адрасоў:",
+       "edit-error-short": "Памылка: $1",
+       "edit-error-long": "Памылкі:\n\n$1",
+       "specialmute-submit": "Пацвердзіць",
+       "revid": "версія $1",
+       "pageid": "ID старонкі $1",
+       "pagedata-title": "Дадзеныя старонкі",
+       "passwordpolicies-policyflag-forcechange": "мусіць быць зменены пры ўваходзе",
+       "passwordpolicies-policyflag-suggestchangeonlogin": "прапанаваць змяненне пры ўваходзе"
 }
index babd4a1..a6a5b20 100644 (file)
        "autoblockedtext": "Vaše IP adresa byla automaticky zablokována, protože ji používal jiný uživatel, kterého zablokoval $1.\nUdaný důvod blokování:\n\n:<em>$2</em>\n\n* Začátek blokování: $8\n* Konec blokování: $6\n* Původně blokovaný uživatel: $7\n\nZablokování můžete prodiskutovat se správcem $1 nebo některým z dalších [[{{MediaWiki:Grouppage-sysop}}|správců]].\n\nUvědomte si však, že funkci „{{int:emailuser}}“ nemůžete použít, pokud nemáte ve svém [[Special:Preferences|uživatelském nastavení]] zadaný platný e-mail a nebylo vám zablokováno jeho užívání.\n\nVaše současná IP adresa je $3, číslo vašeho zablokování je #$5.\nProsíme, uveďte tyto údaje při komunikaci se správci.",
        "systemblockedtext": "Vaše IP adresa byla automaticky zablokována softwarem MediaWiki.\nUdaný důvod blokování:\n\n:<em>$2</em>\n\n* Začátek blokování: $8\n* Konec blokování: $6\n* Původně blokovaný uživatel: $7\n\nVaše současná IP adresa je $3.\nProsíme, uveďte tyto údaje při komunikaci se správci.",
        "blockednoreason": "důvod nebyl zadán",
-       "blockedtext-composite": "<strong>Vaše uživatelské jméno nebo IP adresa byla zablokována.</strong>\n\nUdaný důvod blokování:\n\n:<em>$2</em>\n\n* Začátek blokování: $8\n* Konec nejdelšího blokování: $6\n\nVaše současná IP adresa je $3.\nProsíme, uveďte tyto údaje při komunikaci se správci.",
+       "blockedtext-composite": "<strong>Vaše uživatelské jméno nebo IP adresa byla zablokována.</strong>\n\nUdaný důvod blokování:\n\n:<em>$2</em>\n\n* Začátek blokování: $8\n* Konec nejdelšího blokování: $6\n\n* $5\n\nVaše současná IP adresa je $3.\nProsíme, uveďte tyto údaje při komunikaci se správci.",
        "blockedtext-composite-reason": "Na váš účet a/nebo vaši IP adresu se vztahuje více blokování.",
        "whitelistedittext": "Pro editaci se musíte $1.",
        "confirmedittext": "Pro editaci stránek je vyžadováno potvrzení vaší e-mailové adresy.\nNa stránce [[Special:Preferences|nastavení]] zadejte a nechte potvrdit svou e-mailovou adresu.",
        "undeletehistory": "Pokud stránku obnovíte, budou  v historii obnoveny všechny verze. Pokud byla vytvořena nová stránka se stejným jménem jako smazaná, obnovené verze se zapíší na starší místo v historii novější stránky.",
        "undeleterevdel": "Obnovení nebude provedeno, pokud by vedlo k částečnému odstranění aktuální verze stránky. V takovém případě musíte odznačit nebo odkrýt nejnovější smazanou verzi.",
        "undeletehistorynoadmin": "Tato stránka byla smazána. Důvod smazání je uveden níže, spolu s informacemi o uživatelích, kteří tuto stránku před smazáním editovali. Samotný text stránky je dostupný pouze správcům.",
-       "undelete-revision": "Smazaná verze stránky $1 (z $4 dne $5) od uživatele $3:",
+       "undelete-revision": "Smazaná verze stránky $1 (z $5, $4) od uživatele $3:",
        "undeleterevision-missing": "Nesprávná nebo chybějící revize. Možná máte špatný odkaz, nebo revize byla obnovena či odstraněna z archivu.",
        "undeleterevision-duplicate-revid": "Nebylo možné obnovit {{PLURAL:$1|jednu revizi|$1 revize|$1 revizí}}, protože {{PLURAL:$1|její|jejich}} <code>rev_id</code> již {{PLURAL:$1|bylo obsazeno|byla obsazena}}.",
        "undelete-nodiff": "Nebyla nalezena žádná předchozí verze.",
index 1c510f3..495cdd6 100644 (file)
        "autoblockedtext": "Deine IP-Adresse wurde automatisch gesperrt, da sie von einem anderen Benutzer genutzt wurde, der von $1 gesperrt wurde.\nAls Grund wurde angegeben:\n\n:''$2''\n\n* Beginn der Sperre: $8\n* Ende der Sperre: $6\n* Sperre betrifft: $7\n\nDu kannst $1 oder einen der anderen [[{{MediaWiki:Grouppage-sysop}}|Administratoren]] kontaktieren, um über die Sperre zu diskutieren.\n\nDu kannst die „{{int:emailuser}}“-Funktion nicht nutzen, solange keine gültige E-Mail-Adresse in deinen [[Special:Preferences|Benutzerkonto-Einstellungen]] eingetragen ist oder diese Funktion für dich gesperrt wurde.\n\nDeine aktuelle IP-Adresse ist $3, und die Sperr-ID ist $5.\nBitte füge alle Informationen jeder Anfrage hinzu, die du stellst.",
        "systemblockedtext": "Dein Benutzername oder deine IP-Adresse wurde von MediaWiki automatisch gesperrt.\nDer angegebene Grund ist:\n\n:<em>$2</em>\n\n* Beginn der Sperre: $8\n* Ablauf der Sperre: $6\n* Sperre betrifft: $7\n\nDeine aktuelle IP-Adresse ist $3.\nBitte gib alle oben stehenden Details in jeder Anfrage an.",
        "blockednoreason": "keine Begründung angegeben",
-       "blockedtext-composite": "<strong>Dein Benutzername oder deine IP-Adresse wurde gesperrt.</strong>\n\nDer Angegebene Grund ist:\n\n:<em>$2</em>\n\n* Beginn der Sperre: $8\n* Ablauf der längsten Sperre: $6\n\nDeine aktuelle IP-Adresse ist $3.\nBitte gib alle oben stehenden Details in jeder Anfrage an.",
+       "blockedtext-composite": "<strong>Dein Benutzername oder deine IP-Adresse wurde gesperrt.</strong>\n\nDer Angegebene Grund ist:\n\n:<em>$2</em>\n\n* Beginn der Sperre: $8\n* Ablauf der längsten Sperre: $6\n\n* $5\n\nDeine aktuelle IP-Adresse ist $3.\nBitte gib alle oben stehenden Details in jeder Anfrage an.",
        "blockedtext-composite-reason": "Es gibt mehrere Sperren gegen dein Benutzerkonto und/oder deine IP-Adresse",
        "whitelistedittext": "Du musst dich $1, um Seiten bearbeiten zu können.",
        "confirmedittext": "Du musst deine E-Mail-Adresse erst bestätigen, bevor du Bearbeitungen durchführen kannst. Bitte ergänze und bestätige deine E-Mail in den [[Special:Preferences|Einstellungen]].",
        "logentry-block-block": "$1 {{GENDER:$2|sperrte}} {{GENDER:$4|$3}} für eine Dauer von $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|hob}} die Sperre von {{GENDER:$4|$3}} auf",
        "logentry-block-reblock": "$1 {{GENDER:$2|änderte}} die Sperreinstellungen für {{GENDER:$4|$3}} mit einer Sperrdauer von $5 $6",
-       "logentry-partialblock-block-page": "{{PLURAL:$1|der Seite|den Seiten}} $2",
+       "logentry-partialblock-block-page": "der {{PLURAL:$1|Seite|Seiten}} $2",
        "logentry-partialblock-block-ns": "{{PLURAL:$1|des Namensraums|der Namensräume}} $2",
-       "logentry-partialblock-block": "$1 {{GENDER:$2|sperrte}} {{GENDER:$4|$3}} für das Bearbeiten $7 mit einer Ablaufzeit von $5 $6",
+       "logentry-partialblock-block": "$1 {{GENDER:$2|sperrte}} {{GENDER:$4|$3}} vom Bearbeiten $7 für eine Dauer von $5 $6",
        "logentry-partialblock-reblock": "$1 {{GENDER:$2|änderte}} die Sperreinstellungen für {{GENDER:$4|$3}} zum Verhindern von Bearbeitungen auf $7 mit einer Ablaufzeit von $5 $6",
        "logentry-non-editing-block-block": "$1 {{GENDER:$2|sperrte}} {{GENDER:$4|$3}} für angegebene Nicht-Bearbeitungsaktionen mit einer Ablaufzeit von $5 $6",
        "logentry-non-editing-block-reblock": "$1 {{GENDER:$2|änderte}} die Sperreinstellungen für {{GENDER:$4|$3}} für angegebene Nicht-Bearbeitungsaktionen mit einer Ablaufzeit von $5 $6",
index bf9f4ff..d654a52 100644 (file)
        "right-editmyusercss": "Edit your own user CSS files",
        "right-editmyuserjson": "Edit your own user JSON files",
        "right-editmyuserjs": "Edit your own user JavaScript files",
+       "right-editmyuserjsredirect": "Edit your own user JavaScript files that are redirects",
        "right-viewmywatchlist": "View your own watchlist",
        "right-editmywatchlist": "Edit your own watchlist. Note some actions will still add pages even without this right.",
        "right-viewmyprivateinfo": "View your own private data (e.g. email address, real name)",
        "action-editmyusercss": "edit your own user CSS files",
        "action-editmyuserjson": "edit your own user JSON files",
        "action-editmyuserjs": "edit your own user JavaScript files",
+       "action-editmyuserjsredirect": "edit your own user JavaScript files that are redirects",
        "action-viewsuppressed": "view revisions hidden from any user",
        "action-hideuser": "block a username, hiding it from the public",
        "action-ipblock-exempt": "bypass IP blocks, auto-blocks and range blocks",
        "specialmute-success": "Your mute preferences have been updated. See all muted users in [[Special:Preferences|your preferences]].",
        "specialmute-submit": "Confirm",
        "specialmute-label-mute-email": "Mute emails from this user",
-       "specialmute-header": "Please select your mute preferences for {{BIDI:[[User:$1]]}}.",
+       "specialmute-header": "Please select your mute preferences for user <b>{{BIDI:[[User:$1|$1]]}}</b>.",
        "specialmute-error-invalid-user": "The username requested could not be found.",
-       "specialmute-error-email-blacklist-disabled": "Muting users from sending you emails is not enabled.",
-       "specialmute-error-email-preferences": "You must confirm your email address before you can mute a user. You may do so from [[Special:Preferences]].",
-       "specialmute-email-footer": "To manage email preferences for {{BIDI:$2}} please visit <$1>.",
+       "specialmute-error-no-options": "Mute features are unavailable. This might be because: you haven't confirmed your email address or the wiki administrator has disabled email features and/or email blacklist for this wiki.",
+       "specialmute-email-footer": "To manage email preferences for user {{BIDI:$2}} please visit <$1>.",
        "specialmute-login-required": "Please log in to change your mute preferences.",
        "mute-preferences": "Mute preferences",
        "revid": "revision $1",
        "passwordpolicies-policy-passwordnotinlargeblacklist": "Password cannot be in the list of 100,000 most commonly used passwords.",
        "passwordpolicies-policyflag-forcechange": "must change on login",
        "passwordpolicies-policyflag-suggestchangeonlogin": "suggest change on login",
+       "mycustomjsredirectprotected": "You do not have permission to edit this JavaScript page because it is a redirect and it does not point inside your userspace.",
        "easydeflate-invaliddeflate": "Content provided is not properly deflated",
        "unprotected-js": "For security reasons JavaScript cannot be loaded from unprotected pages. Please only create javascript in the MediaWiki: namespace or as a User subpage",
        "userlogout-continue": "Do you want to log out?"
index b1bc55d..378ab0f 100644 (file)
        "autoblockedtext": "Via IP-adreso estas aŭtomate forbarita, ĉar uzis ĝin alia uzanto, kiun baris $1.\nLa donita kialo estas jena:\n\n:<em>$2</em>\n\n*Komenco de forbaro: $8\n*Limdato de la blokado: $6\n*Intencis forbari uzanton: $7\n\nVi povas kontakti $1 aŭ iun ajn el la aliaj [[{{MediaWiki:Grouppage-sysop}}|administrantojn]] por diskuti la blokon.\n\nNotu, ke vi ne povas uzi la servon \"{{int:emailuser}}\" krom se vi havas validan retpoŝt-adreson registritan en viaj [[Special:Preferences|preferojn]], kaj vi estas ne blokita kontraŭ ĝia uzado.\n\nVia nuna IP-adreso estas $3, kaj la forbaro-identigo estas $5.\nBonvolu inkluzivi tiujn detalojn en iuj ajn demandoj kiun vi farus.",
        "systemblockedtext": "Via salutnomo aŭ IPa adreso estis aŭtomate forbarita de MediaWiki.\nLa kialo donita estas:\n\n:<em>$2</em>\n\n* Komenco de forbaro: $8\n* Eksvalidiĝo de forbaro: $6\n* Intenca forbarulo: $7\n\nVia nuna IP-adreso estas $3.\nBonvolu inkluzivi ĉiujn suprajn detalojn en ajnaj demandoj kiujn vi faras.",
        "blockednoreason": "neniu kialo estis donita",
-       "blockedtext-composite": "<strong>Oni forbaris vian salutnomon aŭ IP-adreson.</strong>\n\nLa donita kialo estas:\n\n:<em>$2</em>.\n\n* Komenco de forbaro: $8\n* Fino de plej longa forbaro: $6\n\nVia aktuala IP-adreso estas $3.\nPlease include all above details in any queries you make.",
+       "blockedtext-composite": "<strong>Oni forbaris vian salutnomon aŭ IP-adreson.</strong>\n\nLa donita kialo estas:\n\n:<em>$2</em>.\n\n* Komenco de forbaro: $8\n* Fino de plej longa forbaro: $6\n\n* $5\n\nVia aktuala IP-adreso estas $3.\nPlease include all above details in any queries you make.",
        "blockedtext-composite-reason": "Estas pluraj forbaroj kontraŭ via konto kaj/aŭ IP-adreso",
        "whitelistedittext": "Vi devas $1 por redakti paĝojn.",
        "confirmedittext": "Vi devas konfirmi vian retpoŝtan adreson antaŭ ol redakti paĝojn. Bonvolu agordi kaj validigi vian retadreson per viaj [[Special:Preferences|preferoj]].",
index 75ecf03..6a31fae 100644 (file)
        "createacct-another-realname-tip": "El nombre real es opcional.\nSi lo proporciona, se usará para dar atribución al trabajo del usuario.",
        "changeemail-password": "Su contraseña en {{SITENAME}}:",
        "anoneditwarning": "<strong>Advertencia:</strong> no ha iniciado sesión. Su dirección IP se hará pública si hace cualquier edición en estas condiciones. Si <strong>[$1 inicia sesión]</strong> o <strong>[$2 crea una cuenta]</strong>, sus ediciones se atribuirán a su nombre de usuario, además de otros beneficios.",
+       "blockedtext-composite-no-ids": "Su dirección IP figura en varias listas negras",
        "newarticletext": "Ha seguido usted un enlace a una página que aún no existe.\nPara crear esta página, escriba en el campo a continuación. Para más información, consulte la [$1 página de ayuda].\nSi ha llegado aquí por error, vuelva a la página anterior.",
        "noarticletext": "En este momento no hay texto en esta página.\nPuede [[Special:Search/{{PAGENAME}}|buscar el título de esta página]] en otras páginas,\n<span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} buscar en los registros relacionados],\no [{{fullurl:{{FULLPAGENAME}}|action=edit}} crear esta página]</span>.",
        "copyrightwarning": "Por favor observe que todas las contribuciones realizadas en {{SITENAME}} serán consideradas como liberadas bajo $2 (véase $1 para más detalles).\nSi no desea que sus escritos sean editados o redistribuídos a voluntad, entonces no contribuya aquí.<br />\nAl mismo tiempo está usted prometiendo que lo que usted va a enviar lo ha escrito usted, o copiado de una fuente de dominio público.\n'''¡No envíe textos con derechos de autor sin el debido permiso!'''",
index 55773f5..5f6c846 100644 (file)
                        "Johny Weissmuller Jr"
                ]
        },
-       "tog-underline": "Enlaces que se van a subrayar:",
+       "tog-underline": "Subrayar enlaces:",
        "tog-hideminor": "Ocultar las ediciones menores en cambios recientes",
        "tog-hidepatrolled": "Ocultar las ediciones verificadas de cambios recientes",
        "tog-newpageshidepatrolled": "Ocultar, de la lista de páginas nuevas, las páginas verificadas",
        "autoblockedtext": "Tu dirección IP ha sido bloqueada automáticamente porque fue utilizada por otro usuario, que resultó bloqueado por $1.\nEl motivo dado es el siguiente:\n\n:<em>$2</em>\n\n* Inicio del bloqueo: $8\n* Caducidad del bloqueo: $6\n* Bloqueo destinado a: $7\n\nPuedes contactar con $1 o con otro de los [[{{MediaWiki:Grouppage-sysop}}|administradores]] para discutir el bloqueo.\n\nObserva que no puedes utilizar la función «{{int:emailuser}}» a menos que hayas registrado una dirección de correo electrónico válida en tus [[Special:Preferences|preferencias de usuario]] y la función no haya sido también bloqueada.\n\nTu dirección IP actual es $3, y el identificador del bloqueo es n.º $5.\nIncluye todos los datos aquí mostrados en cualquier consulta que hagas.",
        "systemblockedtext": "Tu nombre de usuario o dirección IP ha sido bloqueado automáticamente por el software MediaWiki.\nLa razón dada es:\n\n:<em>$2</em>\n\n* Inicio del bloqueo: $8\n* Caducidad de bloqueo: $6\n* Destinatario del bloqueo: $7\n\nTu dirección IP actual es $3.\nPor favor, incluye todos los datos aquí mostrados en cualquier consulta que hagas.",
        "blockednoreason": "no se ha especificado el motivo",
-       "blockedtext-composite": "<strong>Tu nombre de usuario o dirección IP han sido bloqueados.</strong>\n\nLa razón es:\n\n:<em>$2</em>.\n\n* Inicio del bloqueo: $8\n* Vencimiento del bloqueo más largo: $6\n\nTu dirección IP actual es $3.\nPor favor, incluye todos los detalles anteriores en cualquier consulta que realices.",
+       "blockedtext-composite": "<strong>Tu nombre de usuario o dirección IP han sido bloqueados.</strong>\n\nLa razón es:\n\n:<em>$2</em>.\n\n* Inicio del bloqueo: $8\n* Vencimiento del bloqueo más largo: $6\n\n* $5\n\nTu dirección IP actual es $3.\nPor favor, incluye todos los detalles anteriores en cualquier consulta que realices.",
+       "blockedtext-composite-no-ids": "Tu dirección IP figura en varias listas negras",
        "blockedtext-composite-reason": "Hay múltiples bloques contra tu cuenta y/o dirección IP.",
        "whitelistedittext": "Tienes que $1 para editar páginas.",
        "confirmedittext": "Debes confirmar tu dirección de correo electrónico antes de poder editar páginas. Por favor, configura y confirma tu dirección de correo a través de tus [[Special:Preferences|preferencias de usuario]].",
index e8e9086..3245986 100644 (file)
        "autoblockedtext": "Sinu IP-aadress blokeeriti automaatselt, sest seda kasutas teine kasutaja, kelle $1 blokeeris.\nPõhjendus on järgmine:\n\n:<em>$2</em>\n\n* Blokeeringu algus: $8\n* Blokeeringu lõpp: $6\n* Sooviti blokeerida: $7\n\nKüsimuse arutamiseks võid pöörduda kasutaja $1 või mõne teise [[{{MediaWiki:Grouppage-sysop}}|administraatori]] poole.\n\nPane tähele, et sa ei saa kasutada funktsiooni \"{{int:emailuser}}\", kui [[Special:Preferences|konto eelistustes]] pole määratud kehtivat e-posti aadressi või kui sul on keelatud seda funktsiooni kasutada.\n\nSinu praegune IP-aadress on $3 ja blokeeringu number #$5. Palun lisa need andmed kõigile järelpärimistele, mida kavatsed teha.",
        "systemblockedtext": "MediaWiki tarkvara on sinu kasutajanime või IP-aadressi automaatselt blokeerinud.\nToodud on järgmine põhjus:\n\n:<em>$2</em>\n\n* Blokeerimisaeg: $8\n* Blokeeringu aegumistähtaeg: $6\n* Sooviti blokeerida: $7\n\nSinu praegune IP-aadress on $3.\nLisa need andmed kõigile järelepärimistele, mida kavatsed teha.",
        "blockednoreason": "põhjendust ei ole kirja pandud",
-       "blockedtext-composite": "<strong>Sinu kasutajanimi või IP-aadress on blokeeritud.</strong>\n\nÄra on toodud järgmine põhjus:\n\n:<em>$2</em>.\n\n* Blokeeringu algus: $8\n* Pikima blokeeringu aegumistähtaeg: $6\n\nSinu praegune IP-aadress on $3.\nPalun lisa need andmed kõigile järelepärimistele, mida kavatsed teha.",
+       "blockedtext-composite": "<strong>Sinu kasutajanimi või IP-aadress on blokeeritud.</strong>\n\nÄra on toodud järgmine põhjus:\n\n:<em>$2</em>.\n\n* Blokeeringu algus: $8\n* Pikima blokeeringu aegumistähtaeg: $6\n\n* $5\n\nSinu praegune IP-aadress on $3.\nPalun lisa need andmed kõigile järelepärimistele, mida kavatsed teha.",
        "blockedtext-composite-reason": "Sinu IP-aadressi ja/või kasutajanime suhtes on kehtestatud mitu blokeeringut",
        "whitelistedittext": "Lehekülgede toimetamiseks pead $1.",
        "confirmedittext": "Lehekülgi ei saa toimetada enne e-posti aadressi kinnitamist.\nPalun määra ja kinnita e-posti aadress [[Special:Preferences|eelistuste leheküljel]].",
index c6ca43e..9110c93 100644 (file)
        "subject-preview": "Gaiaren aurrebista:",
        "previewerrortext": "Errore bat gertatu da aldaketak aurrezten saiatzean.",
        "blockedtitle": "Erabiltzailea blokeatuta dago",
-       "blocked-email-user": "<strong> Zure erabiltzailea blokeatua izan da eta ezin duzu mezurik bidali. Oraindik wiki honetako beste orri batzuk editatu ditzakezu.</strong> Blokeoaren detaile guztiak hemen ikus ditzakezu [[Special:MyContributions|account contributions]].\n\nBlokeoa $1-(e)k egin zuen.\n\nBlokeoaren arrazoiak hauek dira <em>$2</em>.\n\n*Blokeoa hemen hasten da: $8\n*Blokeoa amaitzen da: $6\n*Berariazko blokeoa: $7\n*Blokeoaren ID #$5",
+       "blocked-email-user": "<strong>Zure erabiltzaile izenari blokeoa ezarri zaio, mezuak bidaltzea eragozteko. Hala ere, wiki honetako beste orri batzuk edita ditzakezu.</strong> Blokeoaren xehetasun guztiak hemen dituzu: [[Special:MyContributions|kontuaren ekarpenak]].\n\nBlokeoa ezarri duena: $1.\n\nEmandako arrazoia: <em>$2</em>.\n\n*Blokeoaldiaren hasiera: $8\n*Blokeoaldiaren amaiera: $6\n*Blokeatua: $7\n*Blokeoaren identifikazioa: #$5",
        "blockedtext-partial": "<strong>Zure erabiltzailea edo IP helbidea blokeatua izan da eta ezin du aldaketarik egin orrialde honetan. Wiki honetako beste orrialdeak oraindik editatu ditzakezu.</strong> Blokeoaren xehetasun guztiak hemen ikus ditzakezu: [[Special:MyContributions|account contributions]].\n\nBlokeoa $1-(e)k egin du.\n\nBlokeoaren arrazoia <em>$2</em> da.\n\n* Blokeoaren hasiera: $8\n* Blokeoaren amaiera: $6\n* Berariazko blokeoa: $7\n* Blokeoaren IDa #$5 da.",
        "blockedtext": "<strong> Zure erabiltzaile izena edo IP helbidea blokeatuta dago. </strong>\n\nBlokeoa $1-ek ezarri du.\nEmandako arrazoia hau da: ''$2''.\n\n* Blokeoaren hasiera: $8\n* Blokeoaren bukaera: $6\n* Blokeatua: $7\n\nBlokeoari buruz eztabaidatzeko, jo ezazu $1 edo beste [[{{MediaWiki:Grouppage-sysop}}|administratzaile]] batengana.\n\"{{int:emailuser}}\" tresna erabili ahal izateko, ezinbestekoa da zure [[Special:Preferences|hobespenetan]] baliozkoa den helbide elektroniko bat emanda izatea, eta tresna hori erabiltzeko aukera blokeatu ez izana.\nOrain duzun IP helbidea $3 da, eta blokeoaren zenbakia #$5.\nMesedez, eman aipatutako datu hauek guztiak, blokeoari buruzko edozein eskaera egitean.",
-       "autoblockedtext": "Zure IP helbidea automatikoki blokeaturik dago, $1-ek blokeatutako beste erabiltzaile batek erabili zuelako. Emandako arrazoia hau da:\n\n:<em>$2</em>\n\n* Blokeoaren hasiera: $8\n* Blokeoaren bukaera: $6\n* Blokeatua: $7\n\nBlokeoari buruz eztabaidatzeko, jo ezazu $1 edo beste [[{{MediaWiki:Grouppage-sysop}}|administratzaile]] batengana.\n\n\"{{int:emailuser}}\" tresna erabili ahal izateko, ezinbestekoa da zure [[Special:Preferences|hobespenetan]] baliozkoa den helbide elektroniko bat emanda izatea, eta tresna hori erabiltzeko aukera blokeatu ez izana.\n\nOrain duzun IP helbidea $3 da, eta blokeoaren zenbakia #$5.\n\nMesedez, eman itzazu aipatutako datu guztiak, blokeoari buruzko edozein eskaera egitean.",
-       "systemblockedtext": "Zure erabiltzaile izena edo IP helbidea MediaWiki-k automatikoki blokeatu du.\nHau da emandako arrazoia:\n\n:<em>$2</em>\n\n*Bloke sarrera: $8\n*Blokearen iraungintzea: $6\n*Blokeo helburua: $7\n\nZure uneko IP helbidea: $3.\nSartu goiko xehetasun guztiak egiten dituzun kontsulta guztietan mesedez.",
+       "autoblockedtext": "Zure IP helbidea automatikoki blokeaturik dago, $1 administratzaileak blokeatutako beste erabiltzaile batek erabili zuelako. Emandako arrazoia hau da:\n\n:<em>$2</em>\n\n* Blokeoaldiaren hasiera: $8\n* Blokeoaldiaren bukaera: $6\n* Blokeatua: $7\n\nBlokeoari buruz eztabaidatzeko, jo ezazu $1 administratzailearengana edo [[{{MediaWiki:Grouppage-sysop}}|beste administratzaile batengana]].\n\n\"{{int:emailuser}}\" tresna erabili ahal izateko, ezinbestekoa da zure [[Special:Preferences|hobespenetan]] baliozkoa den helbide elektroniko bat emanda izatea, eta tresna hori erabiltzeko aukera blokeatuta ez izatea.\n\nOrain duzun IP helbidea $3 da. Blokeoaren identifikazio kodea: #$5.\n\nBlokeoari buruzko eskaerarik egiten baduzu, eman itzazu goian adierazitako datu horiek guztiak.",
+       "systemblockedtext": "Zure erabiltzaile izena edo IP helbidea automatikoki blokeatu du MediaWikik.\nHau da emandako arrazoia:\n\n:<em>$2</em>\n\n*Blokeoaldiaren hasiera: $8\n*Blokeoaldiaren amaiera: $6\n*Blokeatua: $7\n\nOrain darabilzun IP helbidea: $3.\nKontsultarik egiten baduzu, adierazi goiko xehetasun horiek guztiak.",
        "blockednoreason": "ez da arrazoirik zehaztu",
        "whitelistedittext": "$1 behar duzu orrialdeak aldatu ahal izateko..",
        "confirmedittext": "Orrialdeetan aldaketak egin aurretik zure e-posta helbidea egiaztatu beharra daukazu. Mesedez, zehaztu eta egiaztatu zure e-posta helbidea [[Special:Preferences|hobespenetan]].",
        "page_first": "lehena",
        "page_last": "azkena",
        "histlegend": "Alderaketa hautatzea: marka itzazu alderatu nahi dituzun bi bertsioak, eta saka ezazu «Enter» edo klika ezazu beheko botoia.<br />\nAzalpenak: '''({{int:cur}})''' = oraingo bertsioarekiko aldeak,\n'''({{int:last}})''' = aurreko bertsioarekiko aldeak, \n'''{{int:minoreditletter}}''' = aldaketa txikia.",
-       "history-fieldset-title": "Berraztertzeak bilatu",
+       "history-fieldset-title": "Bertsioak iragazi",
        "history-show-deleted": "Berrikusketa ezabatuak soilik",
        "histfirst": "zaharrena",
        "histlast": "berriena",
        "rcfilters-savedqueries-already-saved": "Iragazki hauek dagoeneko gorde dira. Aldatu ezarpenak Gordetako Iragazki berri bat sortzeko.",
        "rcfilters-restore-default-filters": "Leheneratu iragazki lehenetsiak",
        "rcfilters-clear-all-filters": "Iragazki guztiak garbitu",
-       "rcfilters-show-new-changes": "Aldaketa berrienak ikusi",
+       "rcfilters-show-new-changes": "Aldaketa berriak ikusi $1-tik",
        "rcfilters-search-placeholder": "Aldaketak iragazi (menua erabili edo bilatu iragazkiaren izena)",
        "rcfilters-invalid-filter": "Balio ez duen iragazkia",
        "rcfilters-empty-filter": "Filtro aktiborik ez dago. Ekarpen guztiak erakusten.",
        "delete-confirm": "\"$1\" ezabatu",
        "delete-legend": "Ezabatu",
        "historywarning": "<strong>Oharra:</strong> Ezabatzera zoazen orrialdeak {{PLURAL:$1|berrikuspen bateko|$1 berrikuspeneko}} historia du:",
-       "historyaction-submit": "Erakutsi",
+       "historyaction-submit": "Berrikuspenak erakutsi",
        "confirmdeletetext": "Orrialde edo irudi bat eta beste historia guztia datu-basetik ezabatzear zaude. Mesedez, egiaztatu hori egin nahi duzula, ondorioak zeintzuk diren badakizula, eta [[{{MediaWiki:Policy-url}}|politikak]] errespetatuz egingo duzula.",
        "actioncomplete": "Ekintza burutu da",
        "actionfailed": "Ekintzak huts egin du",
        "blocklist-editing-page": "orrialdeak",
        "blocklist-editing-ns": "izen-tarteak",
        "ipblocklist-empty": "Blokeaketa zerrenda hutsik dago.",
-       "ipblocklist-no-results": "Zehaztutako IP helbide edo erabiltzaile izena ez dago blokeatuta.",
+       "ipblocklist-no-results": "Zehaztutako IP helbide edo erabiltzaile izenak ez du bat egiten.",
        "blocklink": "blokeatu",
        "unblocklink": "blokeoa kendu",
        "change-blocklink": "blokeoa aldatu",
index 486c908..e278a54 100644 (file)
        "autoblockedtext": "دسترسی نشانی آی‌پی شما قطع شده‌است، زیرا این نشانی آی‌پی توسط کاربر دیگری استفاده شده که دسترسی او توسط $1 قطع شده‌است.\nدلیل ارائه‌شده چنین است:\n\n:''$2''\n\n* شروع قطع دسترسی: $8\n* پایان قطع دسترسی: $6\n* کاربری هدف قطع دسترسی: $7\n\nشما می‌توانید با $1 یا [[{{MediaWiki:Grouppage-sysop}}|مدیری]] دیگر تماس بگیرید و در این باره صحبت کنید.\nتوجه کنید که شما نمی‌توانید از قابلیت «{{int:emailuser}}» استفاده کنید مگر آنکه نشانی ایمیل معتبری در [[Special:Preferences|ترجیحات کاربری]] خودتان ثبت کرده باشید و نیز باید امکان استفاده از این قابلیت برای شما قطع نشده باشد.\nنشانی آی‌پی فعلی شما $3 و شمارهٔ قطع دسترسی شما $5 است.\nلطفاً تمامی جزئیات فوق را در کلیهٔ درخواست‌هایی که در این باره مطرح می‌کنید ذکر کنید.",
        "systemblockedtext": "نام کاربری یا نشانی آی‌پی شما خودکار توسط مدیاویکی مسدود شده‌است.\nدلیل ارائه‌شده:\n\n:<em>$2</em>\n\n* آغاز بلاک: $8\n* پایان بلاک: $6\n* قطع دسترسی‌شده مورد نظر: $7\n\nنشانی آی‌پی کنونی شما $3 است.\nخواهشمند است تمام جزئیات بالا را در هر پرس‌وجویی که انجام می‌دهید قرار دهید.",
        "blockednoreason": "دلیلی مشخص نشده‌است",
-       "blockedtext-composite": "<strong>نام کاربری یا نشانی آی‌پی شما خودکار توسط مدیاویکی مسدود شده‌است.</strong>\nدلیل ارائه‌شده:\n\n:<em>$2</em>\n\n* آغاز بلاک: $8\n* پایان بلاک: $6\n\nنشانی آی‌پی کنونی شما $3 است.\nخواهشمند است تمام جزئیات بالا را در هر پرس‌وجویی که انجام می‌دهید قرار دهید.",
+       "blockedtext-composite": "<strong>نام کاربری یا نشانی آی‌پی شما خودکار توسط مدیاویکی مسدود شده‌است.</strong>\nدلیل ارائه‌شده:\n\n:<em>$2</em>\n\n* آغاز بلاک: $8\n* پایان بلاک: $6\n\n* $5\n\nنشانی آی‌پی کنونی شما $3 است.\nخواهشمند است تمام جزئیات بالا را در هر پرس‌وجویی که انجام می‌دهید قرار دهید.",
        "blockedtext-composite-reason": "حساب/آی‌پی شما به چند طریق بسته شده‌است",
        "whitelistedittext": "برای ویرایش مقاله‌ها باید $1.",
        "confirmedittext": "شما باید، پیش از ویرایش صفحات، آدرس ایمیل خود را مشخص و تأیید کنید. لطفاً از طریق [[Special:Preferences|ترجیحات کاربر]] این کار را صورت دهید.",
index 80c17e0..867e9dc 100644 (file)
        "index-category": "Indeksoidut sivut",
        "noindex-category": "Indeksointikiellolliset sivut",
        "broken-file-category": "Sivut, joissa on toimimattomia tiedostolinkkejä",
+       "categoryviewer-pagedlinks": "($1) ($2)",
+       "category-header-numerals": "$1–$2",
        "about": "Tietoja",
        "article": "Sisältösivu",
        "newwindow": "(avautuu uuteen ikkunaan)",
        "versionrequiredtext": "MediaWikistä tarvitaan vähintään versio $1 tämän sivun käyttämiseen. Katso [[Special:Version|versio]].",
        "ok": "OK",
        "pagetitle": "$1 – {{SITENAME}}",
+       "pagetitle-view-mainpage": "{{SITENAME}}",
+       "backlinksubtitle": "← $1",
        "retrievedfrom": "Noudettu kohteesta ”$1”",
        "youhavenewmessages": "Sinulle on $1 ($2).",
        "youhavenewmessagesfromusers": "{{PLURAL:$4|Sinulle on}} $1 {{PLURAL:$3|toiselta käyttäjältä|$3 käyttäjältä}} ($2).",
        "site-atom-feed": "$1-Atom-syöte",
        "page-rss-feed": "$1 (RSS-syöte)",
        "page-atom-feed": "$1 (Atom-syöte)",
+       "feed-atom": "Atom",
+       "feed-rss": "RSS",
        "red-link-title": "$1 (sivua ei ole)",
        "sort-descending": "Lajittele laskevassa järjestyksessä",
        "sort-ascending": "Lajittele nousevassa järjestyksessä",
        "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.",
        "logging-out-notify": "Sinua kirjataan ulos, odota hetki.",
+       "logout-failed": "Ei voida kirjautua ulos: $1",
        "cannotlogoutnow-title": "Nyt ei voi kirjautua ulos",
        "cannotlogoutnow-text": "Kirjautuminen ulos ei ole mahdollista käytettäessä $1.",
        "welcomeuser": "Tervetuloa $1!",
        "nocookiesnew": "Käyttäjätunnus on luotu, mutta et ole kirjautunut sisään. \n{{SITENAME}} käyttää evästeitä sisäänkirjautumisen yhteydessä. \nSelaimesi ei salli evästeitä. \nSalli evästeiden käyttö, ja sen jälkeen kirjaudu sisään juuri luomallasi käyttäjätunnuksella ja salasanalla.",
        "nocookieslogin": "{{SITENAME}} käyttää evästeitä sisäänkirjautumisen yhteydessä. Selaimesi ei salli evästeitä. Ota ne käyttöön, ja yritä uudelleen.",
        "nocookiesfornew": "Käyttäjätunnusta ei luotu, koska sen lähdettä ei kyetty varmistamaan. Varmista, että selaimessasi on käytössä evästeet, päivitä tämä sivu ja yritä uudelleen.",
+       "nocookiesforlogin": "{{int:nocookieslogin}}",
        "createacct-loginerror": "Tunnus luotiin onnistuneesti, mutta automaattista sisäänkirjautumista ei voitu tehdä. Siirry [[Special:UserLogin|manuaaliseen kirjautumiseen]].",
        "noname": "Et ole määritellyt kelvollista käyttäjänimeä.",
        "loginsuccesstitle": "Olet kirjautunut sisään",
        "previewerrortext": "Muokkaustesi esikatselun toteuttamisessa on tapahtunut virhe.",
        "blockedtitle": "Käyttäjä on estetty",
        "blocked-email-user": "<strong>Käyttäjänimeäsi on estetty lähettämästä sähköpostia. Voit silti muokata muita sivuja tässä wikissä.</strong> Voit katsoa eston täydelliset tiedot [[Special:MyContributions|käyttäjän muokkauksista]].\n\nEston antoi $1.\n\nAnnettu syy on <em>$2</em>.\n\n* Eston alkamisaika: $8\n* Eston päättymisaika: $6\n* Kohde: $7\n* Eston tunnus #$5",
-       "blockedtext-partial": "<strong>Käyttäjänimesi tai IP-osoitteesi on estetty muokkaamasta tätä sivua.</strong> Voit katsoa eston täydelliset tiedot [[Special:MyContributions|käyttäjän muokkauksista]].\n\nEston antoi $1.\n\nAnnettu syy on <em>$2</em>.\n\n* Eston alkamisaika: $8\n* Eston päättymisaika: $6\n* Kohde: $7\n* Eston tunnus #$5",
+       "blockedtext-partial": "<strong>Käyttäjänimesi tai IP-osoitteesi on estetty muokkaamasta tätä sivua. Voit yhä muokata muita sivuja tässä wikissä.</strong> Voit katsoa eston täydelliset tiedot [[Special:MyContributions|käyttäjän muokkaussivulta]].\n\nEston antoi $1.\n\nAnnettu syy on <em>$2</em>.\n\n* Eston alkamisaika: $8\n* Eston päättymisaika: $6\n* Eston kohde: $7\n* Eston tunnus #$5",
        "blockedtext": "<strong>Käyttäjätunnuksesi tai IP-osoitteesi on estetty.</strong>\n\nEston on asettanut $1.\nAnnettu syy on <em>$2</em>.\n\n* Eston alkamisaika: $8\n* Eston päättymisaika: $6\n* Kohde: $7\n\nVoit keskustella ylläpitäjän $1 tai toisen [[{{MediaWiki:Grouppage-sysop}}|ylläpitäjän]] kanssa estosta.\nHuomaa, ettet voi lähettää sähköpostia {{GRAMMAR:genitive|{{SITENAME}}}} kautta, ellet ole asettanut olemassa olevaa sähköpostiosoitetta [[Special:Preferences|asetuksissa]] tai jos esto on asetettu koskemaan myös sähköpostin lähettämistä.\nIP-osoitteesi on $3 ja estotunnus on #$5.\nLiitä kaikki yllä olevat tiedot mahdollisiin kyselyihisi.",
        "autoblockedtext": "IP-osoitteesi on estetty automaattisesti, koska sitä on käyttänyt toinen käyttäjä, jonka on estänyt ylläpitäjä $1.\nAnnettu syy on:\n\n:<em>$2</em>\n\n* Eston alkamisaika: $8\n* Eston päättymisaika: $6\n* Kohde: $7\n\nVoit keskustella ylläpitäjän $1 tai toisen [[{{MediaWiki:Grouppage-sysop}}|ylläpitäjän]] kanssa estosta.\n\nHuomaa, ettet voi lähettää sähköpostia {{GRAMMAR:genitive|{{SITENAME}}}} kautta, ellet ole asettanut olemassa olevaa sähköpostiosoitetta [[Special:Preferences|asetuksissa]] tai jos esto on asetettu koskemaan myös sähköpostin lähettämistä.\n\nIP-osoitteesi on $3 ja estotunnus on #$5.\nLiitä kaikki yllä olevat tiedot mahdollisiin kyselyihisi.",
        "systemblockedtext": "Käyttäjätunnuksesi tai IP-osoitteesi on automaattisesti estetty MediaWikin toimesta.\nAnnettu syy on:\n\n:<em>$2</em>\n\n* Start of block: $8\n* Expiration of block: $6\n* Intended blockee: $7\n\nTämänhetkinen IP-osoitteesi on $3.\nOle hyvä ja liitä kaikki yllä olevat tiedot mahdollisiin kyselyihisi.",
        "blockednoreason": "(syytä ei annettu)",
-       "blockedtext-composite": "<strong>Käyttäjätunnuksesi tai IP-osoitteesi on estetty.</strong>\n\nAnnettu syy on:\n\n:<em>$2</em>.\n\n* Esto alkoi: $8\n* Pisin esto vanhentuu: $6\n\nTämänhetkinen IP-osoitteesi on $3.\nOle hyvä ja liitä kaikki yllä olevat tiedot mahdollisiin kyselyihisi.",
+       "blockedtext-composite": "<strong>Käyttäjätunnuksesi tai IP-osoitteesi on estetty.</strong>\n\nAnnettu syy on:\n\n:<em>$2</em>.\n\n* Esto alkoi: $8\n* Pisin esto vanhentuu: $6\n\n* $5\n\nTämänhetkinen IP-osoitteesi on $3.\nOle hyvä ja liitä kaikki yllä olevat tiedot mahdollisiin kyselyihisi.",
+       "blockedtext-composite-reason": "Käyttäjätunnuksellesi ja/tai IP-osoitteellesi on asetettu useita päällekkäisiä estoja.",
        "whitelistedittext": "Sinun täytyy $1, jotta voisit muokata sivuja.",
        "confirmedittext": "Et voi muokata sivuja, ennen kuin olet varmentanut sähköpostiosoitteesi. Voit tehdä varmennuksen [[Special:Preferences|asetussivulla]].",
        "nosuchsectiontitle": "Pyydettyä osiota ei ole",
        "template-semiprotected": "(osittain suojattu)",
        "hiddencategories": "Tämä sivu kuuluu {{PLURAL:$1|seuraavaan piilotettuun luokkaan|seuraaviin piilotettuihin luokkiin}}:",
        "edittools": "<!-- Tässä oleva teksti näytetään muokkauskentän alla. -->",
+       "edittools-upload": "-",
        "nocreatetext": "Et voi luoda uusia sivuja. Voit muokata olemassa olevia sivuja tai [[Special:UserLogin|kirjautua sisään tai luoda käyttäjätunnuksen]].",
        "nocreate-loggedin": "Sinulla ei ole oikeutta luoda uusia sivuja.",
        "sectioneditnotsupported-title": "Osioiden muokkaamista ei tueta.",
        "content-model-text": "pelkkä teksti",
        "content-model-javascript": "JavaScript",
        "content-model-css": "CSS",
+       "content-model-json": "JSON",
        "content-json-empty-object": "Tyhjä objekti",
        "content-json-empty-array": "Tyhjä array",
        "deprecated-self-close-category": "Sivut, joissa on virheellisiä itsensäsulkevia HTML-tageja",
        "mergehistory-comment": "Yhdisti sivun [[:$1]] sivuun [[:$2]]: $3",
        "mergehistory-same-destination": "Lähde- ja kohdesivut eivät voi olla samat",
        "mergehistory-reason": "Syy:",
+       "mergehistory-revisionrow": "$1 ($2) $3 . . $4 $5 $6",
        "mergelog": "Yhdistämisloki",
        "revertmerge": "Peru yhdistäminen",
        "mergelogpagetext": "Alla on loki viimeisimmistä muutoshistorioiden yhdistämisistä.",
        "youremail": "Sähköpostiosoite",
        "username": "{{GENDER:$1|Käyttäjätunnus}}",
        "prefs-memberingroups": "{{GENDER:$2|Jäsenenä}} {{PLURAL:$1|ryhmässä|ryhmissä}}",
+       "prefs-memberingroups-type": "$1",
        "group-membership-link-with-expiry": "$1 ($2 asti)",
        "prefs-registration": "Rekisteröintiaika",
+       "prefs-registration-date-time": "$1",
        "yourrealname": "Oikea nimi",
        "yourlanguage": "Käyttöliittymän kieli",
        "yourvariant": "Sisällön kielivariantti",
        "saveusergroups": "Tallenna {{GENDER:$1|käyttäjän}} ryhmät",
        "userrights-groupsmember": "Jäsenenä ryhmissä:",
        "userrights-groupsmember-auto": "Automaattisesti jäsenenä ryhmissä:",
+       "userrights-groupsmember-type": "$1",
        "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 enää poistamaan käyttäjää ryhmästä, kun olet hänet siihen lisännyt, tai päinvastoin.\n* <nowiki>#</nowiki> tarkoittaa, että voit ainoastaan pidentää tämän ryhmän jäsenten oikeuksien vanhentumisen määräaikaa, mutta et voi lyhentää sitä.",
        "userrights-reason": "Syy:",
        "userrights-no-interwiki": "Sinulla ei ole oikeutta muokata käyttöoikeuksia muissa wikeissä.",
        "userrights-nodatabase": "Tietokantaa $1 ei ole tai se ei ole paikallinen.",
        "userrights-changeable-col": "Ryhmät, joita voit muuttaa",
        "userrights-unchangeable-col": "Ryhmät, joita et voi muuttaa",
+       "userrights-irreversible-marker": "$1*",
+       "userrights-no-shorten-expiry-marker": "$1#",
        "userrights-expiry-current": "Vanhentuu $1",
        "userrights-expiry-none": "Ei vanhennu",
        "userrights-expiry": "Vanhentuu:",
        "action-override-export-depth": "viedä sivuja sisältäen viitatut sivut viiden syvyydellä",
        "action-suppressredirect": "siirtää sivuja luomatta automaattisia ohjauksia",
        "nchanges": "$1 {{PLURAL:$1|muutos|muutosta}}",
+       "ntimes": "$1×",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|viimeisen käynnin jälkeen}}",
        "enhancedrc-history": "historia",
        "recentchanges": "Tuoreet muutokset",
        "minoreditletter": "p",
        "newpageletter": "U",
        "boteditletter": "b",
+       "unpatrolledletter": "!",
+       "rc-change-size": "$1",
        "rc-change-size-new": "$1 {{PLURAL:$1|tavu|tavua}} muutosten jälkeen",
        "newsectionsummary": "/* $1 */ uusi osio",
        "rc-enhanced-expand": "Näytä yksityiskohdat",
        "move": "Siirrä",
        "movethispage": "Siirrä tämä sivu",
        "unusedimagestext": "Seuraavat tiedostot ovat olemassa, mutta niitä ei käytetä millään sivulla.\nHuomaa, että muut verkkosivut saattavat viitata tiedostoon suoran URL:n avulla, jolloin tiedosto saattaa olla tässä listassa, vaikka sitä käytetäänkin.",
+       "unusedimagestext-categorizedimgisused": "Seuraavat tiedostot ovat olemassa, mutta niitä ei ole upotettu mihinkään sivuun. Luokitellut kuvat lasketaan käytössä oleviksi, vaikka niitä ei ole upotettu mihinkään sivuun.\nHuomaa, että muut verkkosivustot voivat linkittää suoraan URL-osoitteeseen, joten ne voivat silti olla tässä luettelossa, vaikka niitä on käytetty aktiivisesti.",
        "unusedcategoriestext": "Nämä luokat ovat olemassa, mutta niitä ei käytetä.",
        "notargettitle": "Ei kohdetta",
        "notargettext": "Et ole määritellyt kohteeksi sivua tai käyttäjää, johon toiminto suoritetaan.",
        "apisandbox-request-selectformat-label": "Näytä pyyntödata muodossa:",
        "apisandbox-request-format-url-label": "URL-kyselymerkkijono",
        "apisandbox-request-url-label": "Pyynnön URL",
+       "apisandbox-request-format-json-label": "JSON",
        "apisandbox-request-json-label": "Pyydetty JSON:",
        "apisandbox-request-time": "Pyyntöön kulunut aika: {{PLURAL:$1|$1 ms}}",
        "apisandbox-results-fixtoken": "Korjaa ”token” ja lähetä uudelleen",
        "listgrants-summary": "Alla on luettelo toimintaoikeuksista sekä niiden vastaavista pääsytavoista käyttäjäoikeuksiin. Käyttäjät voivat valtuuttaa sovelluksia käyttämään heidän tiliään rajoitetuin oikeuksien riippuen toimintaoikeuksista, jota käyttäjä on sovellukselle myöntänyt. Käyttäjän puolesta toimiva sovellus ei kuitenkaan voi käyttää oikeuksia, joita käyttäjällä ei ole.\nYksittäisistä oikeuksista saattaa olla saatavilla [[{{MediaWiki:Listgrouprights-helppage}}|lisätietoa]].",
        "listgrants-grant": "Toimintaoikeus (grant)",
        "listgrants-rights": "Oikeudet (rights)",
+       "listgrants-grant-display": "$1 <code>($2)</code>",
        "trackingcategories": "Tarkkailuluokat",
        "trackingcategories-summary": "Tällä sivulla on luettelo sellaisista ongelmia havaitsevista luokista (tarkkailuluokat), joiden sisällön koostaa automaattisesti MediaWiki-ohjelmisto. Luokkien nimiä voi vaihtaa muuttamalla asianomaista järjestelmäviestiä nimiavaruudessa {{ns:8}}.",
        "trackingcategories-msg": "Tarkkailuluokka",
        "protect-fallback": "Salli vain käyttäjät, joilla on oikeus $1",
        "protect-level-autoconfirmed": "Vain hyväksytyt käyttäjät",
        "protect-level-sysop": "Salli vain ylläpitäjät",
+       "protect-summary-desc": "[$1=$2] ($3)",
        "protect-summary-cascade": "tarttuva",
        "protect-expiring": "vanhentuu $1 (UTC)",
        "protect-expiring-local": "vanhentuu $1",
        "undelete-error-long": "Tiedoston palauttaminen epäonnistui:\n\n$1",
        "undelete-show-file-confirm": "Haluatko varmasti nähdä poistetun version tiedostosta <nowiki>$1</nowiki>, joka on tallennettu $2 kello $3?",
        "undelete-show-file-submit": "Kyllä",
+       "undelete-revision-row2": "$1 ($2) $3 . . $4 $5 $6 $7 $8",
        "namespace": "Nimiavaruus:",
        "invert": "Käänteinen valinta",
        "tooltip-invert": "Valitse tämä kohta, jos haluat piilottaa muutokset sivuihin valitussa nimiavaruudessa (ja liittyviin nimiavaruuksiin, jos valittu)",
        "blocklist-editing-page": "sivut",
        "blocklist-editing-ns": "nimiavaruudet",
        "ipblocklist-empty": "Estolista on tyhjä.",
-       "ipblocklist-no-results": "Pyydettyä IP-osoitetta tai käyttäjätunnusta ei ole estetty.",
+       "ipblocklist-no-results": "Pyydetylle IP-osoitteelle tai käyttäjätunnukselle ei löytynyt vastaavia estoja.",
        "blocklink": "estä",
        "unblocklink": "poista esto",
        "change-blocklink": "muuta estoa",
        "pageinfo-few-watchers": "Vähemmän kuin $1 {{PLURAL:$1|tarkkailija|tarkkailijaa}}",
        "pageinfo-few-visiting-watchers": "Tuoreita muutoksia saatetaan tarkkailla, mutta tämä ei ole varmaa",
        "pageinfo-redirects-name": "Sivulle johtavat ohjaukset",
+       "pageinfo-redirects-value": "$1",
        "pageinfo-subpages-name": "Sivun alasivut",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|ohjaus|ohjausta}}; $3 {{PLURAL:$3|ei-ohjausta}})",
        "pageinfo-firstuser": "Sivun luonut",
        "mediawarning": "'''Varoitus''': Tämä tiedostomuoto saattaa sisältää vahingollista koodia.\nSuorittamalla sen järjestelmäsi voi muuttua epäluotettavaksi.",
        "imagemaxsize": "Kuvien enimmäiskoko tiedostokuvaussivuilla",
        "thumbsize": "Pienoiskuvien koko",
+       "widthheight": "$1 × $2",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|sivu|sivua}}",
        "file-info": "$1, MIME-tyyppi: $2",
        "file-info-size": "$1 × $2 kuvapistettä, $3, MIME-tyyppi: $4",
        "ilsubmit": "Hae",
        "bydate": "päiväyksen mukaan",
        "sp-newimages-showfrom": "Näytä uudet tiedostot alkaen $1 kello $2",
+       "video-dims": "$1, $2 × $3",
+       "seconds-abbrev": "$1 s",
+       "minutes-abbrev": "$1 min",
+       "hours-abbrev": "$1 h",
+       "days-abbrev": "$1 d",
        "seconds": "{{PLURAL:$1|$1 sekunti|$1 sekuntia}}",
        "minutes": "{{PLURAL:$1|$1 minuutti|$1 minuuttia}}",
        "hours": "{{PLURAL:$1|$1 tunti|$1 tuntia}}",
        "sunday-at": "Sunnuntaina klo $1",
        "yesterday-at": "Eilen klo $1",
        "bad_image_list": "Listan muoto on seuraava:\n\nVain *-merkillä alkavat rivit otetaan huomioon.\nRivin ensimmäisen linkin on osoitettava tiedostoon.\nKaikki muut linkit ovat poikkeuksia eli toisin sanoen sivuja, joissa tiedostoa saa käyttää.",
+       "variantname-zh-hans": "hans",
+       "variantname-zh-hant": "hant",
        "metadata": "Sisältökuvaukset",
        "metadata-help": "Tämä tiedosto sisältää esimerkiksi kuvanlukijan, digikameran tai kuvankäsittelyohjelman lisäämiä lisätietoja. Kaikki tiedot eivät enää välttämättä vastaa todellisuutta, jos kuvaa on muokattu sen alkuperäisen luonnin jälkeen.",
        "metadata-expand": "Näytä kaikki sisältökuvaukset",
        "confirm-unwatch-top": "Poistetaanko tämä sivu tarkkailulistaltasi?",
        "confirm-rollback-button": "OK",
        "confirm-rollback-top": "Palauta tämän sivun muokkaukset?",
+       "confirm-rollback-bottom": "Tämä toiminto palauttaa välittömästi takaisinpäin valitut muutokset tällä sivulla.",
        "confirm-mcrrestore-title": "Palauta takaisin yksittäinen versio",
        "confirm-mcrundo-title": "Kumoa muutos",
        "mcrundofailed": "Kumoaminen epäonnistui",
        "mcrundo-missingparam": "Tarvittavat parametrit puuttuvat pyynnöstä.",
        "mcrundo-changed": "Sivu on muuttunut siitä lähtien, kun katsoit tätä muokkausta. Arvioi uusi muokkaus.",
        "mcrundo-parse-failed": "Uuden version jäsentäminen epäonnistui: $1",
+       "semicolon-separator": ";&#32;",
+       "comma-separator": ",&#32;",
+       "colon-separator": ":&#32;",
+       "pipe-separator": "&#32;|&#32;",
+       "word-separator": "&#32;",
+       "ellipsis": "...",
        "percent": "$1&#160;%",
+       "parentheses": "($1)",
+       "parentheses-start": "(",
+       "parentheses-end": ")",
        "quotation-marks": "”$1”",
        "imgmultipageprev": "← edellinen sivu",
        "imgmultipagenext": "seuraava sivu →",
        "logentry-block-block": "$1 {{GENDER:$2|esti}} käyttäjän {{GENDER:$4|$3}}. Eston kesto on $5 $6",
        "logentry-block-unblock": "$1 {{GENDER:$2|poisti muokkauseston}} käyttäjältä {{GENDER:$4|$3}}",
        "logentry-block-reblock": "$1 {{GENDER:$2|muutti}} eston asetuksia kohteessa {{GENDER:$4|$3}}. Eston kesto on $5 $6",
-       "logentry-partialblock-block-page": "{{PLURAL:$1|sivulla|sivuilla}} $2",
-       "logentry-partialblock-block-ns": "{{PLURAL:$1|nimiavaruuteen|nimiavaruuksiin}} $2",
-       "logentry-partialblock-block": "$1 {{GENDER:$2|blocked}} {{GENDER:$4|$3}} from editing $7 with an expiration time of $5 $6",
+       "logentry-partialblock-block-page": "{{PLURAL:$1|sivua|sivuja}} $2",
+       "logentry-partialblock-block-ns": "{{PLURAL:$1|nimiavaruutta|nimiavaruuksia}} $2",
+       "logentry-partialblock-block": "$1 {{GENDER:$2|esti}} käyttäjää {{GENDER:$4|$3}} muokkaamasta $7. Eston kesto $5 $6",
        "logentry-partialblock-reblock": "$1 {{GENDER:$2|muutti}} käyttäjän {{GENDER:$4|$3}} muokkauseston asetuksia estäen muokkausten tekemisen $7. Eston kesto on $5 $6",
        "logentry-non-editing-block-block": "$1 {{GENDER:$2|esti}} käyttäjää {{GENDER:$4|$3}} suorittamasta määrättyjä toimenpiteitä (lukuun ottamatta muokkaamista). Eston kesto on $5 $6",
        "logentry-non-editing-block-reblock": "$1 {{GENDER:$2|muutti}} käyttäjän {{GENDER:$4|$3}} toimintaeston asetuksia, jotka koskevat määrättyjä toimenpiteitä. Eston kesto on $5 $6",
        "mw-widgets-abandonedit-keep": "Jatka muokkaamista",
        "mw-widgets-abandonedit-title": "Oletko varma?",
        "mw-widgets-copytextlayout-copy": "Kopioi",
+       "mw-widgets-copytextlayout-copy-fail": "Leikepöydälle kopioiminen epäonnistui.",
        "mw-widgets-copytextlayout-copy-success": "Kopioitu leikepöydälle",
        "mw-widgets-dateinput-no-date": "Ei ole valittu mitään päivää",
        "mw-widgets-dateinput-placeholder-day": "VVVV-KK-PP",
        "passwordpolicies-summary": "Tämä on luettelo voimassa olevista salasanakäytännöistä tämän wikin käyttäjäryhmille.",
        "passwordpolicies-group": "Ryhmä",
        "passwordpolicies-policies": "Käytännöt",
+       "passwordpolicies-policy-display": "<span class=\"passwordpolicies-policy\">$1 <code>($2)</code></span>",
+       "passwordpolicies-policy-displaywithflags": "<span class=\"passwordpolicies-policy\">$1 <code>($2)</code></span> <span class=\"passwordpolicies-policy-flags\">($3)</span>",
        "passwordpolicies-policy-minimalpasswordlength": "Salasanan tulee olla vähintään {{PLURAL:$1|yhden merkin|$1 merkin}} pituinen",
        "passwordpolicies-policy-minimumpasswordlengthtologin": "Salasanassa on oltava vähintään $1 {{PLURAL:$1|merkki|merkkiä}} pystyäksesi kirjautumaan",
        "passwordpolicies-policy-passwordcannotmatchusername": "Salasana ei saa olla sama kuin käyttäjänimi",
        "passwordpolicies-policy-maximalpasswordlength": "Salasanan tulee olla lyhyempi kuin $1 {{PLURAL:$1|merkki|merkkiä}}",
        "passwordpolicies-policy-passwordcannotbepopular": "Salasana ei saa olla {{PLURAL:$1|suosittu salasana|$1 suosituimman salasanan listalla}}",
        "passwordpolicies-policy-passwordnotinlargeblacklist": "Salasana ei voi olla 100,000 yleisimmin käytetyn joukossa.",
+       "passwordpolicies-policyflag-forcechange": "täytyy vaihtaa kirjautuessa",
+       "passwordpolicies-policyflag-suggestchangeonlogin": "ehdota vaihtamista kirjautuessa",
+       "easydeflate-invaliddeflate": "Annettua sisältöä ei ole tyhjennetty kunnolla",
        "unprotected-js": "Turvallisuussyistä JavaScriptiä ei voi ladata suojaamattomilta sivuilta. Luo JavaScript-sivuja vain MediaWiki-nimiavaruuteen tai käyttäjän alasivulle.",
        "userlogout-continue": "Haluatko kirjautua ulos?"
 }
index 698d3b5..d57678c 100644 (file)
        "autoblockedtext": "Votre adresse IP a été bloquée automatiquement car elle a été utilisée par un autre utilisateur, lui-même bloqué par $1.\nLa raison invoquée est :\n\n: <em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage : $6\n* Compte bloqué : $7\n\nVous pouvez contacter $1 ou l’un des autres [[{{MediaWiki:Grouppage-sysop}}|administrateurs]] pour discuter de ce blocage.\n\nNotez que vous ne pourrez utiliser la fonctionnalité « {{int:emailuser}} » que si vous avez une adresse de courriel validée dans vos [[Special:Preferences|préférences]] et que cette fonctionnalité ne vous a pas été désactivée.\n\nVotre adresse IP actuelle est $3, et le numéro de blocage est $5.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
        "systemblockedtext": "Votre nom d'utilisateur ou votre adresse IP ont été bloqués automatiquement par MediaWiki.\nLa raison donnée est la suivante:\n\n: <em>$2</em>.\n\n* Le début du blocage: $8\n* Expiration du délai de blocage: $6\n* Elément concerné: $7\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chacune des requêtes que vous ferez.",
        "blockednoreason": "aucune raison donnée",
-       "blockedtext-composite": "<strong>Votre nom d'utilisateur ou votre adresse IP ont été bloqués.</strong>\n\nLa raison invoquées est :\n\n:<em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage le plus long : $6\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chaque demande que vous ferez.",
+       "blockedtext-composite": "<strong>Votre nom d'utilisateur ou votre adresse IP ont été bloqués.</strong>\n\nLa raison invoquées est :\n\n:<em>$2</em>.\n\n* Début du blocage : $8\n* Expiration du blocage le plus long : $6\n\n* $5\n\nVotre adresse IP actuelle est $3.\nVeuillez inclure tous les détails ci-dessus dans chaque demande que vous ferez.",
+       "blockedtext-composite-ids": "ID de bloc pertinents : $1 (votre adresse IP peut aussi être en liste noire)",
+       "blockedtext-composite-no-ids": "Votre adresse IP apparaît dans plusieurs listes noires",
        "blockedtext-composite-reason": "Il existe plusieurs blocages sur votre compte et/ou votre adresse IP",
        "whitelistedittext": "Vous devez vous $1 pour avoir la permission de modifier le contenu.",
        "confirmedittext": "Vous devez confirmer votre adresse de courriel avant de modifier les pages.\nVeuillez entrer et valider votre adresse de courriel dans vos [[Special:Preferences|préférences]].",
        "specialmute-error-email-preferences": "Vous devez confirmer votre adresse courriel avant de pouvoir mettre en sourdine un utilisateur. Vous pouvez le faire depuis [[Special:Preferences]].",
        "specialmute-email-footer": "Veuillez voir <$1> pour gérer les préférences courriel pour {{BIDI:$2}}.",
        "specialmute-login-required": "Veuillez vous connecter pour mettre-à-jour vos préférences de mise en sourdine d’utilisateurs.",
+       "mute-preferences": "Préférences de misse en silence",
        "revid": "version $1",
        "pageid": "ID de page $1",
        "interfaceadmin-info": "$1\n\nLes droits pour modifier les fichiers CSS/JS/JSON globaux au site ont été récemment séparés du droit <code>editinterface</code>. Si vous ne comprenez pas pourquoi vous avez cette erreur, voyez [[mw:MediaWiki_1.32/interface-admin]].",
index 7fc5b45..8852724 100644 (file)
        "autoblockedtext": "O seu enderezo IP foi bloqueado automaticamente porque foi empregado por outro usuario que foi bloqueado por $1.\nA razón que deu foi a seguinte:\n\n:<em>$2</em>\n\n* Inicio do bloqueo: $8\n* Caducidade do bloqueo: $6\n* Pretendeuse bloquear: $7\n\nPode contactar con $1 ou con calquera outro [[{{MediaWiki:Grouppage-sysop}}|administrador]] para discutir este bloqueo.\n\nTeña en conta que non pode empregar a característica \"{{int:emailuser}}\" a non ser que dispoña dun enderezo electrónico válido rexistrado nas súas [[Special:Preferences|preferencias de usuario]] e e que o seu uso non fose bloqueado.\n\nO seu enderezo IP actual é $3 e o ID do bloqueo é #$5.\nPor favor, inclúa eses datos nas consultas que faga.",
        "systemblockedtext": "O seu nome de usuario ou enderezo IP foi bloqueado automaticamente polo sistema MediaWiki.\nO motivo do bloqueo é:\n\n:<em>$2</em>\n\n* Comezo do bloqueo: $8\n* Expiración do bloqueo: $6\n* Destinatario do bloqueo: $7\n\nO seu enderezo IP actual é $3.\nPor favor, inclúa todos estes detalles en calquera consulta que realice.",
        "blockednoreason": "non se deu ningunha razón",
-       "blockedtext-composite": "<strong>O seu nome de usuario ou enderezo IP foron bloqueados.</strong>\n\nO motivo dado é:\n\n:<em>$2</em>.\n\n* Comezo do bloqueo: $8\n* Remate do bloqueo máis longo: $6\n\nO seu enderezo IP actual é $3.\nPor favor, inclúa todos os detalles de arriba en calquera contacto sobre este asunto.",
+       "blockedtext-composite": "<strong>O seu nome de usuario ou enderezo IP foron bloqueados.</strong>\n\nO motivo dado é:\n\n:<em>$2</em>.\n\n* Comezo do bloqueo: $8\n* Remate do bloqueo máis longo: $6\n\n* $5\n\nO seu enderezo IP actual é $3.\nPor favor, inclúa todos os detalles de arriba en calquera contacto sobre este asunto.",
        "blockedtext-composite-reason": "Existen varios bloqueos contra a súa conta ou enderezo IP",
        "whitelistedittext": "Debe $1 para poder editar páxinas.",
        "confirmedittext": "Debe confirmar o correo electrónico antes de comezar a editar. Por favor, configure e dea validez ao correo mediante as súas [[Special:Preferences|preferencias de usuario]].",
index f76e312..9c54b07 100644 (file)
        "history": "Riwāyati lo halaman",
        "history_short": "Riwāyati",
        "history_small": "riwāyati",
-       "updatedmarker": "biloli'o to'u bililohe pulitiyo",
+       "updatedmarker": "loboli'a to'u bililohe'u pulitiyo",
        "printableversion": "Persi cetak",
        "permalink": "Wūmbuta kakali",
        "print": "Cetakiya",
        "pt-login-continue-button": "Turusi tumuwoto log",
        "pt-createaccount": "Mohutu akun",
        "pt-userlogout": "Lumuwalo log",
+       "resetpass_header": "Tulo'i tahe u'unti lo akun",
+       "oldpassword": "Tahe u'unti mulolo:",
+       "newpassword": "Tahe u'unti bohu:",
+       "retypenew": "Tepuwa taheu'unti bohu:",
        "botpasswords-label-create": "Mohutu",
        "botpasswords-label-update": "Mopobohu",
        "botpasswords-label-cancel": "Bataliya",
index 5b522e2..c388f62 100644 (file)
        "autoblockedtext": "כתובת ה־IP שלך נחסמה באופן אוטומטי כיוון שמשתמש אחר, שנחסם על־ידי $1, השתמש בה.\nהסיבה שניתנה לחסימה היא:\n\n:<em>$2</em>\n\n* תחילת החסימה: $8\n* פקיעת החסימה: $6\n* החסימה שבוצעה: $7\n\nבאפשרותך ליצור קשר עם $1 או עם כל אחד מ[[{{MediaWiki:Grouppage-sysop}}|מפעילי המערכת]] האחרים כדי לדון בחסימה.\n\nכמו־כן, באפשרותך להשתמש בתכונת \"{{int:emailuser}}\", אלא אם לא ציינת כתובת דוא\"ל תקפה ב[[Special:Preferences|העדפות המשתמש שלך]] או אם נחסמת משליחת דוא\"ל.\n\nכתובת ה־IP הנוכחית שלך היא $3, ומספר החסימה שלך הוא #$5.\nיש לציין את כל הפרטים הללו בכל פנייה לבירור החסימה.",
        "systemblockedtext": "שם המשתמש או כתובת ה־IP שלך נחסמו באופן אוטומטי על־ידי תוכנת מדיה־ויקי.\nהסיבה שניתנה לחסימה היא:\n\n:<em>$2</em>\n\n* תחילת החסימה: $8\n* פקיעת החסימה: $6\n* החסימה שבוצעה: $7\n\nכתובת ה־IP הנוכחית שלך היא $3.\nיש לציין את כל הפרטים הללו בכל פנייה לבירור החסימה.",
        "blockednoreason": "לא ניתנה סיבה",
-       "blockedtext-composite": "<strong>שם המשתמש או כתובת ה־IP שלך נחסמו.</strong>\n\nהסיבה שניתנה לכך היא:\n\n:<em>$2</em>.\n\n* תחילת החסימה: $8\n* פקיעת החסימה הארוכה ביותר: $6\n\nכתובת ה־IP הנוכחית שלך היא $3.\nיש לציין את כל הפרטים הללו בכל פנייה לבירור החסימה.",
+       "blockedtext-composite": "<strong>שם המשתמש או כתובת ה־IP שלך נחסמו.</strong>\n\nהסיבה שניתנה לכך היא:\n\n:<em>$2</em>.\n\n* תחילת החסימה: $8\n* פקיעת החסימה הארוכה ביותר: $6\n\n* $5\n\nכתובת ה־IP הנוכחית שלך היא $3.\nיש לציין את כל הפרטים הללו בכל פנייה לבירור החסימה.",
        "blockedtext-composite-reason": "הופעלו מספר חסימות על חשבון המשתמש שלך או על כתובת ה־IP שלך (או על שניהם)",
        "whitelistedittext": "נדרשת $1 כדי לערוך דפים.",
        "confirmedittext": "יש לאמת את כתובת הדוא\"ל לפני עריכת דפים.\nנא להגדיר ולאמת את כתובת הדוא\"ל שלך באמצעות [[Special:Preferences|העדפות המשתמש]] שלך.",
        "specialmute-error-email-preferences": "יש לאמת את כתובת הדואר האלקטרוני שלך לפני שתהיה לך אפשרות להשתיק משתמש. אפשר לעשות זאת מהדף [[Special:Preferences]].",
        "specialmute-email-footer": "כדי לנהל את העדפות קבלת הדואר האלקטרוני שנשלח על־ידי {{BIDI:$2}}, באפשרותך לבקר בדף <$1>.",
        "specialmute-login-required": "נדרשת כניסה לחשבון כדי לשנות את העדפות ההשתקה שלך.",
+       "mute-preferences": "העדפות השתקה",
        "revid": "גרסה $1",
        "pageid": "מזהה דף $1",
        "interfaceadmin-info": "$1\n\nההרשאות לעריכת קובצי CSS/JS/JSON של האתר כולו הופרדו לאחרונה מההרשאה <code>editinterface</code>. אם לא ברור לך מדוע קיבלת את הודעת השגיאה הזאת, ר' [[mw:MediaWiki_1.32/interface-admin]].",
index 9cd104b..83d01d1 100644 (file)
        "autoblockedtext": "Az IP-címed automatikusan blokkolva lett, mert korábban egy olyan szerkesztő használta, akit $1 blokkolt, az alábbi indoklással:\n\n:''$2''\n\n*A blokk kezdete: '''$8'''\n*A blokk lejárata: '''$6'''\n*Blokkolt szerkesztő: '''$7'''\n\nKapcsolatba léphetsz $1 szerkesztőnkkel, vagy egy másik [[{{MediaWiki:Grouppage-sysop}}|adminisztrátorral]], és megbeszélheted vele a blokkolást.\n\nAz „{{int:emailuser}}” funkciót csak akkor használhatod, ha érvényes e-mail címet adtál meg\n[[Special:Preferences|fiókbeállításaidban]], és nem blokkolták a használatát.\n\nJelenlegi IP-címed: $3, a blokkolás azonosítószáma: #$5.\nKérjük, hogy érdeklődés esetén mindkettőt add meg.",
        "systemblockedtext": "A felhasználónevedet vagy IP-címedet automatikusan blokkolta a MediaWiki.\nA blokkolás indoka:\n\n:<em>$2</em>\n\n* A blokk kezdete: $8\n* A blokk lejárata: $6\n* Blokkolt szerkesztő: $7\n\nA jelenlegi IP-címed: $3.\nKérjük, hogy érdeklődés esetén minden fenti részletet adj meg.",
        "blockednoreason": "nem adott meg okot",
-       "blockedtext-composite": "<strong>A felhasználónevedet vagy IP-címedet blokkolták.</strong>\nA blokkolás indoka:\n\n:<em>$2</em>\n\n* A blokk kezdete: $8\n* A leghoszabb blokk lejárata: $6\n\nA jelenlegi IP-címed: $3.\nKérjük, hogy érdeklődés esetén minden fenti részletet adj meg.",
+       "blockedtext-composite": "<strong>A felhasználónevedet vagy IP-címedet blokkolták.</strong>\nA blokkolás indoka:\n\n:<em>$2</em>\n\n* A blokk kezdete: $8\n* A leghoszabb blokk lejárata: $6\n\n* $5\n\nA jelenlegi IP-címed: $3.\nKérjük, hogy érdeklődés esetén minden fenti részletet adj meg.",
        "blockedtext-composite-reason": "Fiókoddal és/vagy IP-címeddel szemben több blokk is érvényben van",
        "whitelistedittext": "Lapok szerkesztéséhez $1.",
        "confirmedittext": "Lapok szerkesztése előtt meg kell erősítened az e-mail címedet. Kérjük, hogy a [[Special:Preferences|szerkesztői beállításaidban]] add meg, majd erősítsd meg az e-mail címedet.",
index 851afba..90c1a22 100644 (file)
        "autoblockedtext": "Tu adresse IP ha essite automaticamente blocate perque un altere usator lo usava qui esseva blocate per $1.\nLe motivo presentate es:\n\n:<em>$2</em>\n\n* Initio del blocada: $8\n* Expiration del blocada: $6\n* Blocato intendite: $7\n\nTu pote contactar $1 o un del altere [[{{MediaWiki:Grouppage-sysop}}|administratores]] pro discuter le blocada.\n\nNota que tu pote solmente utilisar le function \"{{int:emailuser}}\" si tu ha registrate un adresse de e-mail valide in tu [[Special:Preferences|preferentias de usator]] e tu non ha essite blocate de usar lo.\n\nTu adresse IP actual es $3, e le ID del blocada es #$5.\nPer favor include tote le detalios supra specificate in omne correspondentia.",
        "systemblockedtext": "Tu nomine de usator o adresse IP ha essite blocate automaticamente per MediaWiki.\nLe motivo presentate es:\n\n:<em>$2</em>\n\n* Initio del blocada: $8\n* Expiration del blocada: $6\n* Blocato intendite: $7\n\nTu adresse IP actual es $3.\nPer favor, include tote le detalios enumerate hic supra in omne questiones que tu pone.",
        "blockednoreason": "nulle motivo specificate",
-       "blockedtext-composite": "<strong>Tu nomine de usator o adresse IP ha essite blocate.</strong>\n\nLe motivo presentate es:\n\n:<em>$2</em>.\n\n* Initio del blocada: $8\n* Expiration del blocada le plus longe: $6\n\nTu adresse IP actual es $3.\nPer favor, include tote le detalios enumerate hic supra in omne questiones que tu pone.",
+       "blockedtext-composite": "<strong>Tu nomine de usator o adresse IP ha essite blocate.</strong>\n\nLe motivo presentate es:\n\n:<em>$2</em>.\n\n* Initio del blocada: $8\n* Expiration del blocada le plus longe: $6\n\n* $5\n\nTu adresse IP actual es $3.\nPer favor, include tote le detalios enumerate hic supra in omne questiones que tu pone.",
        "blockedtext-composite-reason": "Il ha plure blocadas contra tu conto e/o adresse IP",
        "whitelistedittext": "Tu debe $1 pro poter modificar paginas.",
        "confirmedittext": "Tu debe confirmar tu adresse de e-mail pro poter modificar paginas.\nPer favor entra e valida tu adresse de e-mail per medio de tu [[Special:Preferences|preferentias de usator]].",
index 1dd959a..3c91602 100644 (file)
                        "Manfredi26",
                        "Albe Albe 460",
                        "Senpremì",
-                       "Ignazio Cannata"
+                       "Ignazio Cannata",
+                       "Frubino"
                ]
        },
        "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.",
+       "logging-out-notify": "La tua sessione si sta chiudendo, attendere prego.",
+       "logout-failed": "Impossibile uscire ora: $1",
        "cannotlogoutnow-title": "Impossibile uscire ora",
        "cannotlogoutnow-text": "La disconnessione non è possibile quando si sta usando $1.",
        "welcomeuser": "Benvenuto, $1!",
        "subject-preview": "Anteprima dell'oggetto:",
        "previewerrortext": "Si è verificato un errore durante il tentativo di mostrare l'anteprima delle tue modifiche.",
        "blockedtitle": "Utente bloccato.",
+       "blocked-email-user": "<strong>Alla tua utenza è stato vietato l'invio di email. Puoi ancora modificare altre pagine di questa wiki.</strong> Puoi vedere tutti i dettagli del blocco su [[Special:MyContributions|contributi dell'utenza]].\n\nIl blocco è stato effettuato da $1.\n\nLa ragione data è <em>$2</em>.\n\n* Inizio del blocco: $8\n* Scadenza del blocco: $6\n* Destinatario del blocco: $7\n* ID Blocco #$5",
+       "blockedtext-partial": "<strong>Alla tua utenza o indirizzo IP address è stato vietato di apportare modifiche a questa pagina. Puoi comunque modificare altre pagine di questa wiki.</strong> Puoi vedere tutti i dettagli del blocco su [[Special:MyContributions|contributi dell'utenza]].\n\nIl blocco è stato effettuato da $1.\n\nLa ragione data è <em>$2</em>.\n\n* Inizio del blocco: $8\n* Scadenza del blocco: $6\n* Destinatario del blocco: $7\n* ID Blocco #$5",
        "blockedtext": "<strong>Il tuo nome utente o indirizzo IP è stato bloccato.</strong>\n\nIl blocco è stato imposto da $1. La motivazione del blocco è la seguente: <em>$2</em>.\n\n* Inizio del blocco: $8\n* Scadenza del blocco: $6\n* Intervallo di blocco: $7\n\nSe lo si desidera, è possibile contattare $1 o un altro [[{{MediaWiki:Grouppage-sysop}}|amministratore]] per discutere del blocco.\n\nSi noti che la funzione \"{{int:emailuser}}\" non è attiva se non è stato registrato un indirizzo email valido nelle proprie [[Special:Preferences|preferenze]] o se l'utilizzo di tale funzione è stato bloccato.\n\nL'indirizzo IP attuale è $3, il numero ID del blocco è #$5.\nSi prega di specificare tutti i dettagli precedenti in qualsiasi richiesta di chiarimenti.",
        "autoblockedtext": "Questo indirizzo IP è stato bloccato automaticamente perché condiviso con un altro utente, a sua volta bloccato da $1.\nLa motivazione del blocco è la seguente:\n\n:<em>$2</em>\n\n* Inizio del blocco: $8\n* Scadenza del blocco: $6\n* Intervallo di blocco: $7\n\nÈ possibile contattare $1 o un altro [[{{MediaWiki:Grouppage-sysop}}|amministratore]] per richiedere eventuali chiarimenti circa il blocco.\n\nSi noti che la funzione \"{{int:emailuser}}\" non è attiva se non è stato registrato un indirizzo e-mail valido nelle proprie [[Special:Preferences|preferenze]] e, comunque, se nell'applicare il blocco, tale funzione è stata disabilitata (per la durata del blocco).\n\nL'indirizzo IP attuale è $3, il numero ID del blocco è #$5\nSi prega di specificare tutti i dettagli qui inclusi nel compilare qualsiasi richiesta di chiarimenti.",
        "systemblockedtext": "Il tuo nome utente o l'indirizzo IP è stato bloccato automaticamente da MediaWiki.\nLa motivazione del blocco è la seguente:\n\n:''$2''\n\n* Inizio del blocco: $8\n* Scadenza del blocco: $6\n* Intervallo di blocco: $7\n\nL'indirizzo IP attuale è $3.\nSi prega di specificare tutti i dettagli qui inclusi nel compilare qualsiasi richiesta di chiarimenti.",
        "blockednoreason": "nessuna motivazione indicata",
+       "blockedtext-composite": "<strong>La tua utenza o indirizzo IP sono stati bloccati.</strong>\n\nLa ragione data è:\n\n:<em>$2</em>.\n\n* Inizio del blocco: $8\n* Scadenza del blocco più lungo: $6\n\n* $5\n\nIl tuo indirizzo IP attuale è $3.\nPer facore includi tutti i dettagli sopra riportati nelle richieste che farai.",
        "whitelistedittext": "Per modificare le pagine è necessario $1.",
        "confirmedittext": "Per essere abilitati alla modifica delle pagine è necessario confermare il proprio indirizzo e-mail. Per impostare e confermare l'indirizzo servirsi delle [[Special:Preferences|preferenze]].",
        "nosuchsectiontitle": "Impossibile trovare la sezione",
index 7764a21..ee0e787 100644 (file)
        "protectedinterface": "このページにはこのウィキのソフトウェアのインターフェイスに使用されるテキストが保存されており、いたずらなどの防止のために保護されています。\nすべてのウィキに対して翻訳を追加/変更する場合は、MediaWiki の地域化プロジェクト [https://translatewiki.net/wiki/Main_Page?setlang=ja translatewiki.net] を使用してください。",
        "editinginterface": "<strong>警告:</strong> ソフトウェアのインターフェイスに使用されるテキストのページを編集しています。\nこのページを変更すると、このウィキの他の利用者のユーザーインターフェイスの外観に影響します。",
        "translateinterface": "すべてのウィキに対して翻訳を追加/変更する場合は、MediaWiki の地域化プロジェクト [https://translatewiki.net/wiki/Main_Page?setlang=ja translatewiki.net] を使用してください。",
-       "cascadeprotected": "このページは、「カスケード保護」が指定された状態で保護されている以下の{{PLURAL:$1|ページ|ページ群}}でトランスクルードのため読み込まれているので、編集できないように保護されています:\n$2",
+       "cascadeprotected": "このページは、「カスケード保護」が指定された状態で保護されている以下の{{PLURAL:$1|ページ|ページ群}}で参照読み込みされているため、編集できないように保護されています:\n$2",
        "namespaceprotected": "<strong>$1</strong>名前空間にあるページを編集する権限がありません。",
        "customcssprotected": "この CSS ページは他の利用者の個人設定を含んでいるため、あなたには編集する権限がありません。",
        "customjsonprotected": "この JSON ページは他の利用者の個人設定を含んでいるため、あなたには編集する権限がありません。",
        "autoblockedtext": "このIPアドレスは、$1によりブロックされた利用者によって使用されたため、自動的にブロックされています。\n理由は次の通りです。\n\n:<em>$2</em>\n\n* ブロック開始日時: $8\n* ブロック解除予定: $6\n* ブロック対象: $7\n\n$1または他の[[{{MediaWiki:Grouppage-sysop}}|管理者]]にこのブロックについて問い合わせることができます。\n\nただし、[[Special:Preferences|個人設定]]に正しいメールアドレスが登録されていない場合、またはメール送信がブロックされている場合、「{{int:emailuser}}」機能を使用できないことに注意してください。\n\n現在ご使用中のIPアドレスは$3 、このブロックIDは#$5です。\nお問い合わせの際は、上記の情報を必ず書いてください。",
        "systemblockedtext": "あなたの利用者名またはIPアドレスはMediaWikiによって自動的にブロックされています。\n理由は次の通りです。\n\n:<em>$2</em>\n\n* ブロック開始日時: $8\n* ブロック解除予定: $6\n* ブロック対象: $7\n\nあなたの現在のIPアドレスは $3 です。\nお問い合わせの際は、上記の詳細情報をすべて含めてください。",
        "blockednoreason": "理由が設定されていません",
-       "blockedtext-composite": "<strong>あなたのアカウントまたはIPアドレスはブロックされています</strong>\n\n理由:\n\n:<em>$2</em>.\n\n* ブロック開始日: $8\n* ブロックの有効期限: $6\n\nあなたの現在のIPアドレスは$3です。\n上記の詳細は,ご質問にお答えください。",
+       "blockedtext-composite": "<strong>あなたのアカウントまたはIPアドレスはブロックされています</strong>\n\n理由:\n\n:<em>$2</em>.\n\n* ブロック開始日: $8\n* ブロックの有効期限: $6\n\n* $5\n\nあなたの現在のIPアドレスは$3です。\n上記の詳細は,ご質問にお答えください。",
        "blockedtext-composite-reason": "アカウントまたはIPアドレスに対して複数のブロックが存在します",
        "whitelistedittext": "このページを編集するには$1してください。",
        "confirmedittext": "ページの編集を始める前にメールアドレスの確認をする必要があります。\n[[Special:Preferences|個人設定]]でメールアドレスを設定し、確認を行ってください。",
        "search-interwiki-more-results": "結果をさらに取得",
        "search-relatedarticle": "関連",
        "searchrelated": "関連",
-       "searchall": "て",
+       "searchall": "すべて",
        "showingresults": "<strong>$2</strong> 件目以降の最大 {{PLURAL:$1|<strong>$1</strong> 件の結果}}を表示しています。",
        "showingresultsinrange": "<strong>$2</strong> 件目から<strong>$3</strong> 件目までの範囲内で最大 {{PLURAL:$1|<strong>$1</strong> 件の結果}}を表示しています。",
        "search-showingresults": "{{PLURAL:$4|<strong>$3</strong> 件中の <strong>$1</strong> 件目|<strong>$3</strong> 件中の <strong>$1</strong> 件目から <strong>$2</strong> 件目}}",
        "powersearch-ns": "名前空間を指定して検索:",
        "powersearch-togglelabel": "チェックを入れる:",
        "powersearch-toggleall": "すべて",
-       "powersearch-togglenone": "て外す",
+       "powersearch-togglenone": "すべて外す",
        "powersearch-remember": "この選択を今後の検索のために記憶させる",
        "search-external": "外部検索",
        "searchdisabled": "{{SITENAME}}の検索機能は無効化されています。\nさしあたってはGoogleなどで検索できます。\nただし外部の検索エンジンの索引にある{{SITENAME}}のコンテンツは古い場合があります。",
index f5ca1fe..1d1fab9 100644 (file)
        "category-subcat-count": "{{PLURAL:$2|ចំណាត់ថ្នាក់ក្រុមនេះមានតែកូនចំណាត់ថ្នាក់ក្រុមមួយដូចខាងក្រោមទេ។|ចំណាត់ថ្នាក់ក្រុមនេះមាន{{PLURAL:$1|កូនចំណាត់ថ្នាក់ក្រុមមួយ|កូនចំណាត់ថ្នាក់ក្រុមចំនួន$1}}ដូចខាងក្រោម ក្នុងចំណោមកូនចំណាត់ថ្នាក់ក្រុមសរុបចំនួន$2។}}",
        "category-subcat-count-limited": "ចំណាត់ថ្នាក់ក្រុមនេះមាន {{PLURAL:$1|កូនចំណាត់ថ្នាក់ក្រុមមួយ|កូនចំណាត់ថ្នាក់ក្រុមចំនួន$1}}ដូចខាងក្រោម។",
        "category-article-count": "{{PLURAL:$2|ចំណាត់ថ្នាក់ក្រុមនេះមានទំព័រមួយដូចខាងក្រោម។|{{PLURAL:$1|ទំព័រមួយ|ទំព័រចំនួន$1}}ក្នុងចំណោមទំព័រសរុប $2 ដូចខាងក្រោមស្ថិតក្នុងចំណាត់ថ្នាក់ក្រុមនេះ។}}",
-       "category-article-count-limited": "{{PLURAL:$1|ទំព័រ|ទំព័រចំនួន$1}}ខាងក្រោមស្ថិតនៅក្នុងចំណាត់ថ្នាក់ក្រុមនេះ។",
+       "category-article-count-limited": "{{PLURAL:$1|ទំព័រ|ទំព័រចំនួន$1}}ខាងក្រោម ស្ថិតនៅក្នុងចំណាត់ថ្នាក់ក្រុមនេះ។",
        "category-file-count": "{{PLURAL:$2|ចំណាត់ថ្នាក់ក្រុមនេះមានឯកសារមួយដូចខាងក្រោម។|{{PLURAL:$1|ឯកសារមួយ|ឯកសារចំនួន$1}}ក្នុងចំណោមឯកសារសរុប $2 ដូចខាងក្រោមស្ថិតនៅក្នុងចំណាត់ថ្នាក់ក្រុមនេះ។}}",
        "category-file-count-limited": "{{PLURAL:$1|ឯកសារមួយ|ឯកសារចំនួន$1}}ដូចខាងក្រោមស្ថិតនៅក្នុងចំណាត់ថ្នាក់ក្រុមនេះ។",
        "listingcontinuesabbrev": "បន្ត",
index 3a4ba28..551e5be 100644 (file)
        "autoblockedtext": "당신의 IP 주소는 $1님이 차단한 사용자가 사용했던 IP이기 때문에 자동으로 차단되었습니다.\n차단된 이유는 다음과 같습니다:\n\n:<em>$2</em>\n\n* 차단이 시작된 시간: $8\n* 차단이 끝나는 시간: $6\n* 차단된 사용자: $7\n\n$1 또는 [[{{MediaWiki:Grouppage-sysop}}|다른 관리자]]에게 차단에 대해 문의할 수 있습니다.\n\n[[Special:Preferences|사용자 환경 설정]]에 올바른 이메일 주소가 있어야만 \"이메일 보내기\" 기능을 사용할 수 있습니다. 또한 이메일 보내기 기능이 차단되어 있으면 이메일을 보낼 수 없습니다.\n\n현재 IP 주소는 $3이고, 차단 ID는 #$5입니다.\n문의할 때에 이 정보를 같이 알려주세요.",
        "systemblockedtext": "당신의 사용자 이름 또는 IP 주소가 자동으로 미디어위키에 의해 차단되었습니다.\n이유는 다음과 같습니다:\n\n:<em>$2</em>\n\n* 차단 시작: $8\n* 차단 만료: $6\n* 차단 대상: $7\n\n당신의 현재 IP 주소는 $3입니다.\n문의에 대해 상기의 상세 설명을 모두 포함해 주십시오.",
        "blockednoreason": "이유를 입력하지 않음",
-       "blockedtext-composite": "<strong>당신의 사용자 이름 또는 IP 주소가 미디어위키에 의해 차단되었습니다.\n\n이유는 다음과 같습니다:\n\n:<em>$2</em>\n\n* 차단 시작: $8\n* 차단 만료: $6\n\n당신의 현재 IP 주소는 $3입니다.\n문의에 대해 상기의 상세 설명을 모두 포함해 주십시오.",
+       "blockedtext-composite": "<strong>당신의 사용자 이름 또는 IP 주소가 미디어위키에 의해 차단되었습니다.\n\n이유는 다음과 같습니다:\n\n:<em>$2</em>\n\n* 차단 시작: $8\n* 차단 만료: $6\n\n* $5\n\n당신의 현재 IP 주소는 $3입니다.\n문의에 대해 상기의 상세 설명을 모두 포함해 주십시오.",
        "whitelistedittext": "문서를 편집하기 전에 $1해야 합니다.",
        "confirmedittext": "문서를 고치려면 이메일 인증 절차가 필요합니다.\n[[Special:Preferences|사용자 환경 설정]]에서 이메일 주소를 입력하고 이메일 주소 인증을 해주시기 바랍니다.",
        "nosuchsectiontitle": "문단을 찾을 수 없음",
index 5dafc1a..f9b47fa 100644 (file)
        "autoblockedtext": "Вашата IP-адреса е автоматски блокирана бидејќи била користена од страна на друг корисник, кој бил блокиран од $1.\nДаденото образложение е следново:\n\n:<em>$2</em>\n\n* Почеток на блокирањето: $8\n* Истекување на блокирањето: $6\n* Со намера да се блокира: $7\n\nМоже да контактирате со $1 или некој друг [[{{MediaWiki:Grouppage-sysop}}|администратор]] за да разговарате во врска со ова блокирање.\n\nИмајте предвид дека можеби нема да можете да ја искористите можноста „{{int:emailuser}}“ доколку не е назначена важечка е-поштенска адреса во [[Special:Preferences|вашите нагодувања]] и ви е забрането користење на истата.\n\nВашата IP-адреса е $3, a назнака на блокирањетo е $5.\nВе молиме наведете ги овие подробности доколку реагирате на блокирањето.",
        "systemblockedtext": "Вашето корисничко име или IP-адреса е автоматски блокирано од МедијаВики.\nНаведената причина гласи:\n\n:<em>$2</em>\n\n* Почеток на блокот: $8\n* Истек на блокот: $6\n* Блокот е наменет за: $7\n\nВашата тековна IP-адреса гласи $3.\nПрепишете ги сите горенаведени поединости доколку сакате да се распрашате кај надлежните во врска со блокот.",
        "blockednoreason": "не е наведена причина",
-       "blockedtext-composite": "<strong>Вашето корисничко име или IP-адреса е блокирано.</strong>\n\nНаведената причина гласи:\n\n:<em>$2</em>.\n\n* Почеток на блокот: $8\n* Истек на најдолгиот блок: $6\n\nВашата тековна IP-адреса гласи $3.\nПрепишете ги сите горенаведени поединости доколку сакате да се распрашате кај надлежните во врска со блокот.",
+       "blockedtext-composite": "<strong>Вашето корисничко име или IP-адреса е блокирано.</strong>\n\nНаведената причина гласи:\n\n:<em>$2</em>.\n\n* Почеток на блокот: $8\n* Истек на најдолгиот блок: $6\n\n* $5\n\nВашата тековна IP-адреса гласи $3.\nПрепишете ги сите горенаведени поединости доколку сакате да се распрашате кај надлежните во врска со блокот.",
+       "blockedtext-composite-ids": "Релеватни назнаки на блоковите: $1 (вашата IP-адреса може воедно да е на црниот список)",
+       "blockedtext-composite-no-ids": "Вашата IP-адреса стои на повеќе црни списоци",
        "blockedtext-composite-reason": "Вашата сметка и/или IP-адреса има неколку блокови",
        "whitelistedittext": "Мора да сте $1 за да уредувате страници.",
        "confirmedittext": "Морате да ја потврдите вашата е-поштенска адреса пред да уредувате страници.\nПоставете ја и валидирајте ја вашата е-поштенска адреса преку вашите [[Special:Preferences|нагодувања]].",
        "edit-error-short": "Грешка: $1",
        "edit-error-long": "Грешки:\n\n$1",
        "specialmute": "Искл. известувања",
-       "specialmute-success": "Ð\9fÑ\80омениÑ\82е Ñ\81е Ñ\83Ñ\81пеÑ\88но Ð½Ð°Ð¿Ñ\80авени. Ð\9fогледаÑ\98Ñ\82е Ð³Ð¸ Ñ\81иÑ\82е Ð¸Ñ\81клÑ\83Ñ\87ени ÐºÐ¾Ñ\80иÑ\81ниÑ\86и Ð½Ð° [[Special:Preferences]].",
+       "specialmute-success": "Ð\9fÑ\80омениÑ\82е Ñ\81е Ñ\83Ñ\81пеÑ\88но Ð½Ð°Ð¿Ñ\80авени. Ð\9fогледаÑ\98Ñ\82е Ð³Ð¸ Ñ\81иÑ\82е Ð¸Ñ\81клÑ\83Ñ\87ени ÐºÐ¾Ñ\80иÑ\81ниÑ\86и Ð²Ð¾ [[Special:Preferences|ваÑ\88иÑ\82е Ð½Ð°Ð³Ð¾Ð´Ñ\83ваÑ\9aа]].",
        "specialmute-submit": "Потврди",
        "specialmute-label-mute-email": "Исклучи е-пошта од корисников",
        "specialmute-header": "Изберете поставки за известувања од {{BIDI:[[User:$1]]}}.",
        "specialmute-error-email-preferences": "Ќе мора да ја потврдите вашата е-пошта пред да исклучите известувања од други. Тоа се прави на страницата [[Special:Preferences]].",
        "specialmute-email-footer": "За нагодување на поставките за {{BIDI:$2}}, појдете на <$1>.",
        "specialmute-login-required": "Најавете се за да ги направите промените.",
+       "mute-preferences": "Нагодувања за исклучување",
        "revid": "преработка $1",
        "pageid": "назнака на страницата $1",
        "interfaceadmin-info": "$1\n\nДозволите за уредување на CSS/JS/JSON податотеки низ цело вики неодамна се одвоени од правото <code>editinterface</code>. Ако не разбирате зошто ја добивате оваа грешка, погл. [[mw:MediaWiki_1.32/interface-admin]].",
index 5664207..8b4341b 100644 (file)
        "autoblockedtext": "Alamat IP anda telah disekat secara automatik kerana ia telah digunakan oleh pengguna lain, yang telah disekat oleh $1.\nSebab yang diberikan ialah:\n\n:<em>$2</em>\n\n* Mula sekatan: $8\n* Tarikh mansuh sekatan: $6\n* Pengguna sasaran: $7\n\nAnda boleh menghubungi $1 atau salah seorang [[{{MediaWiki:Grouppage-sysop}}|penyelia]] yang lain untuk membincangkan sekatan tersebut.\n\nSila ambil perhatian bahawa anda tidak boleh menggunakan ciri \"{{int:emailuser}}\" kecuali anda mempunyai alamat e-mel yang sah yang didaftarkan dalam [[Special:Preferences|keutamaan pengguna]] anda dan anda tidak disekat dari menggunakannya.\n\nAlamat IP semasa anda ialah $3, dan ID sekatan ialah #$5.\nSila sertakan semua butiran di atas dalam sebarang pertanyaan yang anda buat.",
        "systemblockedtext": "Nama pengguna atau alamat IP anda telah diblok secara automatik oleh MediaWiki. Alasan diberikan ialah:\n\n:<em>$2</em>\n\n* Mula blok: $8\n* Luput blok: $6\n* Yang diblok yang dimaksudkan: $7\n\nAlamat IP anda sekarang ialah $3.\nSila sertakan butiran di atas dalam sebarang pertanyaan yang anda buat.",
        "blockednoreason": "tiada sebab diberikan",
-       "blockedtext-composite": "<strong>Nama pengguna atau alamat IP anda telah disekat.</strong>\n\nSebab yang diberikan ialah:\n\n:<em>$2</em>.\n\n* Mula sekatan: $8\n* Tarikh mansuh sekatan terpanjang: $6\n\nAlamat IP semasa anda ialah $3.\nSila sertakan semua butiran di atas dalam sebarang pertanyaan yang anda buat.",
+       "blockedtext-composite": "<strong>Nama pengguna atau alamat IP anda telah disekat.</strong>\n\nSebab yang diberikan ialah:\n\n:<em>$2</em>.\n\n* Mula sekatan: $8\n* Tarikh mansuh sekatan terpanjang: $6\n\n* $5\n\nAlamat IP semasa anda ialah $3.\nSila sertakan semua butiran di atas dalam sebarang pertanyaan yang anda buat.",
        "blockedtext-composite-reason": "Terdapat sekatan berganda terhadap akaun dan/atau alamat IP anda.",
        "whitelistedittext": "Anda hendaklah $1 terlebih dahulu untuk menyunting laman.",
        "confirmedittext": "Anda perlu mengesahkan alamat e-mel anda terlebih dahulu untuk menyunting mana-mana laman. Sila tetapkan dan sahkan alamat e-mel anda melalui [[Special:Preferences|laman keutamaan]].",
index 3f36ff2..dd8c6d2 100644 (file)
        "blockedtitle": "အသုံးပြုသူကို ပိတ်ပင်ထားသည်",
        "blockedtext": "<strong>သင်၏ အသုံးပြုသူအမည် သို့မဟုတ် အိုင်ပီလိပ်စာသည် ပိတ်ပင်ခြင်း ခံထားရသည်။</strong>\n\nဤပိတ်ပင်မှုအား $1 က ဆောင်ရွက်ခဲ့သည်။\nအကြောင်းရင်းမှာ <em>$2</em> ဖြစ်သည်။\n\n* ပိတ်ပင်ခြင်း စတင်ချိန်: $8\n* ပိတ်ပင်ခြင်း သက်တမ်းကုန်ချိန်: $6\n* ရည်ရွယ်ရာ blockee: $7\n\nသင်သည် ပိတ်ပင်မှုအတွက် ဆွေးနွေးရန် $1 သို့မဟုတ် အခြား [[{{MediaWiki:Grouppage-sysop}}|စီမံခန့်ခွဲသူ]] အား ဆက်သွယ်နိုင်သည်။\nသင့်အနေဖြင့် [[Special:Preferences|အကောင့်၏ ရွေးချယ်စရာများ]]ထဲတွင် ရေရာသော အီးမေးလိပ်စာအား မထည့်သွင်းထားပါက \"{{int:emailuser}}\" လုပ်ဆောင်ချက်ကို အသုံးပြုနိုင်မည် မဟုတ်ပါ။ အလားတူ ယင်းလုပ်ဆောင်ချက်ကို ပိတ်ပင်မခံရမှ လုပ်ဆောင်နိုင်မည်ဖြစ်သည်။\nသင်၏ လက်ရှိ အိုင်ပီလိပ်စာမှာ $3 ဖြစ်ပြီး၊ ပိတ်ပင်မှုအိုင်ဒီမှာ #$5 ဖြစ်သည်။\nသင်ပြုလုပ်မည့် စုံစမ်းမေးမြန်းမှုများတွင် အထက်ပါ အချက်များ အားလုံး ပါဝင်နေပါစေ။",
        "blockednoreason": "အကြောင်းပြချက် မပေးထားပါ",
-       "blockedtext-composite": "<strong>သင်၏အသုံးပြုသူအမည် သို့ အိုင်ပီလိပ်စာကို ပိတ်ပင်ထားပါသည်။</strong>\n\nပေးထားသော အကြောင်းပြချက်မှာ:\n\n:<em>$2</em>။\n\n* စတင်ပိတ်ပင်ခြင်း: $8\n* ပိတ်ပင်မှု ကုန်ဆုံးရက်: $6\n\nသင်၏လက်ရှိအိုင်ပီလိပ်စာမှာ $3 ဖြစ်သည်။\nသင် တောင်းဆိုချက်များလုပ်ဆောင်ပါက အထက်ပါအချက်အလက်များကို ထည့်သွင်းပါ။",
+       "blockedtext-composite": "<strong>သင်၏အသုံးပြုသူအမည် သို့ အိုင်ပီလိပ်စာကို ပိတ်ပင်ထားပါသည်။</strong>\n\nပေးထားသော အကြောင်းပြချက်မှာ:\n\n:<em>$2</em>။\n\n* စတင်ပိတ်ပင်ခြင်း: $8\n* ပိတ်ပင်မှု ကုန်ဆုံးရက်: $6\n\n* $5\n\nသင်၏လက်ရှိအိုင်ပီလိပ်စာမှာ $3 ဖြစ်သည်။\nသင် တောင်းဆိုချက်များလုပ်ဆောင်ပါက အထက်ပါအချက်အလက်များကို ထည့်သွင်းပါ။",
        "whitelistedittext": "စာမျက်နှာများကို တည်းဖြတ်ရန် $1ရမည်။",
        "nosuchsectiontitle": "အပိုင်းကို ရှာမရနိုင်ပါ",
        "loginreqtitle": "လော့ဂ်အင်ဝင်ထားရန် လိုသည်",
        "ipbreason-dropdown": "*ယေဘုယျ ပိတ်ပင်တားဆီးရခြင်း အကြောင်းပြချက်များ\n** မှားယွင်းအချက်အလက်များကို ထည့်သွင်းမှု\n** စာမျက်နှာများမှ အကြောင်းအရာကို ဖယ်ရှားမှု\n** ပြင်ပဆိုဒ်များသို့လင့်ခ်ချိတ်၍ ဖွမှု\n** စာမျက်နှာများတွင် ပေါက်တတ်ကရများ ထည့်သွင်းမှု\n** ခြိမ်းခြောက်ခြင်း အပြုအမူ/အနှောက်အယှက်ပေးခြင်း\n** အကောင့်များစွာကို အလွဲသုံးစားလုပ်မှု\n** လက်ခံနိုင်ဖွယ်မရှိသော အသုံးပြုသူအမည်",
        "ipb-hardblock": "ဤအိုင်ပီလိပ်စာမှ လော့ဂ်အင်ဝင်ထားသော အသုံးပြုသူများကို တည်းဖြတ်ခြင်းမှ တားမြစ်ရန်",
        "ipbcreateaccount": "အကောင့်ဖန်တီးခြင်း",
-       "ipbemailban": "á\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\9cá\80ºá\80\95á\80­á\80¯á\80·á\80\94á\80±á\80\9eá\80\8aá\80º",
+       "ipbemailban": "á\80¡á\80®á\80¸á\80\99á\80±á\80¸á\80\9cá\80ºá\80\95á\80­á\80¯á\80·á\80\81á\80¼á\80\84á\80ºá\80¸",
        "ipbenableautoblock": "ဤအသုံးပြုသူ အသုံးပြုသော အိုင်ပီလိပ်စာနှင့် သူတို့ ပြင်ဆင်ရန် ကြိုးစားသည့် နောက်ဆက်တွဲ အိုင်ပီလိပ်စာများကိုပါ အလိုအလျောက်ပိတ်ပင်ရန်",
        "ipbsubmit": "ဤအသုံးပြုသူကို ပိတ်ပင်ရန်",
        "ipbother": "အခြားအချိန်:",
        "ipboptions": "၂ နာရီ:2 hours,၁ ရက်:1 day,၃ ရက်:3 days,၁ ပတ်:1 week,၂ ပတ်:2 weeks,၁ လ:1 month,၃ လ:3 months,၆ လ:6 months,၁ နှစ်:1 year,အနန္တ:infinite",
        "ipbhidename": "အသုံးပြုသူအမည်ကို တည်းဖြတ်မှုများနှင့် စာရင်းမှထဲတွင် ဝှက်ထားရန်",
        "ipbwatchuser": "ဤအသုံးပြုသူ၏ စာမျက်နှာနှင့် ဆွေးနွေးချက်တို့ကို စောင့်ကြည့်ရန်",
-       "ipb-disableusertalk": "သူတို့၏ ကိုယ်ပိုင်ဆွေးနွေးချက် စာမျက်နှာကို ပြင်ဆင်ခြင်းနေသည်",
+       "ipb-disableusertalk": "သူတို့၏ ကိုယ်ပိုင်ဆွေးနွေးချက် စာမျက်နှာကို ပြင်ဆင်ခြင်း",
        "ipb-change-block": "အသုံးပြုသူအား ဤအပြင်အဆင်များဖြင့် ထပ်မံပိတ်ပင်ရန်",
        "ipb-confirm": "ပိတ်ပင်မှုကို အတည်ပြု",
        "ipb-partial": "တစ်စိတ်တစ်ပိုင်း",
        "ipb-blocklist": "ရှိနှင့်ပြီးသား ပိတ်ပင်မှုများကို ကြည့်ရန်",
        "ipb-blocklist-contribs": "{{GENDER:$1|$1}} အတွက် ဆောင်ရွက်ချက်များ",
        "ipb-blocklist-duration-left": "$1 ကျန်ရှိ",
+       "block-actions": "ပိတ်ပင်ရန် ဆောင်ရွက်ချက်များ:",
        "block-expiry": "သက်တမ်းကုန်လွန်ရက် -",
        "block-prevent-edit": "တည်းဖြတ်ခြင်း",
        "block-reason": "အ​ကြောင်း​ပြ​ချက်:",
        "watchlistedit-normal-explain": "သင်၏ စောင့်ကြည့်စာရင်းရှိ ခေါင်းစဉ်များကို အောက်တွင် ပြသထားသည်။\nခေါင်းစဉ်တစ်ခုကို ဖယ်ရှားရန် ထိုခေါင်းစဉ်ဘေးရှိ အကွက်တွင် အမှန်ခြစ်ပြီး \"{{int:Watchlistedit-normal-submit}}\" ကိုနှိပ်ပါ။\n[[Special:EditWatchlist/raw|စောင့်ကြည့်စာရင်း အကြမ်း]]ကိုလည်း ကြည့်နိုင်ပါသည်။",
        "watchlistedit-normal-submit": "ခေါင်းစဉ်များကို ဖယ်ရှားရန်",
        "watchlistedit-normal-done": "{{PLURAL:$1|ခေါင်းစဉ်တစ်ခု|ခေါင်းစဉ် $1 ခုတို့}}ကို သင်၏ စောင့်ကြည့်စာရင်းမှ ဖယ်ရှားပြီးပြီ:",
+       "watchlistedit-raw-title": "စောင့်ကြည့်စာရင်း အကြမ်းကို တည်းဖြတ်ရန်",
+       "watchlistedit-raw-legend": "စောင့်ကြည့်စာရင်း အကြမ်းကို တည်းဖြတ်ရန်",
+       "watchlistedit-raw-explain": "သင်၏ စောင့်ကြည့်စာရင်းကို အောက်တွင်ဖော်ပြထားသည်၊ မျဉ်းကြောင်းတစ်ခုလျင် ခေါင်းစဉ်တစ်ခုချင်းစီ စာရင်းမှ ပေါင်းထည့်/ဖယ်ရှားခြင်းဖြင့် တည်းဖြတ်နိုင်ပါသည်။ ပြီးပါက \"{{int:Watchlistedit-raw-submit}}\" ကိုနှိပ်ပါ။ [[Special:EditWatchlist|စံနှုန်း အယ်ဒီတာ]]ကိုလည်း အသုံးပြုနိုင်ပါသည်။",
        "watchlistedit-raw-titles": "ခေါင်းစဉ်များ -",
        "watchlistedit-raw-submit": "စောင့်ကြည့်စာရင်းကို မွမ်းမံရန်",
        "watchlistedit-raw-done": "သင်၏ စောင့်ကြည့်စာရင်းကို မွမ်းမံပြီးဖြစ်သည်။",
        "specialpage-securitylevel-not-allowed-title": "ခွင့်မပြုပါ",
        "cannotauth-not-allowed-title": "ခွင့်ပြုချက် ငြင်းပယ်လိုက်သည်",
        "cannotauth-not-allowed": "သင်သည် ဤစာမျက်နှာကို အသုံးပြုခွင့်မရှိပါ",
+       "changecredentials": "အထောက်အထားများကို ပြောင်းလဲရန်",
+       "changecredentials-submit": "အထောက်အထားများကို ပြောင်းလဲရန်",
+       "credentialsform-provider": "အထောက်အထားများ အမျိုးစား:",
        "credentialsform-account": "အကောင့်နာမည်-",
        "userjsispublic": "ကျေးဇူးပြု၍ မှတ်သားပါ- JavaScript စာမျက်နှာခွဲများတွင် အခြားအသုံးပြုသူများ ကြည့်ရှုနိုင်သော လျို့ဝှက်အပ်သည့်အချက်အလက် မပါဝင်သင့်ပါ။",
        "edit-error-short": "အမှား - $1",
index fb5a296..58c5bb6 100644 (file)
        "autoblockedtext": "Din IP-adresse har blitt automatisk blokkert fordi den ble brukt av en annen bruker som ble blokkert av $1.\nDen oppgitte grunnen var:\n\n:'''$2'''\n\n* Blokkeringen begynte: $8\n* Blokkeringen utgår: $6\n* Blokkeringen er ment for: $7\n\nDu kan kontakte $1 eller en av de andre [[{{MediaWiki:Grouppage-sysop}}|administratorene]] for å diskutere blokkeringen.\n\nMerk at du ikke kan bruke «{{int:emailuser}}»-funksjonen med mindre du har registrert en gyldig e-postadresse i [[Special:Preferences|innstillingene dine]].\n\nDin IP-adresse er $3, og blokkerings-ID-en er #$5.\nVennligst ta med all denne informasjonen ved henvendelser.",
        "systemblockedtext": "Ditt brukernavn eller IP-adresse har blitt blokkert automatisk av MediaWiki.\n\nBlokkeringen grunnes:\n\n:<em>$2</em>\n\n* Blokkeringen startet: $8\n* Blokkeringen gjelder til: $6\n* Blokkeringen er ment for: $7\n\nDin nåværende IP-adresse er $3.\nVennligst inkluder informasjonen over i alle spørsmål du spør angående dette.",
        "blockednoreason": "ingen grunn gitt",
-       "blockedtext-composite": "<strong>Brukernavnet ditt eller IP-adressa di har blitt blokkert.</strong>\n\nBlokkeringen grunnes:\n\n:<em>$2</em>\n\n* Blokkeringen startet: $8\n* Blokkeringen løper ut: $6\n\nIP-adressa di er $3.\nVennligst inkluder alle detaljene ovenfor i spørsmål du måtte ha angående dette.",
+       "blockedtext-composite": "<strong>Brukernavnet ditt eller IP-adressa di har blitt blokkert.</strong>\n\nBlokkeringen grunnes:\n\n:<em>$2</em>\n\n* Blokkeringen startet: $8\n* Blokkeringen løper ut: $6\n\n* $5\n\nIP-adressa di er $3.\nVennligst inkluder alle detaljene ovenfor i spørsmål du måtte ha angående dette.",
        "blockedtext-composite-reason": "Det foreligger flere blokkeringer på din konto og/eller IP-adresse",
        "whitelistedittext": "Du må $1 for å redigere artikler.",
        "confirmedittext": "Du må bekrefte e-postadressen din før du kan redigere sider. Vennligst oppgi og bekreft e-postadressen din via [[Special:Preferences|innstillingene dine]].",
index 34af306..62c46d5 100644 (file)
        "autoblockedtext": "Uw IP-adres is automatisch geblokkeerd, omdat het gebruikt is door een andere gebruiker, die geblokkeerd is door $1.\nDe opgegeven reden is:\n\n:''$2''\n\n* Aanvang blokkade: $8\n* Einde blokkade: $6\n* Bedoeld te blokkeren: $7\n\nU kunt contact opnemen met $1 of een andere [[{{MediaWiki:Grouppage-sysop}}|beheerder]] om de blokkade te bespreken.\n\nU kunt geen gebruik maken van de functie \"{{int:emailuser}}\", tenzij u een geldig e-mailadres hebt opgegeven in uw [[Special:Preferences|voorkeuren]], en het gebruik van deze functie niet is geblokkeerd.\n\nUw huidige IP-adres is $3 en het blokkadenummer is #$5.\nVermeld alle bovenstaande gegevens als u ergens op deze blokkade reageert.",
        "systemblockedtext": "Uw gebruikersaccount of IP-adres is automatisch geblokkeerd door MediaWiki.\nDe opgegeven reden is:\n\n:<em>$2</em>\n\n* Aanvang blokkade: $8\n* Einde blokkade: $6\n* Bedoeld te blokkeren: $7\n\nUw huidige IP-adres is $3.\nVermeld alle bovenstaande gegevens als u ergens op deze blokkade reageert.",
        "blockednoreason": "geen reden opgegeven",
-       "blockedtext-composite": "Uw gebruikersaccount of IP-adres is geblokkeerd.\n\nDe opgegeven reden is:\n\n:<em>$2</em>\n\n* Aanvang blokkade: $8\n* Einde van de langste blokkade: $6\n\nUw huidige IP-adres is $3.\nVermeld alle bovenstaande gegevens als u ergens op deze blokkade reageert.",
+       "blockedtext-composite": "Uw gebruikersaccount of IP-adres is geblokkeerd.\n\nDe opgegeven reden is:\n\n:<em>$2</em>\n\n* Aanvang blokkade: $8\n* Einde van de langste blokkade: $6\n\n* $5\n\nUw huidige IP-adres is $3.\nVermeld alle bovenstaande gegevens als u ergens op deze blokkade reageert.",
        "blockedtext-composite-reason": "Er zijn meerdere blokkades tegen uw account en/of IP-adres",
        "whitelistedittext": "U moet $1 om pagina's te bewerken.",
        "confirmedittext": "U moet uw e-mailadres bevestigen voor u kunt bewerken.\nVoer uw e-mailadres in en bevestig het via uw [[Special:Preferences|voorkeuren]].",
        "specialmute-error-email-preferences": "U moet uw e-mailadres bevestigen voordat u een gebruiker kunt negeren. U kunt dit doen in [[Special:Preferences|uw voorkeuren]].",
        "specialmute-email-footer": "Om uw e-mailvoorkeuren voor {{BIDI:$2}} te beheren gaat u naar <$1>.",
        "specialmute-login-required": "U moet aanmelden om voorkeuren voor het negeren van gebruikers in te stellen.",
+       "mute-preferences": "Negeervoorkeuren",
        "revid": "versie $1",
        "pageid": "Pagina-ID $1",
        "interfaceadmin-info": "$1\n\nRechten voor het bewerken van wikibrede CSS/JS/JSON-bestanden zijn recentelijk gescheiden van het <code>editinterface</code> recht. Als u niet begrijpt waarom u deze foutmelding te zien krijgt, ga dan naar [[mw:MediaWiki_1.32/interface-admin]].",
index 446174f..7f5cefe 100644 (file)
        "watchlisttools-edit": "Vis og endre overvakingslista",
        "watchlisttools-raw": "Endre på overvakingslista i råformat",
        "signature": "[[{{ns:user}}:$1|$2]] ([[{{ns:user_talk}}:$1|diskusjon]])",
-       "duplicate-defaultsort": "Åtvaring: Standardsorteringa «$2» tar over for den tidlegare sorteringa «$1».",
+       "duplicate-defaultsort": "<strong>Åtvaring:</strong> Standardsorteringa «$2» tar over for den tidlegare sorteringa «$1».",
        "restricted-displaytitle": "<strong>Åtvaring:</strong> Visingstittelen «$1» vart sett bort frå sidan han ikkje svarar til den faktiske tittelen til sida.",
        "version": "Versjon",
        "version-extensions": "Installerte utvidingar",
index cc77368..fe65276 100644 (file)
        "autoblockedtext": "Ten adres IP został zablokowany automatycznie, gdyż korzysta z niego inny użytkownik, zablokowany przez administratora $1.\nPowód blokady:\n\n:<em>$2</em>\n\n* Początek blokady: $8\n* Wygaśnięcie blokady: $6\n* Zablokowany został: $7\n\nMożesz skontaktować się z $1 lub jednym z pozostałych [[{{MediaWiki:Grouppage-sysop}}|administratorów]] w celu uzyskania informacji o blokadzie.\n\nNie możesz użyć funkcji „{{int:emailuser}}”, jeśli brak jest poprawnego adresu e‐mail w Twoich [[Special:Preferences|preferencjach]] lub jeśli taka możliwość została Ci zablokowana.\n\nTwój obecny adres IP to $3, a numer identyfikacyjny blokady to #$5.\nProsimy o podanie obu tych numerów przy wyjaśnianiu blokady.",
        "systemblockedtext": "Twoja nazwa użytkownika lub adres IP zostały automatycznie zablokowane przez MediaWiki.\nPodany powód to:\n\n:<em>$2</em>\n\n* Początek blokady: $8\n* Wygaśnięcie blokady: $6\n* Zamierzano zablokować: $7\n\nTwój obecny adres IP to $3.\nProsimy o dołączenie powyższych szczegółów w jakichkolwiek zadawanych pytaniach.",
        "blockednoreason": "nie podano przyczyny",
-       "blockedtext-composite": "<strong>Twoja nazwa użytkownika lub adres IP zostały zablokowane.</strong>\n\nPodany powód to:\n\n:<em>$2</em>\n\n* Początek blokady: $8\n* Wygaśnięcie blokady: $6\n\nTwój obecny adres IP to $3.\nProsimy o dołączenie powyższych szczegółów w jakichkolwiek zadawanych pytaniach.",
+       "blockedtext-composite": "<strong>Twoja nazwa użytkownika lub adres IP zostały zablokowane.</strong>\n\nPodany powód to:\n\n:<em>$2</em>\n\n* Początek blokady: $8\n* Wygaśnięcie blokady: $6\n\n* $5\n\nTwój obecny adres IP to $3.\nProsimy o dołączenie powyższych szczegółów w jakichkolwiek zadawanych pytaniach.",
        "blockedtext-composite-reason": "Na twoje konto i/lub adresy IP nałożono wiele blokad.",
        "whitelistedittext": "Musisz $1, by edytować strony.",
        "confirmedittext": "Edytowanie jest możliwe dopiero po zweryfikowaniu adresu e‐mail.\nPodaj adres e‐mail i potwierdź go w swoich [[Special:Preferences|ustawieniach użytkownika]].",
        "specialmute-error-email-preferences": "Musisz potwierdzić swój adres e-mail zanim będziesz {{GENDER:|mógł|mogła}} ignorować użytkownika. Możesz to zrobić w [[Special:Preferences|preferencjach]].",
        "specialmute-email-footer": "Aby zarządzać preferencjami ignorowania dla {{BIDI:$2}} odwiedź <$1>.",
        "specialmute-login-required": "Zaloguj się, aby zmienić swoje preferencje wyignorowania.",
+       "mute-preferences": "Ustawienia ignorowania",
        "revid": "wersja $1",
        "pageid": "ID strony: $1",
        "interfaceadmin-info": "$1\n\nUprawnienia do edycji plików CSS/JS/JSON całej witryny zostały wydzielone z dotychczasowego uprawnienia <code>editinterface</code>. Jeżeli nie rozumiesz, dlaczego otrzymujesz ten komunikat, przeczytaj [[mw:MediaWiki_1.32/interface-admin]].",
index 18d850a..544ba68 100644 (file)
        "autoblockedtext": "O seu endereço IP foi bloqueado de forma automática porque foi utilizado recentemente por outro usuário, o qual foi bloqueado por $1.\nO motivo apresentado foi:\n\n:<em>$2</em>\n\n* Início do bloqueio: $8\n* Expiração do bloqueio: $6\n* Destinatário do bloqueio: $7\n\nPode contactar $1 ou outro [[{{MediaWiki:Grouppage-sysop}}|administrador]] para discutir o bloqueio.\n\nNote que para utilizar a funcionalidade \"{{int:emailuser}}\" precisa de ter um endereço de e-mail válido nas suas [[Special:Preferences|preferências]] e de não lhe ter sido bloqueado o uso desta funcionalidade.\n\nO seu endereço IP neste momento é $3 e a identificação (ID) do bloqueio é #$5.\nInclua todos os detalhes acima em quaisquer contatos relacionados com este bloqueio, por favor.",
        "systemblockedtext": "O seu nome de usuário ou endereço IP foram bloqueados automaticamente pelo MediaWiki.\nO motivo fornecido é:\n\n:<em>$2</em>\n\n* Início do bloqueio: $8\n* Expiração do bloqueio: $6\n* Destinatário do bloqueio: $7\n\nO seu endereço IP atual é $3.\nInclua todos os detalhes acima em quaisquer contatos sobre este assunto, por favor.",
        "blockednoreason": "sem motivo especificado",
-       "blockedtext-composite": "<strong>Seu nome de usuário ou endereço IP foi bloqueado.</strong>\n\nO motivo fornecido é:\n\n:<em>$2</em>.\n\n* Início do bloqueio: $8\n* Expiração do bloqueio mais longo: $6\n\nSeu endereço IP atual é $3.\nPor favor inclua todos os detalhes acima em qualquer questão que você faça.",
+       "blockedtext-composite": "<strong>Seu nome de usuário ou endereço IP foi bloqueado.</strong>\n\nO motivo fornecido é:\n\n:<em>$2</em>.\n\n* Início do bloqueio: $8\n* Expiração do bloqueio mais longo: $6\n\n* $5\n\nSeu endereço IP atual é $3.\nPor favor inclua todos os detalhes acima em qualquer questão que você faça.",
+       "blockedtext-composite-ids": "IDs de bloqueio relevantes: $1 (seu endereço IP também pode estar na lista negra)",
+       "blockedtext-composite-no-ids": "Seu endereço IP aparece em várias listas negras",
        "blockedtext-composite-reason": "Existem vários bloqueios contra sua conta e/ou endereço IP",
        "whitelistedittext": "Você precisa $1 para poder editar páginas.",
        "confirmedittext": "Você precisa confirmar o seu endereço de e-mail antes de começar a editar páginas.\nPor favor, introduza um e valide-o através das suas [[Special:Preferences|preferências de usuário]].",
        "specialmute-error-email-preferences": "Você deve confirmar seu endereço de e-mail antes de poder silenciar um usuário. Você pode fazer isso de [[Special:Preferences]].",
        "specialmute-email-footer": "Para gerenciar as preferências de e-mail para {{BIDI:$2}} por favor visite <$1>.",
        "specialmute-login-required": "Por favor, entre para alterar suas preferências de mudo.",
+       "mute-preferences": "Preferências do silenciador",
        "revid": "revisão $1",
        "pageid": "ID da página $1",
        "interfaceadmin-info": "$1\n\nAs permissões para edição de arquivos CSS/JS/JSON em todo o site foram separadas recentemente do direito <code>editinterface</code>. Se você não entende porque está recebendo este erro, veja [[mw:MediaWiki_1.32/interface-admin]].",
index 7863e0c..b92e10f 100644 (file)
        "autoblockedtext": "O seu endereço IP foi bloqueado de forma automática porque foi utilizado recentemente por outro utilizador, o qual foi bloqueado por $1.\nO motivo apresentado foi:\n\n:<em>$2</em>\n\n* Início do bloqueio: $8\n* Expiração do bloqueio: $6\n* Destinatário do bloqueio: $7\n\nPode contactar $1 ou outro [[{{MediaWiki:Grouppage-sysop}}|administrador]] para discutir o bloqueio.\n\nNote que para utilizar a funcionalidade \"{{int:emailuser}}\" precisa de ter um endereço de correio eletrónico válido nas suas [[Special:Preferences|preferências]] e de não lhe ter sido bloqueado o uso desta funcionalidade.\n\nO seu endereço IP neste momento é $3 e a identificação (ID) do bloqueio é #$5.\nInclua todos os detalhes acima em quaisquer contactos relacionados com este bloqueio, por favor.",
        "systemblockedtext": "O seu nome de utilizador ou endereço IP foram bloqueados automaticamente pelo MediaWiki.\nO motivo fornecido é:\n\n:<em>$2</em>\n\n* Início do bloqueio: $8\n* Expiração do bloqueio: $6\n* Destinatário do bloqueio: $7\n\nO seu endereço IP atual é $3.\nInclua todos os detalhes acima em quaisquer contactos sobre este assunto, por favor.",
        "blockednoreason": "sem motivo especificado",
-       "blockedtext-composite": "<strong>O seu nome de utilizador ou endereço IP foram bloqueados.</strong>\n\nO motivo fornecido é:\n\n:<em>$2</em>.\n\n* Início do bloqueio: $8\n* Expiração do bloqueio mais longo: $6\n\nO seu endereço IP atual é $3.\nInclua todos os detalhes acima em quaisquer contactos sobre este assunto, por favor.",
+       "blockedtext-composite": "<strong>O seu nome de utilizador ou endereço IP foram bloqueados.</strong>\n\nO motivo fornecido é:\n\n:<em>$2</em>.\n\n* Início do bloqueio: $8\n* Expiração do bloqueio mais longo: $6\n\n* $5\n\nO seu endereço IP atual é $3.\nInclua todos os detalhes acima em quaisquer contactos sobre este assunto, por favor.",
        "blockedtext-composite-reason": "Existem vários bloqueios da sua conta ou endereço IP",
        "whitelistedittext": "Precisa de $1 para poder editar páginas.",
        "confirmedittext": "Precisa de confirmar o seu endereço de correio eletrónico antes de começar a editar páginas.\nIntroduza e valide o endereço através das suas [[Special:Preferences|preferências de utilizador]], por favor.",
        "edit-error-short": "Erro: $1",
        "edit-error-long": "Erros:\n\n$1",
        "specialmute": "Silenciamento",
-       "specialmute-success": "As suas preferências de silenciamento foram atualizadas. Ver todos os utilizadores silenciados em [[Special:Preferences]].",
+       "specialmute-success": "As suas preferências de silenciamento foram atualizadas. Ver todos os utilizadores silenciados nas [[Special:Preferences|suas preferências]].",
        "specialmute-submit": "Confirmar",
        "specialmute-label-mute-email": "Silenciar os correios eletrónicos deste utilizador",
        "specialmute-header": "Selecione as suas preferências de silenciamento para {{BIDI:[[User:$1]]}}, por favor.",
        "specialmute-error-email-preferences": "Tem de confirmar o seu endereço de correio eletrónico antes de poder silenciar um utilizador. Pode fazê-lo em [[Special:Preferences]].",
        "specialmute-email-footer": "Para gerir as preferências de correio eletrónico de {{BIDI:$2}} visite <$1>, por favor.",
        "specialmute-login-required": "Para alterar as suas preferências de silenciamento, inicie uma sessão, por favor.",
+       "mute-preferences": "Preferências de silenciamento",
        "revid": "revisão $1",
        "pageid": "identificador de página $1",
        "interfaceadmin-info": "$1\n\nAs permissões de edição de ficheiros CSS/JS/JSON que afetam todo o ''site'' foram recentemente separadas do privilégio <code>editinterface</code>. Se não compreende porque está a receber este erro, consulte [[mw:MediaWiki_1.32/interface-admin]].",
index 67c16cc..95e04b0 100644 (file)
        "right-editsitejs": "{{doc-right|editsitejs}}",
        "right-editmyusercss": "{{doc-right|editmyusercss}}\nSee also:\n* {{msg-mw|Right-editusercss}}",
        "right-editmyuserjson": "{{doc-right|editmyuserjson}}\nSee also:\n* {{msg-mw|Right-edituserjson}}",
-       "right-editmyuserjs": "{{doc-right|editmyuserjs}}\nSee also:\n* {{msg-mw|Right-edituserjs}}",
+       "right-editmyuserjs": "{{doc-right|editmyuserjs}}\nSee also:\n* {{msg-mw|Right-edituserjs}}\n* {{msg-mw|Right-editmyuserjsredirect}}",
+       "right-editmyuserjsredirect": "{{doc-right|editmyuserjsredirect}}\nSame as {{msg-mw|Right-editmyuserjs}} except if page is a redirect.\n\nSee also:\n* {{msg-mw|Right-edituserjs}}",
        "right-viewmywatchlist": "{{doc-right|viewmywatchlist}}",
        "right-editmywatchlist": "{{doc-right|editmywatchlist}}",
        "right-viewmyprivateinfo": "{{doc-right|viewmyprivateinfo}}",
        "action-editmyusercss": "{{doc-action|editmyusercss}}",
        "action-editmyuserjson": "{{doc-action|editmyuserjson}}",
        "action-editmyuserjs": "{{doc-action|editmyuserjs}}",
+       "action-editmyuserjsredirect": "{{doc-action|editmyuserjsredirect}}",
        "action-viewsuppressed": "{{doc-action|viewsuppressed}}",
        "action-hideuser": "{{doc-action|hideuser}}",
        "action-ipblock-exempt": "{{doc-action|ipblock-exempt}}",
        "specialmute-label-mute-email": "Label for the checkbox that mutes/unmutes emails from the specified user.",
        "specialmute-header": "Used as header text on [[Special:Mute]]. Shown before the form with the muting options.\n* $1 - User selected for muting",
        "specialmute-error-invalid-user": "Error displayed when the username cannot be found.",
-       "specialmute-error-email-blacklist-disabled": "Error displayed when email blacklist is not enabled.",
-       "specialmute-error-email-preferences": "Error displayed when the user has not confirmed their email address.",
+       "specialmute-error-no-options": "Error displayed when there are no options available to mute on [[Special:Mute]].",
        "specialmute-email-footer": "Email footer in plain text linking to [[Special:Mute]] preselecting the sender to manage muting options.\n* $1 - Url linking to [[Special:Mute]].\n* $2 - The user sending the email.",
        "specialmute-login-required": "Error displayed when a user tries to access [[Special:Mute]] before logging in.",
        "mute-preferences": "Link in the sidebar to manage muting preferences for a user. It links to [[Special:Mute]] with the user in context as the subpage.",
        "passwordpolicies-policy-passwordnotinlargeblacklist": "Password policy that enforces that a password is not in a list of 100,000 number of \"popular\" passwords.",
        "passwordpolicies-policyflag-forcechange": "Password policy flag that enforces changing invalid passwords on login.",
        "passwordpolicies-policyflag-suggestchangeonlogin": "Password policy flag that suggests changing invalid passwords on login.",
+       "mycustomjsredirectprotected": "Error message shown when user tries to edit their own JS page that is a foreign redirect without the 'mycustomjsredirectprotected' right. See also {{mw-msg|mycustomjsprotected}}.",
        "easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly",
        "unprotected-js": "Error message shown when trying to load javascript via action=raw that is not protected",
        "userlogout-continue": "Shown if user attempted to log out without a token specified. Probably the user clicked on an old link that hasn't been updated to use the new system. $1 - url that user should click on in order to log out."
index 3783e26..4e53a38 100644 (file)
        "autoblockedtext": "Ваш IP-адрес автоматически заблокирован в связи с тем, что он ранее использовался кем-то из участников, заблокированных администратором $1. \nБыла указана следующая причина блокировки:\n\n: «$2».\n\n* Начало блокировки: $8\n* Окончание блокировки: $6\n* Цель блокировки: $7\n\nВы можете связаться с $1 или любым другим [[{{MediaWiki:Grouppage-sysop}}|администратором]], чтобы обсудить блокировку.\n\nОбратите внимание, что вы не сможете использовать функцию «{{int:emailuser}}», если в своих [[Special:Preferences|персональных настройках]] не задали или не подтвердили корректный адрес электронной почты, или если ваша блокировка включает запрет отправки писем подобным образом.\n\nВаш IP-адрес — $3, идентификатор блокировки — #$5.\nПожалуйста, указывайте эти сведения в любых своих обращениях.",
        "systemblockedtext": "Ваше имя участника или IP-адрес были автоматически заблокированы MediaWiki.\nУказана следующая причина:\n\n:<em>$2</em>\n\n* Начало блокировки: $8\n* Окончание блокировки: $6\n* Цель блокировки: $7\n\nВаш текущий IP-адрес $3.\nПожалуйста, указывайте все эти сведения в любых своих обращениях.",
        "blockednoreason": "причина не указана",
-       "blockedtext-composite": "<strong>Ваше имя участника или IP-адрес были заблокированы.</strong>\n\nУказана следующая причина:\n\n:<em>$2</em>\n\n* Начало блокировки: $8\n* Окончание блокировки: $6\n\nВаш текущий IP-адрес $3.\nПожалуйста, указывайте все эти сведения в любых своих обращениях.",
+       "blockedtext-composite": "<strong>Ваше имя участника или IP-адрес были заблокированы.</strong>\n\nУказана следующая причина:\n\n:<em>$2</em>\n\n* Начало блокировки: $8\n* Окончание блокировки: $6\n\n* $5\n\nВаш текущий IP-адрес $3.\nПожалуйста, указывайте все эти сведения в любых своих обращениях.",
        "blockedtext-composite-reason": "Есть несколько блокировок вашей учётной записи и/или IP-адреса",
        "whitelistedittext": "Вы должны $1 для изменения страниц.",
        "confirmedittext": "Вы должны подтвердить свой адрес электронной почты перед правкой страниц.\nПожалуйста, введите и подтвердите свой адрес электронной почты в своих [[Special:Preferences|персональных настройках]].",
index 7cc15a5..92e10f0 100644 (file)
        "createacct-yourpasswordagain-ph": "Уведьте гесло знову",
        "userlogin-remembermypassword": "Приголосити ня на довго",
        "userlogin-signwithsecure": "Хосновати забеспечене споїня",
+       "cannotcreateaccount-title": "Не є можне вытворїня конт",
        "yourdomainname": "Ваша домена:",
        "password-change-forbidden": "На тій вікі не можете мінити гесла.",
        "externaldberror": "Або ся стала хыба екстерной автентіфікачной датабазы, або не маєте дозволено мінити своє екстерне конто.",
        "sig_tip": "Ваш підпис з датумом і часом",
        "hr_tip": "Горізонтална лінія (хоснуйте єй скупо)",
        "summary": "Куртый опис змін:",
-       "subject": "Тема/надпис:",
+       "subject": "Тема:",
        "minoredit": "Незначна зміна",
        "watchthis": "Слїдовати тоту сторінку",
        "savearticle": "Уложыти сторінку",
+       "savechanges": "Уложыти зміны",
+       "savearticle-start": "Уложыти сторінку…",
+       "savechanges-start": "Уложыти зміны…",
        "preview": "Нагляд",
        "showpreview": "Указати нагляд",
        "showdiff": "Указати зміны",
        "permissionserrorstext": "Не маєте поволїня той операції з  {{PLURAL:$1|такой причіны|такых причін}}:",
        "permissionserrorstext-withaction": "Не маєте дозволїня на $2 з {{PLURAL:$1|такой прічіны|такых прічін}}:",
        "recreate-moveddeleted-warn": "'''Увага: Пробуєте знову створити сторінку, котра была в минулости змазана.'''\n\nУважте, ці справды треба знову створити тоту сторінку.\nНиже видите запис переменовань і змазань той сторінкы:",
-       "moveddeleted-notice": "Тота сторінка была змазана.\nЗапис змазаня а переменованя сі можете посмотрити ниже.",
+       "moveddeleted-notice": "Тота сторінка была змазана.\nЗапис змазаня, замкнутя а переменованя сі можете посмотрити ниже.",
        "log-fulllog": "Зобразити вшыток запис",
        "edit-hook-aborted": "Едітованя было сторноване процедуров без близшого пояснїня.",
        "edit-gone-missing": "Сторінку ся не вдало обновити.\nАсі была змазана.",
        "content-failed-to-parse": "Не вдало ся проаналізовати $2 як тіп $1: $3",
        "invalid-content-data": "Неприпустны даны",
        "content-not-allowed-here": "Обсяг «$1» недозволеный на сторінцї [[:$2]]",
-       "editwarning-warning": "Зохаблїнём той стрінкы ся можуть вшыткы учінены зміны стратити.\nКідь сьте приголошеный, можете тото варованя выпнути на картї „Едітованя“ в хосновательскім інтерфейсї.",
+       "editwarning-warning": "Зохаблїнём той стрінкы ся можуть вшыткы учінены зміны стратити.\nКідь сьте приголошеный, можете тото варованя выпнути на картї „{{int:prefs-editing}}“ в хосновательскім інтерфейсї.",
+       "editpage-invalidcontentmodel-title": "Непідпорованый модел обсягу",
        "editpage-notsupportedcontentformat-title": "Непідпорованый формат обсягу",
        "slot-name-main": "Головна",
        "content-model-wikitext": "вікітекст",
        "page_first": "перша",
        "page_last": "послїдня",
        "histlegend": "(теперь) = роздїлы од актуалной верзії,\n(послїдня) = роздїлы од попереднёй верзії, '''м''' = мале едітованя",
-       "history-fieldset-title": "Ð\9fеÑ\80еглÑ\8fднÑ\83Ñ\82и Ñ\96Ñ\81Ñ\82оÑ\80Ñ\96Ñ\8e",
-       "history-show-deleted": "Лем змазаны",
+       "history-fieldset-title": "ФÑ\96лÑ\8cÑ\82Ñ\80оваÑ\82и Ñ\80евÑ\96зÑ\96Ñ\97",
+       "history-show-deleted": "Лем змазаны ревізії",
        "histfirst": "найстаршы",
        "histlast": "найновшы",
        "historysize": "($1 {{PLURAL:$1|байт|байты|байтів}})",
-       "historyempty": "(порожнє)",
+       "historyempty": "порожня",
        "history-feed-title": "Історія едітовань",
        "history-feed-description": "Історія едітовань той сторінкы на вікі",
        "history-feed-item-nocomment": "$1 в $2",
        "revdelete-legend": "Наставити обмеджіня ревізії",
        "revdelete-hide-text": "Текст ревізії",
        "revdelete-hide-image": "Сховати обсяг файлу",
-       "revdelete-hide-name": "Сховати дїю а ціль",
-       "revdelete-hide-comment": "СÑ\85оваÑ\82и ÐºÐ¾Ð¼ÐµÐ½Ñ\82аÑ\80Ñ\8c",
+       "revdelete-hide-name": "Сховати ціль і параметры",
+       "revdelete-hide-comment": "Ð\9eпиÑ\81 Ð·Ð¼Ñ\96н",
        "revdelete-hide-user": "Імя автора/IP адреса",
        "revdelete-hide-restricted": "Затаїти дата перед адміністраторами",
        "revdelete-radio-same": "(не мінити)",
        "prefs-editwatchlist": "Едітованя списку слїдованых сторінок",
        "prefs-watchlist-days": "Чісло днїв зображеных в слїдованых сторінках:",
        "prefs-watchlist-days-max": "Максімално $1 {{PLURAL:$1|день|днї|днїв}}",
-       "prefs-watchlist-edits": "Чісло едітовань зображеных во вылїпшенім списку слїдованых сторінок:",
+       "prefs-watchlist-edits": "Чісло едітовань зображеных в списку слїдованых сторінок:",
        "prefs-watchlist-edits-max": "Максімалне чісло: 1000",
        "prefs-watchlist-token": "Ключ до списку слїдованых сторінок:",
        "prefs-misc": "Іншы наставлїня",
        "prefs-resetpass": "Змінити гесло",
-       "prefs-changeemail": "Змінити імейл",
+       "prefs-changeemail": "Змінити або выдалити імейл",
        "prefs-setemail": "Наставити імейлову адресу",
        "prefs-email": "Параметры електронічной пошты",
        "prefs-rendering": "Взгляд",
        "restoreprefs": "Обновити вшыткы штандартны наставлїня (у вшыткых секціях)",
        "prefs-editing": "Едітованя",
        "searchresultshead": "Гляданя",
-       "stub-threshold": "Поріг про форматованя одказу як <a href=\"#\" class=\"stub\">одказів на \"stub\"</a> (v bajtech):",
+       "stub-threshold": "Поріг про форматованя одказу як одказів на ''stub'' ($1):",
        "stub-threshold-disabled": "Выпнуте",
        "recentchangesdays": "За кілько днїв вказовати новы едітованя",
        "recentchangesdays-max": "(максімум $1 {{PLURAL:$1|день|днї|днїв}})",
        "prefs-tokenwatchlist": "Кліч",
        "prefs-diffs": "Порівнаня верзії",
        "prefs-help-prefershttps": "Тот параметер набуде чінности по вашім далшім входї до сістемы.",
-       "userrights": "СпÑ\80ава Ñ\85оÑ\81новаÑ\82елÑ\8cÑ\81кÑ\8bÑ\85 Ò\91Ñ\80Ñ\83п",
+       "userrights": "ХоÑ\81новаÑ\82елÑ\8cÑ\81кÑ\8b Ð¿Ñ\80ава",
        "userrights-lookup-user": "Управовати хосновательскы ґрупы",
        "userrights-user-editname": "Задайте мено хоснователя:",
        "editusergroup": "Едітовати хосновательскы ґрупы",
-       "editinguser": "Зміна прав хоснователя '''[[User:$1|$1]]''' $2",
-       "userrights-editusergroup": "Змінити ґрупы хоснователя",
-       "saveusergroups": "Уложыти хосновательскы ґрупы",
+       "editinguser": "Зміна прав {{GENDER:$1|хоснователя}} <strong>[[User:$1|$1]]</strong> $2",
+       "viewinguserrights": "Перегляд прав {{GENDER:$1|хоснователя}} <strong>[[User:$1|$1]]</strong> $2",
+       "userrights-editusergroup": "Змінити ґрупы {{GENDER:$1|хоснователя}}",
+       "userrights-viewusergroup": "Зобразити ґрупы {{GENDER:$1|хоснователя}}",
+       "saveusergroups": "Уложыти {{GENDER:$1|хосновательскы}} ґрупы",
        "userrights-groupsmember": "Член ґруп:",
        "userrights-groupsmember-auto": "Автоматічный  член ґруп:",
        "userrights-groups-help": "Можете мінити ґрупы, до котрых належыть хоснователь:\n* Кідь є позначене  поличко, хоснователь належыть до той ґрупы.\n* Не позначене  поличко значіть, же хоснователь у тій ґрупі не є.\n* Звіздочка (*) значіть, же не можете тоту ґрупу одобрати, буде ли придана, або наспак.",
        "userrights-nodatabase": "Датабаза $1 не екзістує або не є місцёва.",
        "userrights-changeable-col": "Ґрупы, котры можете змінити",
        "userrights-unchangeable-col": "Ґрупы, котры не можете змінити",
+       "userrights-expiry": "Кінчіть:",
+       "userrights-expiry-existing": "Сучасный час експірації: $3, $2",
        "userrights-expiry-othertime": "Іншый час:",
        "userrights-conflict": "Конфлікт змін прав хоснователїв! Просиме, перевірьте зміны і потвердьте їх.",
        "group": "Ґрупа:",
        "right-protect": "Зміна ровни замків і едітованя каскадово замкнутых сторінок",
        "right-editprotected": "Едітованя сторінок замкнутых на „{{int:protect-level-sysop}}“",
        "right-editsemiprotected": "Едітованя сторінок замкнутых на „{{int:protect-level-autoconfirmed}}“",
+       "right-editcontentmodel": "Едітовати модел обсягу сторінкы",
        "right-editinterface": "Едітованя інтерфейсу хоснователя",
        "right-editusercss": "Едітованя CSS файлів іншых хоснователїв",
        "right-edituserjson": "Едітованя JSON файлів іншых хоснователїв",
        "right-sendemail": "Посыланя пошты іншым хоснователям",
        "right-managechangetags": "Вытворїня і (де)актівація [[Special:Tags|значок]]",
        "right-applychangetags": "Придаваня [[Special:Tags|значок]] до властных змін",
+       "grant-group-email": "Послати ел. пошту",
        "grant-blockusers": "Блоковати і одблоковати хоснователїв",
        "grant-createaccount": "Закладаня конт",
        "grant-editmywatchlist": "Едітовати ваш список слїдованых сторінок",
        "grant-editprotected": "Едітованя замкнутых сторінок",
        "grant-uploadfile": "Наладовати новы файлы",
+       "grant-viewdeleted": "Перегляд вымазаных файлів і сторінок",
        "grant-viewmywatchlist": "Перезерати ваш список слїдованых сторінок",
        "newuserlogpage": "Лоґ вытварянь хоснователїв",
        "newuserlogpagetext": "Тото є список ново реґістрованых хоснователїв.",
        "action-writeapi": "хосновати API про писаня",
        "action-delete": "Вымазати тоту сторінку",
        "action-deleterevision": "вымазаня ревізій",
+       "action-deletelogentry": "вымазаня записів лоґів",
        "action-deletedhistory": "зобразити історію змазаных ревізій сторінкы",
        "action-deletedtext": "зобразити змазаны тексты ревізії",
        "action-browsearchive": "глядати змазаны сторінкы",
        "action-viewmyprivateinfo": "перезерати вашы пріватны даны",
        "action-editmyprivateinfo": "едітовати вашы пріватны інформації",
        "action-editcontentmodel": "едітовати модел обсягу сторінкы",
+       "action-apihighlimits": "хоснованя высшых лімітів в API запытах",
+       "action-autoconfirmed": "не тыкать ся лімітів основаных на IP",
+       "action-bigdelete": "мазаня сторінок з довгов історіёв",
+       "action-blockemail": "блокованя хоснователя од посыланя е-пошты",
+       "action-bot": "быти поважованый за автоматічный процес",
+       "action-editprotected": "едітованя сторінок замкнутых на „{{int:protect-level-sysop}}“",
+       "action-editsemiprotected": "едітованя сторінок замкнутых на „{{int:protect-level-autoconfirmed}}“",
+       "action-editinterface": "едітованя інтерфейсу хоснователя",
+       "action-editusercss": "едітованя CSS файлів іншых хоснователїв",
+       "action-edituserjson": "едітованя JSON файлів іншых хоснователїв",
+       "action-edituserjs": "едітованя JavaScript файлів іншых хоснователїв",
+       "action-editmyusercss": "едітовати вашы властны хосновательскы CSS файлы",
+       "action-editmyuserjson": "едітовати вашы властны хосновательскы JSON файлы",
+       "action-editmyuserjs": "едітовати вашы властны хосновательскы JavaScript файлы",
+       "action-hideuser": "блокованя мена хоснователя і ёго схованя",
+       "action-ipblock-exempt": "обходжіня блокованя IP адрес, їх россягів і автоблокованя",
+       "action-unblockself": "одблоковати самого себе",
+       "action-noratelimit": "не має обмеджіня в швыдкости",
+       "action-reupload-own": "переписованя файлів ладованых од себе самого",
+       "action-nominornewtalk": "невыписованя новых повідомлїнь по малых управах діскузной сторінкы",
+       "action-markbotedits": "означованя ревертів як едітованя робота",
+       "action-patrolmarks": "зобразованя патролёваных сторінок в Послїднїх змінах",
+       "action-override-export-depth": "експорт сторінок включаючі звязаны сторінкы з глубков до 5",
+       "action-suppressredirect": "нестворіня напрямлїня про переменоваю сторінкы",
        "nchanges": "$1 {{PLURAL:$1|зміна|зміны|змін}}",
        "enhancedrc-since-last-visit": "$1 {{PLURAL:$1|од остатнёй навщівы}}",
        "enhancedrc-history": "історія",
index d657b61..ca66e64 100644 (file)
        "userinvalidconfigtitle": "'''Attinzioni:''' Nò isisthi nisciun aipettu gràficu \"$1\". Amminta chi li pàgini pa li .css e .js passunari àni lu primu caràtteri di lu tìturu minori, cumenti {{ns:user}}:Foo/vector.css e nò {{ns:user}}:Foo/Vector.css.",
        "updated": "(Aggiornaddu)",
        "note": "'''NOTA:'''",
-       "previewnote": "'''Attinzioni: chistha è soru un'antiprimma. Li mudifigghi a la pagina NÒ so ancora isthaddi saivvaddi!'''",
+       "previewnote": "'''Attinzioni: chistha è soru un'antiprimma. Li mudìfigghi a la pàgina NÒ so ancora isthaddi saivvaddi!'''",
+       "continue-editing": "Andà a la casella di modìfica",
        "previewconflict": "L'antiprimma curripundi a lu testhu prisenti i' la casella di mudìfigga superiori e musthra la pàgina cumenti apparirà si s'à gana di incalchà abà '''Sàivva la pàgina'''.",
        "session_fail_preview": "'''Semmu dipiazuddi, nò è isthaddu pussìbiri sàivva la mudìfiggà parchí sò andaddi pessi i dati reratibi a la sissioni. Si lu probrema continua, prubà a iscì e effettuà una noba intradda.'''",
        "session_fail_preview_html": "'''Semmu dipiazuddi, no è isthaddu pussìbiri elaburà la mudìfigga parchì sò andaddi pessi li dati reratibi a la sissioni.'''\n\n''Parchì in {{SITENAME}} è cunsintiddu l'usu di l'HTML chena limitazioni, l'antiprimma no è visuarizzadda, pa sigguriddai contru l'attacchi JavaScript.''\n\n'''Si lu probrema prisisthi, pói prubà à iscì e turrà a intrà.'''",
        "cascadeprotectedwarning": "'''Attinzioni:''' Chistha pàgina è isthadda broccadda in modu chi soru l'utenti cun pribiréggi di amministhradori possiano mudìfiggarla. Lu chi avvini parchí la pàgina è incrusa {{PLURAL:$1|i la pàgina indicadda ..., ch'è isthadda prutiggidda|i li pàgini indicaddi ..., chi so isthaddi prutiggiddi}} chirriendi la prutizioni \"ricussiba\":",
        "titleprotectedwarning": "'''ATTINZIONI: Chistha pàgina è isthadda broccadda in modu chi soru zerthuni utenti possiano crialla.'''",
        "templatesused": "{{PLURAL:$1|Mudellu utirizaddu|Mudelli utirizaddi}} in chistha pàgina:",
-       "templatesusedpreview": "Mudelli utirizaddi in chisth'antiprimma:",
+       "templatesusedpreview": "{{PLURAL:$1|Mudellu utirizaddu|Mudelli utirizaddi}} in kistha pàgina:",
        "templatesusedsection": "Mudelli utirizaddi in chistha sezzioni:",
        "template-protected": "(prutiggiddu)",
        "template-semiprotected": "(mezu-prutiggiddu)",
        "lineno": "Riga $1:",
        "compareselectedversions": "Cunfronta li versioni sciubaraddi",
        "editundo": "annulla",
+       "diff-empty": "(Nisciuna diffarènzia)",
        "searchresults": "Risulthaddi di la zercha",
        "searchresults-title": "Risulthaddi di la zercha di \"$1\"",
        "titlematches": "Curripundènzi i lu tìturu di li pagini",
        "rightslog": "Diritti di li utenti",
        "rightslogtext": "Chisthu è lu rigisthru di lu mudìfigghi a li diritti assignaddi a l'utenti.",
        "action-edit": "mudìfiggà chistha pàgina",
+       "action-createaccount": "crià kistha registhrazioni",
        "nchanges": "$1 {{PLURAL:$1|mudìfigga|mudìfigghi}}",
        "enhancedrc-history": "cronologia",
        "recentchanges": "Ulthimi mudìfigghi",
        "nolinkstoimage": "Nisciuna pàgina utirizeggia chistha immàgina.",
        "sharedupload": "Chisthu file prubeni da $1 e pó assé utirizaddu da althri prugetti.",
        "sharedupload-desc-here": "Chisthu file prubeni da $1 e pó assé utirizaddu da althri prugetti. La deschrizioni di la [$2 pàgina di deschrizioni] è indicadda in giossu.",
+       "filepage-nofile": "No esisthi nisciun ischedàriu cu' kisthu nommu.",
        "uploadnewversion-linktext": "Carrigga una nóba versioni di chistu file",
        "filerevert": "Turra che primma $1",
        "filerevert-legend": "Ricupara file",
        "allpagesprefix": "Musthra li pàgini ch'ischuminzani cun:",
        "allpagesbadtitle": "Lu tìturu indicaddu pa la pàgina nò è vàriddu o cunteni prefissi interlinga o interwiki. Pudia puru cuntinì unu o più caràtteri chi nò si pudiani usà i' li tìturi.",
        "allpages-bad-ns": "Lu namespace \"$1\" nò isisthi i' {{SITENAME}}.",
+       "allpages-hide-redirects": "Cuà rinvii",
        "categories": "Categuri",
        "categoriespagetext": "Erencu cumpretu di li categuri prisenti i'lu situ.\n[[Special:UnusedCategories|Unused categories]] are not shown here.\nAlso see [[Special:WantedCategories|wanted categories]].",
        "linksearch": "Cullegamenti estherni",
        "wlheader-enotif": "La nutìfica via postha erettrònica è attiba.",
        "wlheader-showupdated": "Li pàgini chi so isthaddi mudìfiggaddi daboi l'ulthima vìsita so evidenziaddi in '''grasseddu'''",
        "wlnote": "Inogghi {{PLURAL:$1|è erencadda la mudìfigga più rizzenti arriggadda|so erencaddi li '''$1''' mudìfigghi più rizzenti arriggaddi}} {{PLURAL:$2|i' la ulthima ora|i' li ulthimi '''$2''' ori}}.",
-       "wlshowlast": "Musthra li ulthimi $1 ori $2 dì",
+       "wlshowlast": "Musthra li ùlthimi $1 ori $2 dì",
        "watchlist-options": "Opzioni abbaidaddi ippiziari",
        "watching": "Aggiunta a l'abbaidaddi ippiziari...",
        "unwatching": "Eliminazioni da l'abbaidaddi ippiziari...",
        "tooltip-feed-rss": "Feed RSS pa chistha pàgina",
        "tooltip-feed-atom": "Feed Atom pa chistha pàgina",
        "tooltip-t-contributions": "Listha di li cuntributi di {{GENDER:$1|chistu utenti}}",
-       "tooltip-t-emailuser": "Invia un'imbasciadda di postha erettrònica a chisth'utenti",
+       "tooltip-t-emailuser": "Invià un'imbasciadda di postha erettrònica a {{GENDER:$1|kisth'utenti}}",
        "tooltip-t-upload": "Carrigga file mùrthimediari",
        "tooltip-t-specialpages": "Listha di tutti li pàgini ippiziari",
        "tooltip-t-print": "Versioni sthampabiri di chistha pàgina",
        "simpleantispam-label": "Cumprobu cuntraspam\nNo <strong>not</strong> ischribì nudda drentu!",
        "pageinfo-header-basic": "Iffuimmaziòni basirari",
        "pageinfo-header-edits": "Cronologia",
+       "pageinfo-display-title": "Tìturu indicaddu",
+       "pageinfo-length": "Tàglia di la pàgina (in bytes)",
+       "pageinfo-language": "Linga di cuntinuddu di ra pàgina",
+       "pageinfo-content-model": "Mudellu di cuntinuddu di ra pàgina",
        "pageinfo-robot-index": "Primmissu",
+       "pageinfo-redirects-name": "Innùmaru di cullegamenti a kistha pàgina",
+       "pageinfo-subpages-name": "Innùmaru di sottupàgini di chistha pàgina",
        "pageinfo-firstuser": "Criadori di pàgini",
        "pageinfo-firsttime": "Data di criazioni di la pàgina",
+       "pageinfo-lasttime": "Data di l'ùlthima modìfica",
        "pageinfo-edits": "Innùmaru totari di modìfichi",
+       "pageinfo-authors": "Innùmaru tutari d'autori dibessi",
+       "pageinfo-recent-authors": "Ùlthimu innùmaru d'autori dibessi",
+       "pageinfo-hidden-categories": "{{PLURAL:$1|categuria cuadda|categurì cuaddi}}",
        "pageinfo-toolboxlink": "Iffuimmaziòni pa la pàgina",
        "pageinfo-contentpage-yes": "Sì",
        "pageinfo-protect-cascading-yes": "Sì",
        "widthheightpage": "$1 × $2, $3 {{PLURAL:$3|pàgina|pàgini}}",
        "file-info": "Misuri: $1, tipu MIME: $2",
        "file-info-size": "$1 × $2 punti, misuri: $3, tipu MIME: $4",
+       "file-info-size-pages": "$1 × $2 punti, misuri di lu file: $3, tipu MIME: $4, $5 {{PLURAL:$5|pàgina|pàgini}}",
        "file-nohires": "Nò so dipunìbiri versioni a risoruzioni maggiori.",
        "svg-long-desc": "file in fuimmaddu SVG, misuri nominari $1 × $2 punti, misuri di lu file: $3",
        "show-big-image": "File d'orìgini",
        "imgmultipageprev": "← pàgina prizzidenti",
        "imgmultipagenext": "pàgina sighenti →",
        "imgmultigo": "Vai",
+       "imgmultigoto": "Vai a la pàgina $1",
        "ascending_abbrev": "crisc",
        "descending_abbrev": "miminan",
        "table_pager_next": "Pàgina sighenti",
        "version-other": "Althru",
        "version-software-version": "Versioni",
        "redirect-submit": "Vai",
+       "redirect-lookup": "Tzirchà:",
+       "redirect-user": "Pàgina utenti",
+       "redirect-page": "Pàgina",
        "redirect-revision": "Versioni di la pàgina",
+       "redirect-file": "Innòmu di l'ischedàriu",
        "fileduplicatesearch-filename": "Innòommu di lu file:",
        "fileduplicatesearch-submit": "Zercha",
        "specialpages": "Pagini ippiziari",
        "tag-filter": "[[Special:Tags|Tag]] filthru:",
        "tag-list-wrapper": "[[Special:Tags|{{PLURAL:$1|Tag|Tags}}]]: $2",
        "tags-active-yes": "Sì",
+       "tags-active-no": "Nò",
        "tags-edit": "mudifigga",
        "tags-hitcount": "$1 {{PLURAL:$1|mudìfigga|mudìfigghi}}",
        "htmlform-submit": "Invia",
        "htmlform-selectorother-other": "Althru",
        "htmlform-yes": "Sì",
        "logentry-delete-delete": "$1 {{GENDER:$2|à isthudaddu}} ra pàgina $3",
+       "revdelete-content-hid": "cuntinuddu cuaddu",
        "logentry-newusers-create": "La registhrazioni di l'utenti $1 è isthadda {{GENDER:$2|criadda}}",
        "logentry-upload-upload": "$1 {{GENDER:$2|à carriggaddu}} $3",
        "rightsnone": "(nisciunu)",
index 354bb70..467ffd6 100644 (file)
        "autoblockedtext": "Vaša IP adresa je automatski blokirana jer je korištena od strane drugog korisnika, a blokirao ju je $1.\nNaveden je slijedeći razlog:\n\n:<em>$2</em>\n\n* Početak blokade: $8\n* Kraj blokade: $6\n* Blokirani korisnik: $7\n\nMožete kontaktirati $1 ili nekog drugog iz grupe [[{{MediaWiki:Grouppage-sysop}}|administratora]] i zahtijevati da Vas deblokira.\n\nZapamtite da ne možete koristiti opciju \"{{int:emailuser}}\" ukoliko nije unesena validna e-mail adresa u [[Special:Preferences|Vašim postavkama]] te Vas ne spriječava ga je koristite.\n\nVaša trenutna IP adresa je $3, a ID blokade je $5.\nMolimo da navedete sve gore navedene detalje u zahtjevu za deblokadu.",
        "systemblockedtext": "MediaWiki je automatski blokirao Vaše korisničko ime ili IP-adresu.\nDat je sljedeći razlog:\n\n:<em>$2</em>\n\n* Početak bloka: $8\n* Istek bloka: $6\n* Blok je namijenjen za: $7\n\nVaša trenutna IP-adresa je $3.\nPrepišite sve gorenavedene pojedinosti ukoliko želite da vlasti pitaju za blok.",
        "blockednoreason": "razlog nije naveden",
-       "blockedtext-composite": "<strong>Vaše korisničko ime ili IP-adresa je blokirano.</strong>\n\nDat je sljedeći razlog:\n\n:<em>$2</em>.\n\n* Početak bloka: $8\n* Istek najdužeg bloka: $6\n\nVaša trenutna IP-adresa je $3.\nPrepišite sve gorenavedene pojedinosti ukoliko želite da vlasti pitaju za blok.",
+       "blockedtext-composite": "<strong>Vaše korisničko ime ili IP-adresa je blokirano.</strong>\n\nDat je sljedeći razlog:\n\n:<em>$2</em>.\n\n* Početak bloka: $8\n* Istek najdužeg bloka: $6\n\n* $5\n\nVaša trenutna IP-adresa je $3.\nPrepišite sve gorenavedene pojedinosti ukoliko želite da vlasti pitaju za blok.",
        "blockedtext-composite-reason": "Vaš račun i/ili IP adresa ima nekoliko blokova",
        "whitelistedittext": "Da bi ste uređivali stranice, morate se $1.",
        "confirmedittext": "Morate potvrditi Vašu e-mail adresu prije nego počnete mijenjati stranice.\nMolimo da postavite i verifikujete Vašu e-mail adresu putem Vaših [[Special:Preferences|korisničkih opcija]].",
index 576512e..42e2934 100644 (file)
        "autoblockedtext": "Vaš IP-naslov je bil samodejno blokiran, saj je bil uporabljen s strani drugega uporabnika, ki ga je blokiral $1.\nRazlog za to je bil naslednji:\n\n:<em>$2</em>\n\n* Začetek blokade: $8\n* Konec blokade: $6\n* Blokirani uporabnik: $7\n\nKontaktirate lahko $1 ali katerega od drugih [[{{MediaWiki:Grouppage-sysop}}|administratorjev]], da razpravljate o blokadi.\n\nVedite, da lahko funkcijo »{{int:emailuser}}« uporabljate le, če ste v svoje [[Special:Preferences|uporabniške nastavitve]] vnesli veljaven e-poštni naslov, in vam njena uporaba ni bila preprečena.\n\nVaš trenutni IP-naslov je $3, ID blokiranja pa #$5. Prosimo, vključite ta ID v vsako zastavljeno vprašanje.",
        "systemblockedtext": "Vaše uporabniško ime ali IP-naslov je MediaWiki samodejno blokiral.\nPodani razlog je:\n\n:<em>$2</em>\n\n* Začetek blokade: $8\n* Potek blokade: $6\n* Blokirani uporabnik: $7\n\nVaš trenutni IP-naslov je $3.\nProsimo, da v svoje poizvedbe vključite vse zgornje podatke.",
        "blockednoreason": "razlog ni podan",
-       "blockedtext-composite": "<strong>Vaše uporabniško ime ali IP-naslov je bil blokiran.</strong>\n\nPodani razlog je:\n\n:<em>$2</em>\n\n* Začetek blokade: $8\n* Potek najdaljše blokade: $6\n\nVaš trenutni IP-naslov je $3.\nProsimo, da v svoje poizvedbe vključite vse zgornje podatke.",
+       "blockedtext-composite": "<strong>Vaše uporabniško ime ali IP-naslov je bil blokiran.</strong>\n\nPodani razlog je:\n\n:<em>$2</em>\n\n* Začetek blokade: $8\n* Potek najdaljše blokade: $6\n\n* $5\n\nVaš trenutni IP-naslov je $3.\nProsimo, da v svoje poizvedbe vključite vse zgornje podatke.",
        "blockedtext-composite-reason": "Za vaš račun in/ali IP-naslov je nastavljenih več blokad.",
        "whitelistedittext": "Za urejanje strani se morate $1.",
        "confirmedittext": "Pred urejanjem strani morate potrditi svoj e-poštni naslov.\nProsimo, da ga z uporabo [[Special:Preferences|uporabniških nastavitev]] vpišete in potrdite.",
index fc0d936..321962f 100644 (file)
                ]
        },
        "tog-underline": "Nënvizimi i lidhjes:",
-       "tog-hideminor": "Fshih redaktimet e vogla nga ndryshimet e fundit",
-       "tog-hidepatrolled": "Fshih redaktimet e kontrolluara nga ndryshimet e fundit",
+       "tog-hideminor": "Fshih redaktimet e vogla nga ndryshimet më të fundit fundit",
+       "tog-hidepatrolled": "Fshih redaktimet e kontrolluara nga ndryshimet më të fundit",
        "tog-newpageshidepatrolled": "Fshih faqet e kontrolluara nga lista e faqeve të reja",
        "tog-hidecategorization": "Fshih kategorizimin e faqeve",
        "tog-extendwatchlist": "Zgjero listën e të gjitha faqeve të vëzhguara në mënyrë që t'i tregojë të gjitha ndryshimet, jo vetëm më të fundit.",
        "tog-usenewrc": "Grupo ndryshimet sipas faqes në ndryshimet më të fundit dhe listën e vëzhgimit (kërkon JavaScript)",
        "tog-numberheadings": "Numërim automatik i titujve",
-       "tog-editondblclick": "Redakto faqe në klikim të dyfishtë",
-       "tog-editsectiononrightclick": "Aktivizo redaktimin e seksioneve duke klikuar me të djathtën mbi titullin e seksionit",
-       "tog-watchcreations": "Shto faqet e krijuara dhe skedarët e ngarkuar nga unë në listën time të mbikqyrjes",
-       "tog-watchdefault": "Shto faqet dhe skedarët e redaktuar prej meje në listën time të mbikqyrjes",
-       "tog-watchmoves": "Shto faqet dhe skedarët e zhvendosur nga unë, në listën time të mbikqyrjes",
-       "tog-watchdeletion": "Shto faqet dhe skedarët e fshirë nga unë në listën time të mbikqyrjes",
-       "tog-watchuploads": "Shto skedarët e rinj të ngarkuar nga unë në listën time të mbikëqyrjes",
-       "tog-watchrollback": "Shto faqet ku unë kam kryer një rikthim tek lista ime mbikqyrëse",
+       "tog-editondblclick": "Redakto faqet me një shtypje të dyfishtë",
+       "tog-editsectiononrightclick": "Aktivizo redaktimin e pjesëve duke shtypur me të djathtën mbi titullin e pjesës",
+       "tog-watchcreations": "Shto faqet e krijuara nga unë dhe materialet multimedialë të ngarkuar nga unë në listën time të mbikqyrjes",
+       "tog-watchdefault": "Shto faqet dhe materialet multimedialë që redaktoj në listën time të mbikqyrjes",
+       "tog-watchmoves": "Shto faqet dhe materialet multimedialë që zhvendos në listën time të mbikqyrjes",
+       "tog-watchdeletion": "Shto faqet dhe materialet multimedialë që fshij në listën time të mbikqyrjes",
+       "tog-watchuploads": "Shto materialet multimedialë të rinj që ngarkoj në listën time të mbikëqyrjes",
+       "tog-watchrollback": "Shto faqet ku unë kam kryer një rikthim te lista ime mbikqyrëse",
        "tog-minordefault": "Shëno të gjitha redaktimet si të vogla automatikisht",
        "tog-previewontop": "Trego parapamjen para kutisë së redaktimit",
        "tog-previewonfirst": "Trego parapamjen në redaktimin e parë",
index db88999..78c695a 100644 (file)
        "filepageexists": "Страница с описом ове датотеке је већ направљена овде <strong>[[:$1]]</strong>, иако датотека не постоји.\nОпис који сте навели се неће појавити на страници с описом.\nДа би се ваш опис овде нашао, потребно је да га ручно измените.\n[[$1|thumb]]",
        "fileexists-extension": "Датотека са сличним називом већ постоји: [[$2|thumb]]\n* Назив датотеке коју шаљете: <strong>[[:$1]]</strong>\n* Назив постојеће датотеке: <strong>[[:$2]]</strong>\nДа ли желите да користите препознатљивије име?",
        "fileexists-thumbnail-yes": "Изгледа да је датотека слика умањене величине <em>(сличица)</em>.\n[[$1|thumb]]\nПроверите датотеку <strong>[[:$1]]</strong>.\nАко је проверена датотека иста слика првобитне величине, није потребно отпремати додатну.",
-       "file-thumbnail-no": "Име датотеке почиње са <strong>$1</strong>.\nИзгледа да се ради о слици умањене величине <em>(сличица)</em>.\nАко имате ову слику у пуној резолуцији, отпремите је, у противном, промените име датотеке.",
+       "file-thumbnail-no": "Име датотеке почиње са <strong>$1</strong>.\nИзгледа да се ради о слици умањене величине <em>(минијатура)</em>.\nАко имате ову слику у пуној резолуцији, отпремите је; у противном, промените име датотеке.",
        "fileexists-forbidden": "Датотека с овим називом већ постоји и не може се заменити.\nАко и даље желите да пошаљете датотеку, вратите се и изаберите други назив.\n[[File:$1|thumb|center|$1]]",
        "fileexists-shared-forbidden": "Датотека са овим именом већ постоји у заједничкој остави.\nАко још увек желите да отпремите датотеку, вратите се и користите ново име.\n[[File:$1|thumb|center|$1]]",
        "fileexists-no-change": "Датотека је дупликат тренутне верзије <strong>[[:$1]]</strong>.",
        "listfiles-userdoesnotexist": "Кориснички налог „$1“ није отворен.",
        "imgfile": "датотека",
        "listfiles": "Списак датотека",
-       "listfiles_thumb": "СлиÑ\87иÑ\86а",
+       "listfiles_thumb": "Ð\9cиниÑ\98аÑ\82Ñ\83Ñ\80а",
        "listfiles_date": "Датум",
        "listfiles_name": "Назив",
        "listfiles_user": "Корисник",
        "filehist-revert": "врати",
        "filehist-current": "тренутна",
        "filehist-datetime": "Датум/време",
-       "filehist-thumb": "СлиÑ\87иÑ\86а",
+       "filehist-thumb": "Ð\9cиниÑ\98аÑ\82Ñ\83Ñ\80а",
        "filehist-thumbtext": "Минијатура за верзију на дан $1",
        "filehist-nothumb": "Без сличице",
        "filehist-user": "Корисник",
index e4d84de..04e0b06 100644 (file)
        "sunday": "Йәкшәмпе",
        "monday": "Түшәмпе",
        "tuesday": "Сишәмпе",
-       "wednesday": "Царшампы",
+       "wednesday": "Цәршәмпе",
        "thursday": "Пәйшәмпе",
        "friday": "Йома",
        "saturday": "Шәмпе",
        "sun": "Йәк",
        "mon": "Түш",
        "tue": "Сиш",
-       "wed": "Цар",
+       "wed": "Цәр",
        "thu": "Пәй",
        "fri": "Йом",
        "sat": "Шәм",
        "viewsource": "Вики-текстны ҡарау",
        "viewsource-title": "$1 питнең паш текстын ҡарау",
        "viewsourcetext": "Сес пы питнеңке паш текстын ҡарап күцерә аласыс.",
-       "userlogin-yourname": "Ҡулланыуцыныңҡы исеме",
+       "userlogin-yourname": "Ошланыуцыныңҡы исеме",
        "userlogin-yourname-ph": "Исәп йасмағысныңҡы исемен кергесең",
        "userlogin-yourpassword": "Пароль",
        "userlogin-yourpassword-ph": "Үсегеснең парольны йасың",
        "image_tip": "Кергеселгән файл",
        "media_tip": "Файлға ссылка",
        "sig_tip": "Ҡул ҡуйыуығыс пелән уаҡыт",
-       "hr_tip": "Горизонталь цыйыҡ (кел ҡулланмаң)",
+       "hr_tip": "Горизонталь цыйыҡ (әлтән ошланмаң)",
        "summary": "Пашҡартыуларны аңнатыу:",
        "minoredit": "Кецкенә пашҡартыу",
        "watchthis": "Пы питне көсәткәле",
        "preview": "Алттан ҡарау",
        "showpreview": "Алттан ҡарап цығыу",
        "showdiff": "Кергеселгән пашҡартыулар",
-       "anoneditwarning": "<strong>Саҡлыҡ!</strong> Сес сайтта теркәлмәтегес. Әгәр тә сес төрлө төсәтеүләр ҡылсағыс, сеснеңке IP-адресығыс пашҡаларға күренеп торор. Әгәр тә Сес <strong>[$1 керсәгез]</strong> йә <strong>[$2 ҡулланыуцы йасманы пултырсағыс]</strong>, сеснең төсәтеүләр ҡулланыуцы йасмағысҡа пәйле пулыр, шалай уҡ пашҡа өстөннөкләр тыуыр.",
+       "anoneditwarning": "<strong>Саҡлыҡ!</strong> Сес сайтта теркәлмәтегес. Әгәр тә сес төрлө төсәтеүләр ҡылсағыс, сеснеңке IP-адресығыс пашҡаларға күренеп торор. Әгәр тә Сес <strong>[$1 керсәгез]</strong> йә <strong>[$2 ошланыуцы йасманы пултырсағыс]</strong>, сеснең төсәтеүләр ошланыуцы йасмағысҡа пәйле пулыр, шалай уҡ пашҡа өстөннөкләр тыуыр.",
        "blockedtext": "<strong>Сеснең исәп йасмағыс йә IP адресығыс тыйылған.</strong>\n* Тыйған администратор: $1.\nКүргәселгән сәбәп: <em>$2.</em>\n\n* Тыйыу пашланҡан уаҡыт: $8\n* Тыйыуның пөтөү уаҡыты: $6\n* Тыйыуның кәрәге: $7\n\nСес $1 йә пүтән [[{{MediaWiki:Grouppage-sysop}}|администраторға]] тыйыу турлы сурауларығысны йебәрә аласыз.\nОнотмаң: әгәр сес  үсегеснеңке ҡоролошоғоста электрон почта адресығысны пирмәгән пулсағыс ([[Special:Preferences|йә аны төрөс күргәсмәгән пулсағыс]]), администраторға ҡат йебәрә алмайсыс. Шалай уҡ тыйыу уаҡытны сес ҡат йебәрә алмаған пуласыс.\nСеснең IP адрес — $3, тыйыу идентификатор — #$5.\nҠатларта пы ҡәбәр-пелемнәрне күргәскәле онотмаң.",
        "loginreqlink": "кергәле",
        "newarticletext": "Сес ссылка пелән әле йасылмаған питкә күцтегес. Йаңа пит ҡылыр өцөн астытағы тәрәсәгә текст йасың (тулыраҡ өцөн [$1 пелешмәлек питне] ҡараң). Әгәр мынта йалғыш кереп киткән пулсағыс, браузерығысныңҡы  \"сыртҡа\" кнопкаға пасың.",
-       "anontalkpagetext": "----\n<em>Пы уйлап ҡарау пит исәп йасыуны ҡылтырмаған йә аны ҡулланмайтығын аноним ҡатнашыуцыныңҡы пите.</em> Ҡулланыуцыны таныу өцөн аныңҡы IP-адресы ҡулланылаты.\nӘгәр сес аноним ҡулланыуцы пулсағыс, сескә йебәрелмәгән ҡатлар алтым тисәгес (пер IP-адрес кән ҡулланыуцыларта пулғалы мөмкин), пашҡа мынтайын аңнашылмауцылыҡлар килеп цыҡмасын өцөн, [[Special:UserLogin|системаға керең]] йә [[Special:CreateAccount|теркәлең]].",
+       "anontalkpagetext": "----\n<em>Пы уйлап ҡарау пит исәп йасыуны ҡылтырмаған йә аны ошланмайтығын аноним ҡатнашыуцыныңҡы пите.</em> Ошланыуцыны таныу өцөн аныңҡы IP-адресы ошланылаты.\nӘгәр сес аноним ошланыуцы пулсағыс, сескә йебәрелмәгән ҡатлар алтым тисәгес (пер IP-адрес кән ошланыуцыларта пулғалы мөмкин), пашҡа мынтайын аңнашылмауцылыҡлар килеп цыҡмасын өцөн, [[Special:UserLogin|системаға керең]] йә [[Special:CreateAccount|теркәлең]].",
        "noarticletext": "Ҡәсерге уаҡытта пы питтә текст йуҡ. Сес [[Special:Search/{{PAGENAME}}|пы исем кергән пашҡа мәҡәләләрне]], <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} туры килгән йасмаларны] таба йә '''[{{fullurl:{{FULLPAGENAME}}|action=edit}} шантайын уҡ исемле йаңа питне ҡыла]'''</span> аласыс.",
        "noarticletext-nopermission": "Ҡәсерге уаҡытта пы питтә текст йуҡ. Сес пашҡа питләртә [[Special:Search/{{PAGENAME}}|пы исемне]] йә <span class=\"plainlinks\">[{{fullurl:{{#Special:Log}}|page={{FULLPAGENAMEE}}}} журналтағы йасмаларны] эстәй аласыс. Сескә пит йасағалы рөҡсәт пирелмәгән.</span>",
        "userpage-userdoesnotexist-view": "«$1» исемле исәп йасма йуҡ.",
        "editing": "«$1» питне төсәткәле",
        "creating": "«$1» питне пултырыу",
        "editingsection": "«$1» питтә төсәтеү",
-       "templatesused": "Пы питтә ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:",
-       "templatesusedpreview": "Алттан ҡарау режимта ҡулланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:",
+       "templatesused": "Пы питтә ошланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:",
+       "templatesusedpreview": "Алттан ҡарау режимта ошланылған {{PLURAL:$1|1=ҡалып|ҡалыплар}}:",
        "template-protected": "(саҡланҡан)",
        "template-semiprotected": "(өлөшө пелән саҡланҡан)",
        "hiddencategories": "Пы пит $1 {{PLURAL:$1|йәшерен төргөнкә|$1 йәшерен төргөннәргә}} керәте:",
        "compareselectedversions": "Сайланҡан аңышларны туры килтергәле",
        "editundo": "пулмайын иткәле",
        "diff-empty": "(айырмалар йуҡ)",
-       "diff-multi-sameuser": "(пы ҡулланыуцының {{PLURAL:$1|аралыҡ версия $1|аралыҡ версиялары $1}} күргәселмәгән)",
+       "diff-multi-sameuser": "(пы ошланыуцының {{PLURAL:$1|аралыҡ версия $1|аралыҡ версиялары $1}} күргәселмәгән)",
        "diff-multi-otherusers": "({{PLURAL:$2|ҡатнашыуцының|$2 ҡатнашыуцыларның}} {{PLURAL:$1|ара аңышы|$1 арадаш аңышлары}} күргәселмәгән)",
        "searchresults": "Эстәүнең йомҡаҡлары",
        "searchresults-title": "«$1»ны эстәгәле",
        "group-sysop": "Администраторлар",
        "grouppage-bot": "{{ns:project}}:Ботлар",
        "grouppage-sysop": "{{ns:project}}:Администраторлар",
-       "right-writeapi": "Йасыр өцөн API-ны ҡулланыу",
-       "newuserlogpage": "Йаңа ҡулланыуцыларны теркәү журналы",
+       "right-writeapi": "Йасыр өцөн API-ны ошланыу",
+       "newuserlogpage": "Йаңа ошланыуцыларны теркәү журналы",
        "rightslog": "Ҡатнашыуцының ҡаҡлары журналы",
        "action-edit": "пы питне төсәткәле",
        "action-createaccount": "Пы исәп йасманы ҡылыу",
        "rcshowhidebots": "ботларны $1",
        "rcshowhidebots-show": "Күргәскәле",
        "rcshowhidebots-hide": "Йәшергәле",
-       "rcshowhideliu": "$1 танылған ҡулланыуцылар",
+       "rcshowhideliu": "$1 танылған ошланыуцылар",
        "rcshowhideliu-show": "Күргәскәле",
        "rcshowhideliu-hide": "Йәшергәле",
-       "rcshowhideanons": "танылмаған ҡулланыуцылар $1",
+       "rcshowhideanons": "танылмаған ошланыуцылар $1",
        "rcshowhideanons-show": "Күргәскәле",
        "rcshowhideanons-hide": "Йәшергәле",
        "rcshowhidepatr": "$1 ҡараған төсәтеүләр",
        "filehist-user": "Ҡатнашыуцы",
        "filehist-dimensions": "Сурлығы",
        "filehist-comment": "Искәрмә",
-       "imagelinks": "Файлны ҡулланыу",
+       "imagelinks": "Файлны ошланыу",
        "linkstoimage": "Пы файлға {{PLURAL:$1|1=пит|$1 пит}} йебәрәте:",
        "linkstoimage-more": "Пы файлға $1-на пашҡа та  {{PLURAL:$1|пит}} ссылка ҡылатылар.\nТүмәнтәге күцермәлектә пы файлға $1 {{PLURAL:$1|ссылка}} ғына күргәселгән.\nШалай уҡ [[Special:WhatLinksHere/$2|тулы күцермәлекне]] ҡарап пулаты.",
        "nolinkstoimage": "Пы файлға йебәргән питләр йуҡ.",
        "linkstoimage-redirect": "$1 (файл йусыҡлау) $2",
-       "sharedupload-desc-here": "Пы файл $1-нан, ул пашҡа проектларта ҡулланыла алаты. Файл турлы [$2 сүрәтләү пите] тулыраҡ пелешмә түмәнтәрәк пирелгән.",
+       "sharedupload-desc-here": "Пы файл $1-нан, ул пашҡа проектларта ошланыла алаты. Файл туры [$2 сүрәтләү пите] тулыраҡ пелешмә түмәнтәрәк пирелгән.",
        "filepage-nofile": "Мынтайын исемле файл йуҡ.",
        "upload-disallowed-here": "Сес пы файлны йаңатан йастыра алмайсыс.",
        "randompage": "Уйланмаған мәҡәлә",
        "protectlogpage": "Саҡлау журнал",
        "protectedarticle": "[[$1]] питен йаҡлаған",
        "modifiedarticleprotection": "[[$1]]-ныңҡы питенең йаҡлау ҡаты пашҡартылты",
-       "protect-default": "Пөтөн ҡулланыуцыларға ацыҡ",
+       "protect-default": "Пөтөн ошланыуцыларға ацыҡ",
        "restriction-edit": "Төсәткәле",
        "restriction-move": "Йаңа исем ҡушыу",
        "namespace": "Исемнәрнең киңнеге:",
        "tooltip-t-whatlinkshere": "Пы питкә тапшырған пөтөн питләрнең күцермәлеге",
        "tooltip-t-recentchangeslinked": "Пы питтән тапшырылған питләрнең суңҡы пашҡартыулары",
        "tooltip-feed-atom": "Пы пит өцөн Atom-ҡа трансляция",
-       "tooltip-t-contributions": "{{GENDER:$1|Пы ҡулланыуцы ҡылған}} пашҡартыуларның күцермәлеге",
-       "tooltip-t-emailuser": "{{GENDER:$1|пы ҡулланыуцыға}} ҡат йебәргәле",
+       "tooltip-t-contributions": "{{GENDER:$1|Пы ошланыуцы ҡылған}} пашҡартыуларның күцермәлеге",
+       "tooltip-t-emailuser": "{{GENDER:$1|пы ошланыуцыға}} ҡат йебәргәле",
        "tooltip-t-upload": "Файлларны төйәгәле",
        "tooltip-t-specialpages": "Ҡесмәт питләрнең күцермәлеге",
        "tooltip-t-print": "Пы питнең пасма аңышы",
        "tooltip-t-permalink": "Пы питнең версиясына тапшырған келәң ссылка",
        "tooltip-ca-nstab-main": "Мәҡәләнеңке эцтәлеге",
-       "tooltip-ca-nstab-user": "Ҡулланыуцыныңҡы үсенең пите",
+       "tooltip-ca-nstab-user": "Ошланыуцыныңҡы үсенең пите",
        "tooltip-ca-nstab-special": "Пы ҡесмәт пит, аны төсәткәле пулмайты",
        "tooltip-ca-nstab-project": "Проектның пите",
        "tooltip-ca-nstab-image": "Файлның пите",
        "tooltip-ca-nstab-category": "Категорияның пите",
        "tooltip-minoredit": "Пы пашҡартыуны пайтаҡ түгел итеп пилгеләгәле",
        "tooltip-save": "Пашҡартыуларығысны ҡалтырғалы",
-       "tooltip-preview": "Питне алттан ҡарап цығыу; төсәтеүләрегесне ҡалтырыр алттан ҡулланың!",
+       "tooltip-preview": "Питне алттан ҡарап цығыу; төсәтеүләрегесне ҡалтырыр алттан ошланың!",
        "tooltip-diff": "Текстта эшләнкән үсегеснең пашҡартыуларығысны генә күргәскәле",
        "tooltip-compareselectedversions": "Пы питнең ике сайланҡан аңышылары арасынтағы айырманы ҡарағалы.",
        "tooltip-watch": "Пы питне көсәтеү күцермәлегемә өстәгәле",
index 5dd7c1b..e165fe0 100644 (file)
        "autoblockedtext": "Din IP-adress har blockerats automatiskt eftersom den har använts av en annan användare som blockerats av $1.\nMotiveringen av blockeringen var:\n\n:''$2''\n\n* Blockeringen startade: $8\n* Blockeringen gäller till: $6\n* Blockeringen är avsedd för: $7\n\nDu kan kontakta $1 eller någon annan [[{{MediaWiki:Grouppage-sysop}}|administratör]] för att diskutera blockeringen.\n\nObservera att du inte kan använda dig av funktionen \"{{int:emailuser}}\" om du inte har registrerat en giltig e-postadress i [[Special:Preferences|dina inställningar]] eller om du har blivit blockerad från att skicka e-post.\n\nDin nuvarande IP-adress är $3, och blockerings-ID är #$5.\nVänligen ange informationen ovan i alla förfrågningar som du gör i ärendet.",
        "systemblockedtext": "Ditt användarnamn eller IP-adress h    ar blockerats automatiskt av MediaWiki.\n\nMotiveringen av blockeringen var:\n\n:<em>$2</em>\n\n* Blockeringen startade: $8\n* Blockeringen gäller till: $6\n* Blockeringen är avsedd för: $7\n\nDin nuvarande IP-adress är $3.\nVänligen ange informationen ovan i alla förfrågningar som du gör i ärendet.",
        "blockednoreason": "ingen motivering angavs",
-       "blockedtext-composite": "<strong>Ditt användarnamn eller din IP-adress har blockerats.</strong>\n\nMotiveringen till detta är:\n\n<em>$2</em>.\n\n* Blockeringen startade: $8\n* Den längsta blockeringen gäller till: $6\n\nDin nuvarande IP-adress är $3.\n\nVänligen ange all informationen ovan i förfrågningar som du gör i ärendet.",
+       "blockedtext-composite": "<strong>Ditt användarnamn eller din IP-adress har blockerats.</strong>\n\nMotiveringen till detta är:\n\n<em>$2</em>.\n\n* Blockeringen startade: $8\n* Den längsta blockeringen gäller till: $6\n\n* $5\n\nDin nuvarande IP-adress är $3.\n\nVänligen ange all informationen ovan i förfrågningar som du gör i ärendet.",
        "blockedtext-composite-reason": "Det föreligger flera blockeringar mot ditt konto eller din IP-adress.",
        "whitelistedittext": "Vänligen $1 för att redigera sidor.",
        "confirmedittext": "Du måste bekräfta din e-postadress innan du kan redigera sidor. Var vänlig ställ in och validera din e-postadress genom dina [[Special:Preferences|användarinställningar]].",
index 7d9374b..004e9bc 100644 (file)
        "blockedtext": "'''Номи корбарии Шумо ё нишонаи IP баста шудааст.'''\n\nБастан аз тарафи $1 иҷро шуд.\nСабаби он ''$2'' аст.\n\n* Замони қатъ кардан: $8\n* Замони саромадани қатъи дастрасӣ: $6\n* Қатъкунанда: $7\n\nШумо метавонед бо $1 ё яке дигаре аз [[{{MediaWiki:Grouppage-sysop}}|мудирон]] барои гуфтугӯ роҷеъ ба қатъи дастрасӣ тамос гиред.\nТаваҷҷӯҳ кунед, ки аз қобилияти 'фиристодаи почтаи электронӣ ба ин корбар' наметавоне истифода кард, магар ин ки як нишони мӯътабари почтаи электронӣ дар [[Special:Preferences|тарҷиҳоти корбарии]] худ сабт карда бошед ва аз корбурди он манъ нашуда бошед.\nНишонаи кунунии IP Шумо $3 аст, ва шиносаи қатъи дастрасии Шумо #$5 аст.\nЛутфан ин ё онро ва ё ҳардуи онро дар дархостҳои худ зикр кунед.",
        "autoblockedtext": "Нишонаи IP шумо қатъ шудааст, сабаб он аст, ки ин нишонаи IP аз тарафи корбаре истифода мешуд, ки дастрасии ӯ аз тарафи $1 қатъ гардонида шудааст.\nИллати зикршуда чунин аст:\n\n:<em>$2</em>\n\n* Шурӯъи қатъи дастрасӣ: $8\n* Поёни қатъи дастрасӣ: $6\n* Шахси басташуда: $7\n\nШумо метавонед бо $1 ё яке аз дигаре [[{{MediaWiki:Grouppage-sysop}}|мудирон]] тамос бигиред, то дар мавриди ин қатъи дастрасӣ сӯҳбат кунед.\n\nТаваҷҷӯҳ кунед, шумо аз \"фиристодани email ба ин корбар\" истифода бурда наметавонед, агар нишонаи почтаи электронӣ худро дар саҳифаи [[Special:Preferences|тарҷиҳоти корбарӣ]] фаъол карда набошед ва низ, имкони истифода аз ин вижагӣ барои шумо қатъ набошад.\n\nНишонаи IP кунунии шумо $3, ва нишонаи басташуда #$5 аст.\nЛутфан ин иттилоотро дар ҳар дархосте, ки мекунед, қайд намоед.",
        "blockednoreason": "далеле мушаххас нашудааст",
+       "blockedtext-composite": "<strong>Саҳифаи корбарӣ ё суроғаи IP-и шумо баста шуддааст.</strong>\n\nСабаб дар он аст:\n\n:<em>$2</em>.\n\n* Аввали бастан: $8\n* Охирин муддати дарозтарини бастан: $6\n\n* $5\n\nСурағаи IP-и кунунии шумо $3 мебошад.\nЛутфан деталҳои дар боло зикршударо дар саволҳои худ дохил намоед.",
+       "blockedtext-composite-no-ids": "Суроғаи IP-и шумо дар чандин рӯйхати сиёҳ мавҷуд аст.",
        "whitelistedittext": "Барои вироиши мақола бояд ба систем $1 шавед.",
        "confirmedittext": "Шумо бояд нишонаи почтаи электрониатонро пеш аз вироиш кардани саҳифаҳо, тасдиқ кунед. Лутфан ин корро тариқи [[Special:Preferences|тарҷиҳоти корбар]] сурат диҳед.",
        "nosuchsectiontitle": "Бахш пайдо нашуд",
index 6db295c..875bd82 100644 (file)
        "autoblockedtext": "เลขที่อยู่ไอพีของคุณถูกบล็อกอัตโนมัติ เพราะเคยมีผู้ใช้อื่นใช้ ซึ่งถูกบล็อกโดย $1\nโดยให้เหตุผลว่า\n\n:<em>$2</em>\n\n* เริ่มการบล็อก: $8\n* สิ้นสุดการบล็อก: $6\n* ผู้ถูกบล็อกที่เจตนา: $7\n\nคุณสามารถติดต่อ $1 หรือ[[{{MediaWiki:Grouppage-sysop}}|ผู้ดูแลระบบ]]คนอื่นเพื่ออภิปรายการบล็อกนี้ \nคุณไม่สามารถใช้คุณลักษณะ \"{{int:emailuser}}\" จนกว่าจะระบุที่อยู่อีเมลที่ถูกต้องใน[[Special:Preferences|การตั้งค่าบัญชี]]ของคุณ และคุณมิได้ถูกห้ามใช้\nเลขที่อยู่ไอพีปัจจุบันของคุณคือ $3 และหมายเลขการบล็อกคือ #$5 \nโปรดรวมรายละเอียดข้างต้นทั้งหมดในการสอบถามใด ๆ",
        "systemblockedtext": "ชื่อผู้ใช้หรือที่อยู่ไอพีของคุณถูกบล็อกอัตโนมัติโดยมีเดียวิกิ\nเหตุผลสำหรับการบล็อกคือ:\n\n:<em>$2</em>\n\n* เริ่มการบล็อก: $8\n* สิ้นสุดการบล็อก: $6\n* ผู้ดำเนินการบล็อก: $7\n\nไอพีแอดเดรสปัจจุบันของคุณคือ $3\nโปรดแจ้งรายละเอียดทั้งหมดข้างต้น ถ้าคุณมีข้อสงสัยใด ๆ",
        "blockednoreason": "ไม่ได้ให้เหตุผล",
-       "blockedtext-composite": "<strong>",
        "whitelistedittext": "คุณต้อง$1เพื่อแก้ไขหน้า",
        "confirmedittext": "คุณต้องยืนยันที่อยู่อีเมลของคุณก่อนแก้ไขหน้า \nโปรดตั้งและตรวจสอบความสมเหตุสมผลของที่อยู่อีเมลของคุผ่าน[[Special:Preferences|การตั้งค่าผู้ใช้]]",
        "nosuchsectiontitle": "ไม่พบส่วน",
index 2eb7bdb..c9db9bc 100644 (file)
        "autoblockedtext": "IP adresiniz otomatik olarak engellendi, çünkü $1 tarafından engellenmiş başka bir kullanıcı tarafından kullanılmaktaydı.\nBelirtilen sebep şudur:\n\n:<em>$2</em>\n\n* Engellemenin başlangıcı: $8\n* Engellemenin bitişi: $6\n* Bloke edilmesi istenen: $7\n\nEngelleme hakkında tartışmak için $1 ile veya diğer [[{{MediaWiki:Grouppage-sysop}}|hizmetlilerden]] biriyle irtibata geçebilirsiniz.\n\nNot, [[Special:Preferences|kullanıcı tercihlerinize]] geçerli bir e-posta adresi kaydetmediyseniz  \"{{int:emailuser}}\" özelliğinden faydalanamayabilirsiniz ve bu özelliği kullanmaktan engellenmediniz.\n\nŞu anki IP numaranız $3 ve engellenme ID'niz #$5.\nLütfen yapacağınız herhangi bir sorguda yukarıdaki bütün detayları bulundurun.",
        "systemblockedtext": "Kullanıcı adınız veya IP adresiniz MediaWiki tarafından otomatik olarak engellendi.\nSebebi:\n\n:<em>$2</em>\n\n* Engelin başlangıcı: $8\n* Engelin süresi: $6\n* Engellenmesi istenen: $7\n\nMevcut IP adresiniz $3.\nLütfen yukarıdaki tüm ayrıntıları, yaptığınız sorgularda belirtin.",
        "blockednoreason": "sebep verilmedi",
-       "blockedtext-composite": "<strong>Kullanıcı adınız veya IP adresiniz engellendi.</strong>\n\nSebebi:\n\n:<em>$2</em>.\n\n* Engel başlama tarihi: $8\n* Engelin süresi: $6\n\nGeçerli IP adresiniz $3.\nLütfen yukarıdaki tüm detayları yaptığınız tüm sorgulara dahil ediniz.",
+       "blockedtext-composite": "<strong>Kullanıcı adınız veya IP adresiniz engellendi.</strong>\n\nSebebi:\n\n:<em>$2</em>.\n\n* Engel başlama tarihi: $8\n* Engelin süresi: $6\n\n* $5\n\nGeçerli IP adresiniz $3.\nLütfen yukarıdaki tüm detayları yaptığınız tüm sorgulara dahil ediniz.",
        "blockedtext-composite-reason": "Hesabınızda ve/veya IP adresinizde birden fazla engel mevcut.",
        "whitelistedittext": "Değişiklik yapabilmek için $1.",
        "confirmedittext": "Sayfa değiştirmeden önce e-posta adresinizi onaylamalısınız. Lütfen [[Special:Preferences|tercihler]] kısmından e-postanızı ekleyin ve onaylayın.",
index ee83ec7..15071bc 100644 (file)
@@ -70,7 +70,7 @@
        "tog-watchlisthidecategorization": "Битләрне төркемләшү яшерелсен",
        "tog-ccmeonemails": "Башка кулланучыларга җибәргән хатларымның копияләре миңа да җибәрелсен",
        "tog-diffonly": "Юрама чагыштыру астында бит эчтәлеге күрсәтелмәсен",
-       "tog-showhiddencats": "Яшерен төркемнәр күрсәтелсен",
+       "tog-showhiddencats": "Яшерен төркемнәр күрсәтергә",
        "tog-norollbackdiff": "Кире кайтару ясагач юрамалар аермасы күрсәтелмәсен",
        "tog-useeditwarning": "Битне сакламыйча киткәндә мине кисәтергә",
        "tog-prefershttps": "Системага кергәндә һәрвакыт саклаулы тоташу кулланылсын",
        "oct": "окт",
        "nov": "ноя",
        "dec": "дек",
-       "january-date": "$1 Ð\93ыйнвар",
-       "february-date": "$1 Февраль",
-       "march-date": "$1 Ð\9cарт",
-       "april-date": "$1 Ð\90прель",
-       "may-date": "$1 Ð\9cай",
-       "june-date": "$1 Ð\98юнь",
-       "july-date": "$1 Ð\98юль",
-       "august-date": "$1 Ð\90вгуст",
-       "september-date": "$1 Сентябрь",
-       "october-date": "$1 Ð\9eктябрь",
-       "november-date": "$1 Ð\9dоябрь",
+       "january-date": "$1 Ð³ыйнвар",
+       "february-date": "$1 февраль",
+       "march-date": "$1 Ð¼арт",
+       "april-date": "$1 Ð°прель",
+       "may-date": "$1 Ð¼ай",
+       "june-date": "$1 Ð¸юнь",
+       "july-date": "$1 Ð¸юль",
+       "august-date": "$1 Ð°вгуст",
+       "september-date": "$1 сентябрь",
+       "october-date": "$1 Ð¾ктябрь",
+       "november-date": "$1 Ð½оябрь",
        "december-date": "$1 декабрь",
        "period-am": "ТК",
        "period-pm": "ТС",
        "navigation": "Навигация",
        "and": "&#32;һәм",
        "faq": "ЕБС",
-       "actions": "Ð\93амÓ\99лләр",
+       "actions": "Ð¥Ó\99Ñ\80Ó\99кÓ\99Ñ\82ләр",
        "namespaces": "Исемнәр мәйданы",
        "variants": "Вариантлар",
        "navigation-heading": "Навигация",
        "returnto": "$1 битенә кайту.",
        "tagline": "{{SITENAME}} проектыннан",
        "help": "Ярдәм",
+       "help-mediawiki": "MediaWiki турында белешмә",
        "search": "Эзләү",
        "searchbutton": "Эзләү",
        "go": "Күчү",
        "history": "Битнең тарихы",
        "history_short": "Тарих",
        "history_small": "тарих",
-       "updatedmarker": "Ñ\81оңгÑ\8b ÐºÐµÑ\80үемнән соң яңартылган",
+       "updatedmarker": "Ñ\81оңгÑ\8b ÐºÐµÑ\80үегездән соң яңартылган",
        "printableversion": "Басма юрама",
        "permalink": "Даими сылтама",
        "print": "Бастыру",
        "redirectedfrom": "($1 битеннән юнәлтелде)",
        "redirectpagesub": "Юнәлтү бите",
        "redirectto": "Шунда юнәлтелә:",
-       "lastmodifiedat": "Бу бит соңгы тапкыр $2 $1 үзгәртелә.",
+       "lastmodifiedat": "Бу бит соңгы тапкыр $1 көнендә $2 вакытында үзгәртелгән.",
        "viewcount": "Бу биткә $1 {{PLURAL:$1|бер тапкыр|$1 тапкыр}} мөрәҗәгать иттеләр.",
        "protectedpage": "Якланган бит",
        "jumpto": "Моңа күчү:",
        "viewsourceold": "чыганак кодны карау",
        "editlink": "үзгәртү",
        "viewsourcelink": "чыганак кодны карау",
-       "editsectionhint": "$1 бүлеген үзгәртү",
+       "editsectionhint": "«$1» бүлеген үзгәртергә",
        "toc": "Эчтәлек",
        "showtoc": "күрсәтү",
        "hidetoc": "яшерү",
        "error": "Хата",
        "databaseerror": "Мәгълүматлар базасында хата",
        "databaseerror-textcl": "Мәгълүмат базасында хата чыкты",
-       "databaseerror-query": "СоÑ\80аÑ\82Ñ\83: $1",
+       "databaseerror-query": "Сорау: $1",
        "databaseerror-function": "Функция: $1",
        "databaseerror-error": "Хата: $1",
        "laggedslavemode": "Игътибар: биттә соңгы яңартулар күрсәтелмәгән булырга мөмкин.",
        "readonly_lag": "Мәгълүматлар базасы, өстәмә сервер төп сервер белән синхронизацияләшкәнче, үзгәрүләрдән автомат рәвештә ябылды.",
        "internalerror": "Эчке хата",
        "internalerror_info": "Эчке хата: $1",
+       "internalerror-fatal-exception": "«$1» тибындагы котылгысыз чыгармалар",
        "filecopyerror": "«$2» файлына «$1» файлының копиясен ясап булмый.",
        "filerenameerror": "«$1» файлының исемен «$2» исеменә алыштырып булмый.",
        "filedeleteerror": "«$1» файлын бетереп булмый.",
        "badarticleerror": "Бу биттә мондый гамәл башкарып булмый.",
        "cannotdelete": "«$1» исемле битне яки файлны бетереп булмый. Аны бүтән кулланучы бетергән булырга мөмкин.",
        "cannotdelete-title": "«$1» битен бетереп булмый",
+       "delete-scheduled": "«$1» бите бетерү өчен планлаштырылган. \nСабыр булыгыз.",
        "delete-hook-aborted": "Үзгәртү махсус процедура тарафыннан кире кагыла.\nСәбәпләре китерелми.",
+       "no-null-revision": "«$1» бите өчен яңа нуленче юраманы ясап булмады",
        "badtitle": "Яраксыз исем",
        "badtitletext": "Битнең соралган исеме дөрес түгел, буш яисә телъара яки интервики исеме дөрес күрсәтелмәгән. Исемдә тыелган символлар кулланылган булырга мөмкин.",
+       "title-invalid-characters": "Соралган битнең исемендә рөхсәт булмаган символлар бар: «$1».",
        "perfcached": "Бу мәгълүматлар кэштан алынган, аларда соңгы үзгәртүләр булмаска мөмкин. Кэшта иң күбе {{PLURAL:$1|язма|$1 язмалар}}  саклана.",
        "perfcachedts": "Бу мәгълүматлар кэштан алынган, ул соңгы тапкыр $1 яңартылды. Кэшта иң күбе {{PLURAL:$4|язма}} саклана",
        "querypage-no-updates": "Хәзер бу битне яңартып булмый. Монда күрсәтелгән мәгълүматлар кабул ителмәячәк.",
        "botpasswords-bad-appid": "Атамасы «$1» булган бот исеме ярамый.",
        "botpasswords-created-title": "Бот серсүзе булдырылды",
        "resetpass_forbidden": "Серсүз үзгәртелә алмый",
+       "resetpass_forbidden-reason": "Серсүзләрне үзгәртергә мөмкин түгел: $1",
        "resetpass-no-info": "Бу битне карау өчен сез системага үз хисап язмагыз ярдәмендә керергә тиеш.",
        "resetpass-submit-loggedin": "Серсүзне үзгәртү",
        "resetpass-submit-cancel": "Кире кагу",
        "image_tip": "Куелган файл",
        "media_sample": "Мисал.ogg",
        "media_tip": "Файлга сылтама",
-       "sig_tip": "Имза һәм вакыт",
+       "sig_tip": "Имзагыз вакыт белән",
        "hr_tip": "Горизонталь сызык (еш кулланмагыз)",
        "summary": "Үзгәртүләр тасвирламасы:",
        "subject": "Тема:",
-       "minoredit": "Бу кече үзгәртү",
+       "minoredit": "Бу кече төзәтмә",
        "watchthis": "Бу битне күзәтү",
        "savearticle": "Битне саклау",
        "savechanges": "Үзгәртүләрне саклау",
        "page_first": "беренче",
        "page_last": "соңгы",
        "histlegend": "Аңлатмалар: '''({{int:cur}})''' = хәзерге юрамадан аерымлыклар, '''({{int:last}})''' = баягы юрамадан аерымлыклар, '''{{int:minoreditletter}}''' = кече үзгәртүләр.",
-       "history-fieldset-title": "Үзгәртүләрне эзләү",
+       "history-fieldset-title": "Үзгәртмәләрне сөзү",
        "history-show-deleted": "Бары тик бетерелгән төзәтмәләр",
        "histfirst": "иң иске",
        "histlast": "иң яңа",
        "historysize": "($1 {{PLURAL:$1|байт}})",
        "historyempty": "(буш)",
-       "history-feed-title": "Үзгәртүләр тарихы",
-       "history-feed-description": "Бу битнең викидагы үзгәртүләр тарихы",
+       "history-feed-title": "Үзгәртү тарихы",
+       "history-feed-description": "Бу битнең викидагы үзгәртү тарихы",
        "history-feed-item-nocomment": "$1, $2",
        "history-feed-empty": "Соратылган бит юк.\nУл бетерелгән яисә бүтән урынга күчерелгән (башка исем алган) булырга мөмкин.\n[[Special:Search|Эзләтеп]] карагыз.",
        "rev-deleted-comment": "(үзгәртүләрнең тасвиры бетерелгән)",
        "compareselectedversions": "Сайланган юрамаларны чагыштыру",
        "showhideselectedversions": "Сайланган юрамаларны күрсәтү/яшерү",
        "editundo": "үткәрмәү",
-       "diff-empty": "(аермалар юк)",
+       "diff-empty": "(аермасы юк)",
        "diff-multi-sameuser": "(шул ук кулланучының {{PLURAL:$1|бер арадаш юрамасы|$1 арадаш юрамасы}} күрсәтелмәгән)",
        "diff-multi-otherusers": "({{PLURAL:$2|Башка бер кулланучының|$2 кулланучының}} {{PLURAL:$1|бер арадаш юрамасы|$1 арадаш юрамасы}} күрсәтелмәгән)",
        "diff-multi-manyusers": "($2 күбрәк {{PLURAL:$2|кулланучының|кулланучының}} {{PLURAL:$1|Бер арадаш юрамасы|$1 арадаш юрамасы}} күрсәтелмәгән)",
        "recentchanges-summary": "Төрле битләрдә эшләнгән соңгы үзгәртүләр исемлеге.",
        "recentchanges-noresult": "Сайланган чорда күрсәтелгән шартларга туры килүче төзәтмәләр юк.",
        "recentchanges-feed-description": "Бу агымда соңгы үзгәртүләрне күзәтү.",
-       "recentchanges-label-newpage": "Бу үзгәртү белән яңа бит төзелгән",
-       "recentchanges-label-minor": "Бу кече үзгәртү",
-       "recentchanges-label-bot": "Бу үзгәртү бот белән эшләнгән",
-       "recentchanges-label-unpatrolled": "Үзгәртүне әлегә тикшермәгәннәр",
+       "recentchanges-label-newpage": "Бу төзәтмә белән яңа бит төзелгән",
+       "recentchanges-label-minor": "Бу кече төзәтмә",
+       "recentchanges-label-bot": "Бу төзәтмә бот белән эшләнгән",
+       "recentchanges-label-unpatrolled": "Бу тәзәтмә тикшерелмәгән әле",
        "recentchanges-label-plusminus": "Битнең зурлыгы шуның кадәрле байтка үзгәрде",
        "recentchanges-legend-heading": "<strong>Аңлатма:</strong>",
        "recentchanges-legend-newpage": "{{int:recentchanges-label-newpage}} (шулай ук [[Special:NewPages|яңа битләр исемлеген]] карагыз)",
        "rcfilters-preference-label": "«Соңгы үзгәртүләр» битенең яңа юрамасын яшерү",
        "rcnotefrom": "Астарак <strong>$3, $4</strong> өчен {{PLURAL:$5|үзгәртүләр күрсәтелгән}} (<strong>$1</strong> артык түгел).",
        "rclistfrom": "$3 $2 башлап яңа үзгәртүләрне күрсәт",
-       "rcshowhideminor": "кече үзгәртүләрне $1",
+       "rcshowhideminor": "кече төзәтмәләрне $1",
        "rcshowhideminor-show": "Күрсәтү",
-       "rcshowhideminor-hide": "Яшер",
+       "rcshowhideminor-hide": "Яшерү",
        "rcshowhidebots": "ботларны $1",
-       "rcshowhidebots-show": "Күрсәт",
-       "rcshowhidebots-hide": "Яшер",
+       "rcshowhidebots-show": "Күрсәтү",
+       "rcshowhidebots-hide": "Яшерү",
        "rcshowhideliu": "теркәлгән кулланучыларны $1",
        "rcshowhideliu-show": "Күрсәт",
-       "rcshowhideliu-hide": "Яшер",
+       "rcshowhideliu-hide": "Яшерү",
        "rcshowhideanons": "кермәгән кулланучыларны $1",
        "rcshowhideanons-show": "Күрсәтү",
-       "rcshowhideanons-hide": "Яшер",
-       "rcshowhidepatr": "тикшерелгән үзгәртүләрне $1",
-       "rcshowhidepatr-show": "Ð\9aүрсәтү",
-       "rcshowhidepatr-hide": "яшер",
-       "rcshowhidemine": "минем үзгәртүләремне $1",
+       "rcshowhideanons-hide": "Яшерү",
+       "rcshowhidepatr": "Тикшерелгән төзәтмәләрне $1",
+       "rcshowhidepatr-show": "күрсәтү",
+       "rcshowhidepatr-hide": "яшерү",
+       "rcshowhidemine": "минем төзәтмәләремне $1",
        "rcshowhidemine-show": "Күрсәтү",
-       "rcshowhidemine-hide": "Яшер",
+       "rcshowhidemine-hide": "Яшерү",
        "rcshowhidecategorization": "битләрне төркемләүне $1",
        "rcshowhidecategorization-show": "Күрсәт",
        "rcshowhidecategorization-hide": "Яшер",
        "rc-old-title": "башта «$1» буларак ясала",
        "recentchangeslinked": "Бәйләнешле үзгәртүләр",
        "recentchangeslinked-feed": "Бәйләнешле үзгәртүләр",
-       "recentchangeslinked-toolbox": "Бәйләнешле үзгәртүләр",
+       "recentchangeslinked-toolbox": "Бәйләнешле үзгәрешләр",
        "recentchangeslinked-title": "\"$1\" битенә бәйләнешле үзгәртүләр",
        "recentchangeslinked-summary": "Бу битттән яисә бу биткә сылтаган битләрдәге үзгәртмәле карау өчен битнең исемен кертегез. (Билгеле бер төркемгә караган битләрне карау өчен {{ns:category}}:Төркем исемен языгыз).[[Special:Watchlist|Күзәтү исемлегегезгә]] керә торган битләр '''калын''' итеп күрсәтелгән.",
        "recentchangeslinked-page": "Битнең исеме:",
        "recentchangeslinked-to": "Моның урынына бу биткә бәйле булган битләрдәге үзгәртүләрне күрсәтү",
-       "upload": "Файл йөкләү",
+       "upload": "Файл төяү",
        "uploadbtn": "Файлны йөкләү",
        "reuploaddesc": "Файлны йөкләүгә кире кату",
        "upload-tryagain": "Яңартылган файлны җибәрү",
        "magiclink-tracking-pmid": "Тылсымлы PMID сылтамаларын кулланучы битләр",
        "magiclink-tracking-isbn": "Тылсымлы ISBN сылтамаларын кулланучы битләр",
        "specialloguserlabel": "Башкаручы:",
-       "speciallogtitlelabel": "Ð\9cакÑ\81аÑ\82 (аÑ\82ама Ñ\8fиÑ\81Ó\99 {{ns:user}}:кулланучы исеме):",
+       "speciallogtitlelabel": "Ð\9cакÑ\81аÑ\82 (аÑ\82ама Ñ\8fки {{ns:user}}:кулланучы исеме):",
        "log": "Көндәлекләр",
        "logeventslist-submit": "Күрсәт",
        "all-logs-page": "Барлык көндәлекләр",
        "allpagessubmit": "Башкару",
        "allpagesprefix": "Алкушымчалы битләрне күрсәтү:",
        "allpages-bad-ns": "{{SITENAME}} проектында «$1» исемнәр мәйданы юк.",
-       "allpages-hide-redirects": "Юнәлтүләрне яшер",
+       "allpages-hide-redirects": "Юнәлтүләрне яшерү",
        "cachedspecial-refresh-now": "Соңгы юраманы карау.",
        "categories": "Төркемнәр",
        "categories-submit": "Күрсәт",
        "mycontris": "Кертем",
        "anoncontribs": "Кертем",
        "contribsub2": "Кертем {{GENDER:$3|$1}} ($2)",
-       "nocontribs": "Ð\9aÒ¯Ñ\80Ñ\81Ñ\88Ñ\82елгÓ\99н Ñ\88аÑ\80Ñ\82лаÑ\80га Ò\97авап Ð±Ð¸Ñ\80гÓ\99н Ò¯Ð·Ð³Ó\99Ñ\80еÑ\88лÓ\99Ñ\80 Ñ\82аÒ\97Ñ\8bлмадÑ\8b.",
+       "nocontribs": "Ð\91Ñ\83 Ñ\88аÑ\80Ñ\82лаÑ\80га Ñ\82Ñ\83Ñ\80Ñ\8b ÐºÐ¸Ð»Ð³Ó\99н Ò¯Ð·Ð³Ó\99Ñ\80еÑ\88лÓ\99Ñ\80 Ñ\82абÑ\8bлмаган.",
        "uctop": "хәзерге",
        "month": "Айдан башлап (һәм элегрәк):",
        "year": "Елдан башлап (һәм элегрәк):",
        "ipblocklist-submit": "Эзләү",
        "ipblocklist-localblock": "Локаль тыюлык",
        "ipblocklist-otherblocks": "Башка {{PLURAL:$1|1=тыю|тыюлар}}",
-       "infiniteblock": "билгеле бер вакытсыз",
+       "infiniteblock": "чикләнмәгән",
        "expiringblock": "$1 $2 тәмамлана",
        "anononlyblock": "анонимнар гына",
        "noautoblockblock": "автотыю ябык",
        "tooltip-pt-preferences": "{{GENDER:|Көйләнмәләрегез}}",
        "tooltip-pt-watchlist": "Сез күзәтелгән төзәтмәле битләр исемлеге",
        "tooltip-pt-mycontris": "{{GENDER:|Сезнең}} кертемегез",
-       "tooltip-pt-login": "Сез хисап язмасы төзи алыр идегез, әмма бу мәҗбүри түгел.",
+       "tooltip-pt-login": "Сез керү алыр идегез, әмма бу мәҗбүри түгел.",
        "tooltip-pt-logout": "Чыгу",
        "tooltip-pt-createaccount": "Сезгә аккаунт ясарга һәм системага керергә киңәш итәбез, әмма бу мәҗбүри түгел.",
        "tooltip-ca-talk": "Битнең эчтәлеге турында бәхәс",
        "tooltip-ca-edit": "Бу битне үзгәртү",
        "tooltip-ca-addsection": "Яңа бүлек башлау",
        "tooltip-ca-viewsource": "Бу бит үзгәртүдән якланган. Сез аның чыганак текстын гына карый аласыз.",
-       "tooltip-ca-history": "Битнең төзәтмәләр исемлеге",
+       "tooltip-ca-history": "Битнең төзәтмәләре исемлеге",
        "tooltip-ca-protect": "Бу битне яклау",
-       "tooltip-ca-delete": "Бу битне бетерү",
+       "tooltip-ca-delete": "Бу битне бетерергә",
        "tooltip-ca-move": "Бу битне күчерү",
        "tooltip-ca-watch": "Бу битне сезнең күзәтү исемлегезгә өстәү",
        "tooltip-ca-unwatch": "Бу битне сезнең күзәтү исемлегездә бетерү",
        "tooltip-search": "{{SITENAME}} эчендә эзләү",
-       "tooltip-search-go": "Нәкъ шундый исеме белән биткә күчәрү",
+       "tooltip-search-go": "Нәкъ шундый исеме белән биткә күчү",
        "tooltip-search-fulltext": "Бу текст белән битләрне табу",
        "tooltip-p-logo": "Баш биткә күчү",
        "tooltip-n-mainpage": "Баш биткә күчү",
        "tooltip-n-mainpage-description": "Баш биткә күчү",
-       "tooltip-n-portal": "Ð\9fÑ\80оекÑ\82 Ñ\82Ñ\83Ñ\80Ñ\8bнда, Ñ\81ез Ð½Ó\99Ñ\80Ñ\81Ó\99 Ð¸Ñ\82Ó\99 Ð°Ð»Ð°Ñ\81Ñ\8bз Ò»Ó\99м Ð½Ó\99Ñ\80Ñ\81Ó\99 ÐºÐ°Ð¹Ð´Ð° Ð±Ñ\83ла Ð´Ð¸Ð¿ Ñ\82Ñ\83Ñ\80Ñ\8bнда.",
-       "tooltip-n-currentevents": "Ð\90гÑ\8bмдагÑ\8b Ð²Ð°ÐºÑ\8bйгалаÑ\80 Ñ\82Ñ\83Ñ\80Ñ\8bнда Ð¼Ó\99гÑ\8aлүмаÑ\82нÑ\8b Ñ\82абÑ\83",
+       "tooltip-n-portal": "Ð\91Ñ\83 Ð¿Ñ\80оекÑ\82 Ñ\82Ñ\83Ñ\80Ñ\8bнда, Ð¼Ð¾Ð½Ð´Ð° Ð½Ó\99Ñ\80Ñ\81Ó\99 Ð¸Ñ\82Ó\99 Ð°Ð»Ð°Ñ\81Ñ\8bз Ò»Ó\99м ÐºÐ°Ð¹Ð´Ð° Ð½Ó\99Ñ\80Ñ\81Ó\99деÑ\80 Ñ\82аба Ð°Ð»Ð°Ñ\81Ñ\8bз",
+       "tooltip-n-currentevents": "Ð¥Ó\99зеÑ\80ге Ð²Ð°ÐºÑ\8bйгалаÑ\80 Ñ\82Ñ\83Ñ\80Ñ\8bнда Ð¼Ó\99гÑ\8aлүмаÑ\82нÑ\8b Ñ\82абаÑ\80га",
        "tooltip-n-recentchanges": "Соңгы үзгәртүләр исемлеге",
        "tooltip-n-randompage": "Очраклы битне карау",
-       "tooltip-n-help": "«{{SITENAME}}» проектының белешмәлек",
+       "tooltip-n-help": "Белешмә алу өчен урын",
        "tooltip-t-whatlinkshere": "Бирегә сылтаган барлык битләрнең исемлеге",
-       "tooltip-t-recentchangeslinked": "Бу биттән сылтаган битләрдә ахыргы үзгәртүләр",
+       "tooltip-t-recentchangeslinked": "Бу биттән сылтаулаган битләрдә соңгы үзгәрешләр",
        "tooltip-feed-rss": "Бу бит өчен RSS трансляциясе",
        "tooltip-feed-atom": "Бу бит өчен Atom трансляциясе",
        "tooltip-t-contributions": "{{GENDER:$1|Бу кулланучының}} кертем исемлеге",
        "tooltip-t-emailuser": "{{GENDER:$1|Бу кулланучыга}} хат җибәрү",
-       "tooltip-t-upload": "Файлларны йөкләү",
+       "tooltip-t-upload": "Файллар төяү",
        "tooltip-t-specialpages": "Барлык махсус битләр исемлеге",
        "tooltip-t-print": "Бу битнең басма юрамасы",
        "tooltip-t-permalink": "Битнең бу юрамасына даими сылтама",
        "tooltip-ca-nstab-template": "Үрнәк бите",
        "tooltip-ca-nstab-help": "Ярдәм битен карау",
        "tooltip-ca-nstab-category": "Төркем битен карау",
-       "tooltip-minoredit": "Бу үзгәртүне кече дип билгелү",
+       "tooltip-minoredit": "Бу төзәтмәне кече дип билгеләү",
        "tooltip-save": "Үзгәртүләрегезне саклау",
        "tooltip-preview": "Алдан карау, саклау алдыннан үзгәртүләрегезнең карап чыгыгыз!",
        "tooltip-diff": "Сезнең үзгәртүләрегезне күрсәтү.",
        "simpleantispam-label": "Анти-спам тикшерә.\nМоны <strong>ТУТЫРМАГЫЗ!</strong>",
        "pageinfo-title": "«$1» буенча мәгълүмат",
        "pageinfo-header-basic": "Төп мәгълүмат",
-       "pageinfo-header-edits": "Үзгәртүләр тарихы",
+       "pageinfo-header-edits": "Үзгәртү тарихы",
        "pageinfo-header-restrictions": "Битне яклау",
        "pageinfo-header-properties": "Битнең үзенчәлекләре",
        "pageinfo-display-title": "Күренмә башлык",
        "pageinfo-redirects-name": "Бу биткә юнәлтүләр саны",
        "pageinfo-subpages-name": "Бу битнең асбитләре",
        "pageinfo-subpages-value": "$1 ($2 {{PLURAL:$2|юнәлтү}}; $3 {{PLURAL:$3|гади}})",
-       "pageinfo-firstuser": "Битне төзүче",
+       "pageinfo-firstuser": "Бит төзүче",
        "pageinfo-firsttime": "Битне төзү датасы",
-       "pageinfo-lastuser": "Соңгы мөхәррирләүче",
-       "pageinfo-lasttime": "Соңгы үзгәртү датасы",
-       "pageinfo-edits": "Ð\93омÑ\83ми Ñ\82өзÓ\99Ñ\82үлÓ\99Ñ\80 саны",
-       "pageinfo-authors": "Ð\93омÑ\83ми Ð°Ð²Ñ\82оÑ\80лаÑ\80 саны",
-       "pageinfo-recent-edits": "Соңгы вакытта үзгәртүләр ($1 эчендә)",
-       "pageinfo-recent-authors": "Ð\9aабаÑ\82ланмаÑ\81 авторлар саны",
-       "pageinfo-magic-words": "{{PLURAL:$1|1=Тылсымлы сүз|Тылсымлы сүзләр}} ($1)",
-       "pageinfo-hidden-categories": "{{PLURAL:$1|1=Яшерен төркем|Яшерен төркемнәр}} ($1)",
+       "pageinfo-lastuser": "Соңгы үзгәртүче",
+       "pageinfo-lasttime": "Соңгы төзәтмә вакыты",
+       "pageinfo-edits": "ТөзÓ\99Ñ\82мÓ\99лÓ\99Ñ\80нең Ñ\82Ñ\83лаем саны",
+       "pageinfo-authors": "ТеÑ\80ле Ð°Ð²Ñ\82оÑ\80лаÑ\80нÑ\8bÒ£ Ñ\82Ñ\83лаем саны",
+       "pageinfo-recent-edits": "Соңгы вакытта төзәтмәләр саны ($1 эчендә)",
+       "pageinfo-recent-authors": "СоңгÑ\8b Ð²Ð°ÐºÑ\8bÑ\82Ñ\82а Ð°ÐµÑ\80Ñ\8bм авторлар саны",
+       "pageinfo-magic-words": "Тылсымлы {{PLURAL:$1|сүз|сүзләр}} ($1)",
+       "pageinfo-hidden-categories": "Яшерен {{PLURAL:$1|төркем|төркемнәр}} ($1)",
        "pageinfo-templates": "{{PLURAL:$1|1=Калып|Калыплар}} ($1)",
        "pageinfo-toolboxlink": "Бит турында мәгълүмат",
        "pageinfo-redirectsto": "Юнәлтү",
        "filedeleteerror-short": "Файлны бетерү хатасы: $1",
        "filedeleteerror-long": "Файлны бетерү вакытында хаталар чыкты:\n\n$1",
        "filedelete-missing": "«$1» исемле файлны бетерергә мөмкин түгел, чөнки ул юк.",
-       "previousdiff": "← Алдагы үзгәртү",
-       "nextdiff": "ЧиÑ\80аÑ\82Ñ\82агÑ\8b Ò¯Ð·Ð³Ó\99Ñ\80Ñ\82Ò¯ →",
+       "previousdiff": "← Алдагы төзәтмә",
+       "nextdiff": "Ð\9aилÓ\99Ñ\81е Ñ\82өзÓ\99Ñ\82мÓ\99 →",
        "imagemaxsize": "Рәсемнең зурлыгына чикләүләр:<br />''(тасвирлау бите өчен)''",
        "thumbsize": "Рәсемнең кечерәйтелгән юрамасы өчен:",
        "widthheight": "$1 × $2",
        "autosumm-new": "Яңа бит: «$1»",
        "watchlistedit-raw-titles": "Язмалар:",
        "watchlistedit-clear-titles": "Башлык:",
-       "watchlisttools-clear": "Күзәтү исемлеген чистарту",
+       "watchlisttools-clear": "Күзәтү исемлеген бушату",
        "watchlisttools-view": "Соңгы үзгәртүләрне күрсәтү",
-       "watchlisttools-edit": "Күзәтү исемлегене карау һәм үзгәртү",
+       "watchlisttools-edit": "Күзәтү исемлеген карау һәм үзгәртү",
        "watchlisttools-raw": "Текст сыман үзгәртү",
        "hijri-calendar-m1": "Мөхәррәм",
        "hijri-calendar-m7": "Раҗәб",
        "tags-delete": "бетерү",
        "tags-activate": "активлаштыру",
        "tags-deactivate": "сүндерү",
-       "tags-hitcount": "$1 {{PLURAL:$1|үзгәртү}}",
+       "tags-hitcount": "$1 {{PLURAL:$1|үзгәреш}}",
        "tags-create-submit": "Төзү",
        "tags-deactivate-submit": "Өзергә",
        "comparepages": "Битләрне чагыштыру",
index f9eeb8e..7e49ece 100644 (file)
        "autoblockedtext": "Ваша IP-адреса автоматично заблокована у зв'язку з тим, що вона раніше використовувалася кимось із користувачів, якого заблокував $1.\nПричина блокування блокування:\n\n:<em>$2</em>\n\n* Початок блокування: $8\n* Закінчення блокування: $6\n* Блокування виконав: $7\n\nВи можете надіслати листа користувачеві $1 або будь-якому іншому [[{{MediaWiki:Grouppage-sysop}}|адміністратору]], щоб обговорити блокування.\n\nЗверніть увагу, що ви не зможете скористатися функцією \"{{int:emailuser}}\", так як не маєте дійсної електронної пошти, зареєстрованої в [[Special:Preferences|особистих налаштуваннях]], а також якщо вам було заборонено надсилати листи при блокуванні.\n\nВаша поточна IP-адреса — $3, ідентифікатор блокування — #$5. Будь ласка, зазначайте ці дані у своїх запитах.",
        "systemblockedtext": "Ваше ім'я користувача або IP-адресу було автоматично заблоковано MediaWiki.\nВказана причина:\n\n:<em>$2</em>\n\n* Початок блокування: $8\n* Закінчення блокування: $6\n* Ціль блокування: $7\n\nВаша поточна IP-адреса — $3.\nБудь ласка, додайте всі вказані подробиці до будь-яких запитів, які Ви будете робити.",
        "blockednoreason": "не вказано причини",
-       "blockedtext-composite": "<strong>Ваше ім'я користувача або IP-адресу було заблоковано.</strong>\n\nВказана причина:\n\n:<em>$2</em>.\n\n* Початок блокування: $8\n* Закінчення найдовшого блокування: $6\n\nВаша поточна IP-адреса — $3.\nБудь ласка, додайте всі вказані подробиці до будь-яких запитів, які Ви будете робити.",
+       "blockedtext-composite": "<strong>Ваше ім'я користувача або IP-адресу було заблоковано.</strong>\n\nВказана причина:\n\n:<em>$2</em>.\n\n* Початок блокування: $8\n* Закінчення найдовшого блокування: $6\n\n* $5\n\nВаша поточна IP-адреса — $3.\nБудь ласка, додайте всі вказані подробиці до будь-яких запитів, які Ви будете робити.",
        "blockedtext-composite-reason": "Встановлено кілька блокувань для Вашого облікового запису та/або IP-адреси",
        "whitelistedittext": "Ви повинні $1, щоб редагувати сторінки.",
        "confirmedittext": "Ви повинні підтвердити вашу адресу електронної пошти перед редагуванням сторінок.\nБудь-ласка вкажіть і підтвердіть вашу електронну адресу на [[Special:Preferences|сторінці налаштувань]].",
index be09343..604a253 100644 (file)
                        "Viztor",
                        "Ps2049",
                        "Suchichi02",
-                       "神樂坂秀吉"
+                       "神樂坂秀吉",
+                       "WQL"
                ]
        },
        "tog-underline": "链接下划线:",
        "autoblockedtext": "您的IP地址因曾被一位被$1封禁的用户使用而被自动封禁。封禁原因:\n\n:<em>$2</em>\n\n* 开始时间:$8\n* 到期时间:$6\n* 目标用户:$7\n\n您可以联系$1或其他[[{{MediaWiki:Grouppage-sysop}}|管理员]]申诉该封禁。\n\n请注意,只有当您已在[[Special:Preferences|系统设置]]确认了电子邮件地址且未被禁止使用“{{int:emailuser}}”功能时,才能发送电子邮件联系管理员。\n\n您当前的IP地址为$3,该封禁ID为#$5。请在您做出的任何查询中包含所有上述详情。",
        "systemblockedtext": "您的用户名或IP地址已被MediaWiki自动封禁。封禁原因:\n\n:<em>$2</em>\n\n* 开始时间:$8\n* 到期时间:$6\n* 目标用户:$7\n\n您当前的IP地址是$3。请在您做出的任何查询中包含所有上述详情。",
        "blockednoreason": "未给出原因",
-       "blockedtext-composite": "您的用户名或IP地址已被封禁。封禁原因:\n\n:<em>$2</em>\n\n* 开始时间:$8\n* 到期时间:$6\n\n您当前的IP地址是$3。请在您做出的任何查询中包含所有上述详情。",
+       "blockedtext-composite": "您的用户名或IP地址已被封禁。封禁原因:\n\n:<em>$2</em>\n\n* 开始时间:$8\n* 到期时间:$6\n\n* $5\n\n您当前的IP地址是$3。请在您做出的任何查询中包含所有上述详情。",
        "blockedtext-composite-reason": "有多个封禁目标为您的账户和/或IP地址",
        "whitelistedittext": "请$1以编辑页面。",
        "confirmedittext": "您必须确认您的电子邮件地址才能编辑页面。请通过[[Special:Preferences|系统设置]]设置并确认您的电子邮件地址。",
        "removedwatchtext": "“[[:$1]]”及其讨论页已从您的[[Special:Watchlist|监视列表]]中移除。",
        "removedwatchtext-talk": "“[[:$1]]”及其相关联页面已从您的[[Special:Watchlist|监视列表]]中移除。",
        "removedwatchtext-short": "页面“$1”已从您的监视列表移除。",
-       "watch": "关注",
+       "watch": "监视",
        "watchthispage": "监视本页",
        "unwatch": "取消监视",
        "unwatchthispage": "停止监视",
        "edit-error-short": "错误:$1",
        "edit-error-long": "错误:\n\n$1",
        "specialmute": "屏蔽",
+       "specialmute-success": "您的提及设置已经被更新。请在[[Special:参数设置|您的参数设置]]中查看所有被提及的用户。",
        "specialmute-submit": "确认",
        "specialmute-label-mute-email": "屏蔽该用户的邮件",
        "specialmute-error-invalid-user": "未找到您请求的用户名。",
+       "specialmute-error-email-blacklist-disabled": "沉默向你发电子邮件的用户的功能没启用。",
+       "specialmute-error-email-preferences": "你需要去确认你的电子邮件地址才能沉默用户提醒。你可能需要去[[Special:Preferences|参数设置]]进行设置。",
+       "specialmute-login-required": "请登录以更改您的沉默用户提醒设置。",
        "revid": "修订版本$1",
        "pageid": "页面ID$1",
        "interfaceadmin-info": "$1\n\n编辑全站CSS/JS/JSON文件的权限刚刚从<code>editinterface</code>权限中拆分。如果您不知道为何收到此错误,请参见[[mw:MediaWiki_1.32/interface-admin]]。",
index 1506e55..032973c 100644 (file)
        "autoblockedtext": "因先前的另一位使用者被 $1 封鎖,您的 IP 位址已被自動封鎖。\n原因是:\n\n:<em>$2</em>\n\n* 封鎖開始時間:$8\n* 封鎖結束時間:$6\n* 相關封鎖對象:$7\n\n您可以聯絡 $1 或其他的 [[{{MediaWiki:Grouppage-sysop}}|管理員]] 討論封鎖的相關問題。\n若您已在 [[Special:Preferences|偏好設定]] 中設定了一個有效的電子郵件地址,且尚未被封鎖郵件功能,則您可透過 \"{{int:emailuser}}\" 的功能來聯絡相關管理員。\n您目前的 IP 位址是 $3,此次封鎖的 ID 為 #$5。\n請您在詢問時附註以上詳細資料。",
        "systemblockedtext": "您的使用者名稱或 IP 位址已被 MediaWiki 自動封鎖,原因如下:\n\n:<em>$2</em>\n\n* 封鎖開始時間:$8\n* 封鎖結束時間:$6\n* 被封鎖的使用者:$7\n\n您目前的 IP 位址為 $3。\n請在做詢問時附上以上資訊。",
        "blockednoreason": "未說明原因",
-       "blockedtext-composite": "<strong>您的使用者名稱或 IP 位址已被封鎖。</strong>\n\n原因如下:\n\n:<em>$2</em>\n\n* 封鎖開始時間:$8\n* 最長的封鎖結束時間:$6\n\n您目前的 IP 位址為 $3。\n請在做詢問時附上以上資訊。",
+       "blockedtext-composite": "<strong>您的使用者名稱或 IP 位址已被封鎖。</strong>\n\n原因如下:\n\n:<em>$2</em>\n\n* 封鎖開始時間:$8\n* 最長的封鎖結束時間:$6\n\n* $5\n\n您目前的 IP 位址為 $3。\n請在做詢問時附上以上資訊。",
        "blockedtext-composite-reason": "有多個封鎖目標為您的帳號和/或IP位址",
        "whitelistedittext": "請先 $1 才可編輯頁面。",
        "confirmedittext": "在編輯此頁之前您必須確認您的電子郵件地址。\n請透過 [[Special:Preferences|偏好設定]] 設定並驗證您的電子郵件地址。",
        "restrictionsfield-help": "一個 IP 位址或 CIDR 範圍一行,要開啟所有範圍可使用:<pre>0.0.0.0/0\n::/0</pre>",
        "edit-error-short": "錯誤:$1",
        "edit-error-long": "錯誤:\n\n$1",
+       "specialmute": "遮蓋",
+       "specialmute-success": "您的遮蓋偏好設定已更新。可在[[Special:Preferences|您的偏好設定]]查看所有遮蓋掉的使用者。",
        "specialmute-submit": "確認",
+       "specialmute-label-mute-email": "遮蓋來自此使用者的郵件",
+       "specialmute-header": "請選擇您對於{{BIDI:[[User:$1]]}}的遮蓋偏好設定。",
        "specialmute-error-invalid-user": "無法找到請求的使用者名稱。",
+       "specialmute-error-email-blacklist-disabled": "未啟用遮蓋掉使用者發送電子郵件給您的功能。",
+       "specialmute-error-email-preferences": "在您遮蓋使用者之前,您必須確認您的電子郵件地址。您可以在[[Special:Preferences|偏好設定]]進行。",
+       "specialmute-email-footer": "要管理{{BIDI:$2}}的電子郵件偏好設定,請查看<$1>。",
+       "specialmute-login-required": "請登入以變更您的遮蓋偏好設定。",
        "revid": "修訂 $1",
        "pageid": "頁面 ID $1",
        "interfaceadmin-info": "$1\n\n編輯全站 CSS/JS/JSON 檔案的權限,近期已從 <code>editinterface</code> 權限裡拆分。若您不清楚為何會收到此錯誤,請查看 [[mw:MediaWiki_1.32/interface-admin]]。",
index e1ea247..13f3cfa 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Maintenance
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -136,8 +138,8 @@ class CleanupSpam extends Maintenance {
                $rev = Revision::newFromTitle( $title );
                $currentRevId = $rev->getId();
 
-               while ( $rev && ( $rev->isDeleted( Revision::DELETED_TEXT )
-                       || LinkFilter::matchEntry( $rev->getContent( Revision::RAW ), $domain, $protocol ) )
+               while ( $rev && ( $rev->isDeleted( RevisionRecord::DELETED_TEXT )
+                       || LinkFilter::matchEntry( $rev->getContent( RevisionRecord::RAW ), $domain, $protocol ) )
                ) {
                        $rev = $rev->getPrevious();
                }
@@ -152,7 +154,7 @@ class CleanupSpam extends Maintenance {
                        $page = WikiPage::factory( $title );
                        if ( $rev ) {
                                // Revert to this revision
-                               $content = $rev->getContent( Revision::RAW );
+                               $content = $rev->getContent( RevisionRecord::RAW );
 
                                $this->output( "reverting\n" );
                                $page->doEditContent(
index 0ee1e6a..fe4905b 100644 (file)
@@ -99,12 +99,20 @@ try {
        $success = $maintenance->execute();
 } catch ( Exception $ex ) {
        $success = false;
+       $exReportMessage = '';
        while ( $ex ) {
                $cls = get_class( $ex );
-               print "$cls from line {$ex->getLine()} of {$ex->getFile()}: {$ex->getMessage()}\n";
-               print $ex->getTraceAsString() . "\n";
+               $exReportMessage .= "$cls from line {$ex->getLine()} of {$ex->getFile()}: {$ex->getMessage()}\n";
+               $exReportMessage .= $ex->getTraceAsString() . "\n";
                $ex = $ex->getPrevious();
        }
+       // Print the exception to stderr if possible, don't mix it in
+       // with stdout output.
+       if ( defined( 'STDERR' ) ) {
+               fwrite( STDERR, $exReportMessage );
+       } else {
+               echo $exReportMessage;
+       }
 }
 
 // Potentially debug globals
index cb4eddf..d3d76ac 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Maintenance
  */
 
+use MediaWiki\Revision\RevisionRecord;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -79,7 +81,7 @@ class FixDefaultJsonContentPages extends LoggedUpdateMaintenance {
                $title = Title::makeTitle( $row->page_namespace, $row->page_title );
                $this->output( "Processing {$title} ({$row->page_id})...\n" );
                $rev = Revision::newFromTitle( $title );
-               $content = $rev->getContent( Revision::RAW );
+               $content = $rev->getContent( RevisionRecord::RAW );
                $dbw = $this->getDB( DB_MASTER );
                if ( $content instanceof JsonContent ) {
                        if ( $content->isValid() ) {
index 2e8cf77..ca67c83 100644 (file)
@@ -23,6 +23,8 @@
  * @ingroup Maintenance
  */
 
+use MediaWiki\Storage\RevisionRecord;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -51,8 +53,8 @@ class GetTextMaint extends Maintenance {
                        $this->fatalError( "Page $titleText does not exist.\n" );
                }
                $content = $rev->getContent( $this->hasOption( 'show-private' )
-                       ? Revision::RAW
-                       : Revision::FOR_PUBLIC );
+                       ? RevisionRecord::RAW
+                       : RevisionRecord::FOR_PUBLIC );
 
                if ( $content === false ) {
                        $titleText = $title->getPrefixedText();
index 513edf3..9548d6b 100644 (file)
@@ -37,6 +37,7 @@ class McTest extends Maintenance {
                        . " memcached server and shows a report" );
                $this->addOption( 'i', 'Number of iterations', false, true );
                $this->addOption( 'cache', 'Use servers from this $wgObjectCaches store', false, true );
+               $this->addOption( 'driver', 'Either "php" or "pecl"', false, true );
                $this->addArg( 'server[:port]', 'Memcached server to test, with optional port', false );
        }
 
@@ -66,41 +67,177 @@ class McTest extends Maintenance {
                # find out the longest server string to nicely align output later on
                $maxSrvLen = $servers ? max( array_map( 'strlen', $servers ) ) : 0;
 
+               $type = $this->getOption( 'driver', 'php' );
+               if ( $type === 'php' ) {
+                       $class = MemcachedPhpBagOStuff::class;
+               } elseif ( $type === 'pecl' ) {
+                       $class = MemcachedPeclBagOStuff::class;
+               } else {
+                       $this->fatalError( "Invalid driver type '$type'" );
+               }
+
                foreach ( $servers as $server ) {
-                       $this->output(
-                               str_pad( $server, $maxSrvLen ),
-                               $server # output channel
-                       );
+                       $this->output( str_pad( $server, $maxSrvLen ) . "\n" );
 
-                       $mcc = new MemcachedClient( [
-                               'persistant' => true,
+                       /** @var BagOStuff $mcc */
+                       $mcc = new $class( [
+                               'servers' => [ $server ],
+                               'persistent' => true,
                                'timeout' => $wgMemCachedTimeout
                        ] );
-                       $mcc->set_servers( [ $server ] );
-                       $set = 0;
-                       $incr = 0;
-                       $get = 0;
-                       $time_start = microtime( true );
-                       for ( $i = 1; $i <= $iterations; $i++ ) {
-                               if ( $mcc->set( "test$i", $i ) ) {
-                                       $set++;
-                               }
+
+                       $this->benchmarkSingleKeyOps( $mcc, $iterations );
+                       $this->benchmarkMultiKeyOpsImmediateBlocking( $mcc, $iterations );
+                       $this->benchmarkMultiKeyOpsDeferredBlocking( $mcc, $iterations );
+               }
+       }
+
+       /**
+        * @param BagOStuff $mcc
+        * @param int $iterations
+        */
+       private function benchmarkSingleKeyOps( $mcc, $iterations ) {
+               $add = 0;
+               $set = 0;
+               $incr = 0;
+               $get = 0;
+               $delete = 0;
+
+               $keys = [];
+               for ( $i = 1; $i <= $iterations; $i++ ) {
+                       $keys[] = "test$i";
+               }
+
+               // Clear out any old values
+               $mcc->deleteMulti( $keys );
+
+               $time_start = microtime( true );
+               foreach ( $keys as $key ) {
+                       if ( $mcc->add( $key, $i ) ) {
+                               $add++;
                        }
-                       for ( $i = 1; $i <= $iterations; $i++ ) {
-                               if ( !is_null( $mcc->incr( "test$i", $i ) ) ) {
-                                       $incr++;
-                               }
+               }
+               $addMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               foreach ( $keys as $key ) {
+                       if ( $mcc->set( $key, $i ) ) {
+                               $set++;
+                       }
+               }
+               $setMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               foreach ( $keys as $key ) {
+                       if ( !is_null( $mcc->incr( $key, $i ) ) ) {
+                               $incr++;
                        }
-                       for ( $i = 1; $i <= $iterations; $i++ ) {
-                               $value = $mcc->get( "test$i" );
-                               if ( $value == $i * 2 ) {
-                                       $get++;
-                               }
+               }
+               $incrMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               foreach ( $keys as $key ) {
+                       $value = $mcc->get( $key );
+                       if ( $value == $i * 2 ) {
+                               $get++;
                        }
-                       $exectime = microtime( true ) - $time_start;
+               }
+               $getMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
 
-                       $this->output( " set: $set   incr: $incr   get: $get time: $exectime", $server );
+               $time_start = microtime( true );
+               foreach ( $keys as $key ) {
+                       if ( $mcc->delete( $key ) ) {
+                               $delete++;
+                       }
                }
+               $delMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $this->output(
+                       " add: $add/$iterations {$addMs}ms   " .
+                       "set: $set/$iterations {$setMs}ms   " .
+                       "incr: $incr/$iterations {$incrMs}ms   " .
+                       "get: $get/$iterations ({$getMs}ms)   " .
+                       "delete: $delete/$iterations ({$delMs}ms)\n"
+               );
+       }
+
+       /**
+        * @param BagOStuff $mcc
+        * @param int $iterations
+        */
+       private function benchmarkMultiKeyOpsImmediateBlocking( $mcc, $iterations ) {
+               $keysByValue = [];
+               for ( $i = 1; $i <= $iterations; $i++ ) {
+                       $keysByValue["test$i"] = 'S' . str_pad( $i, 2048 );
+               }
+               $keyList = array_keys( $keysByValue );
+
+               $time_start = microtime( true );
+               $mSetOk = $mcc->setMulti( $keysByValue ) ? 'S' : 'F';
+               $mSetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               $found = $mcc->getMulti( $keyList );
+               $mGetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+               $mGetOk = 0;
+               foreach ( $found as $key => $value ) {
+                       $mGetOk += ( $value === $keysByValue[$key] );
+               }
+
+               $time_start = microtime( true );
+               $mChangeTTLOk = $mcc->changeTTLMulti( $keyList, 3600 ) ? 'S' : 'F';
+               $mChangeTTTMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               $mDelOk = $mcc->deleteMulti( $keyList ) ? 'S' : 'F';
+               $mDelMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $this->output(
+                       " setMulti (IB): $mSetOk {$mSetMs}ms   " .
+                       "getMulti (IB): $mGetOk/$iterations {$mGetMs}ms   " .
+                       "changeTTLMulti (IB): $mChangeTTLOk {$mChangeTTTMs}ms   " .
+                       "deleteMulti (IB): $mDelOk {$mDelMs}ms\n"
+               );
+       }
+
+       /**
+        * @param BagOStuff $mcc
+        * @param int $iterations
+        */
+       private function benchmarkMultiKeyOpsDeferredBlocking( $mcc, $iterations ) {
+               $flags = $mcc::WRITE_BACKGROUND;
+               $keysByValue = [];
+               for ( $i = 1; $i <= $iterations; $i++ ) {
+                       $keysByValue["test$i"] = 'A' . str_pad( $i, 2048 );
+               }
+               $keyList = array_keys( $keysByValue );
+
+               $time_start = microtime( true );
+               $mSetOk = $mcc->setMulti( $keysByValue, 0, $flags ) ? 'S' : 'F';
+               $mSetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               $found = $mcc->getMulti( $keyList );
+               $mGetMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+               $mGetOk = 0;
+               foreach ( $found as $key => $value ) {
+                       $mGetOk += ( $value === $keysByValue[$key] );
+               }
+
+               $time_start = microtime( true );
+               $mChangeTTLOk = $mcc->changeTTLMulti( $keyList, 3600, $flags ) ? 'S' : 'F';
+               $mChangeTTTMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $time_start = microtime( true );
+               $mDelOk = $mcc->deleteMulti( $keyList, $flags ) ? 'S' : 'F';
+               $mDelMs = intval( 1e3 * ( microtime( true ) - $time_start ) );
+
+               $this->output(
+                       " setMulti (DB): $mSetOk {$mSetMs}ms   " .
+                       "getMulti (DB): $mGetOk/$iterations {$mGetMs}ms   " .
+                       "changeTTLMulti (DB): $mChangeTTLOk {$mChangeTTTMs}ms   " .
+                       "deleteMulti (DB): $mDelOk {$mDelMs}ms\n"
+               );
        }
 }
 
index 333b8b9..5024395 100644 (file)
@@ -28,6 +28,7 @@ require_once __DIR__ . '/Maintenance.php';
 
 use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IResultWrapper;
 use Wikimedia\Rdbms\IMaintainableDatabase;
 
@@ -558,8 +559,12 @@ class NamespaceDupes extends Maintenance {
         * @return bool
         */
        private function canMerge( $id, LinkTarget $linkTarget, &$logStatus ) {
-               $latestDest = Revision::newFromTitle( $linkTarget, 0, Revision::READ_LATEST );
-               $latestSource = Revision::newFromPageId( $id, 0, Revision::READ_LATEST );
+               $latestDest = Revision::newFromTitle(
+                       $linkTarget, 0, RevisionRecord::READ_LATEST
+               );
+               $latestSource = Revision::newFromPageId(
+                       $id, 0, RevisionRecord::READ_LATEST
+               );
                if ( $latestSource->getTimestamp() > $latestDest->getTimestamp() ) {
                        $logStatus = 'cannot merge since source is later';
                        return false;
index dcb89d1..e48b6ab 100644 (file)
@@ -21,6 +21,7 @@
  * @ingroup Maintenance
  */
 
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IDatabase;
 
 require_once __DIR__ . '/Maintenance.php';
@@ -144,7 +145,7 @@ class PopulateRevisionLength extends LoggedUpdateMaintenance {
                        ? Revision::newFromArchiveRow( $row )
                        : new Revision( $row );
 
-               $content = $rev->getContent( Revision::RAW );
+               $content = $rev->getContent( RevisionRecord::RAW );
                if ( !$content ) {
                        # This should not happen, but sometimes does (T22757)
                        $id = $row->$idCol;
index a62e019..b9e084e 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 
 require_once __DIR__ . '/dumpIterator.php';
 
@@ -81,7 +82,7 @@ class PreprocessDump extends DumpIterator {
         * @param Revision $rev
         */
        public function processRevision( $rev ) {
-               $content = $rev->getContent( Revision::RAW );
+               $content = $rev->getContent( RevisionRecord::RAW );
 
                if ( $content->getModel() !== CONTENT_MODEL_WIKITEXT ) {
                        return;
index 8306243..3f48abb 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Storage\RevisionRecord;
 use Wikimedia\Rdbms\IDatabase;
 
 require_once __DIR__ . '/Maintenance.php';
@@ -231,7 +232,7 @@ class RefreshLinks extends Maintenance {
                }
 
                $rt = null;
-               $content = $page->getContent( Revision::RAW );
+               $content = $page->getContent( RevisionRecord::RAW );
                if ( $content !== null ) {
                        $rt = $content->getUltimateRedirectTarget();
                }
index c2fa687..d8a8808 100644 (file)
@@ -22,6 +22,7 @@
  */
 
 use MediaWiki\MediaWikiServices;
+use MediaWiki\Revision\RevisionRecord;
 use MediaWiki\Shell\Shell;
 
 if ( !defined( 'MEDIAWIKI' ) ) {
@@ -523,7 +524,7 @@ class CheckStorage {
 
        function importRevision( &$revision, &$importer ) {
                $id = $revision->getID();
-               $content = $revision->getContent( Revision::RAW );
+               $content = $revision->getContent( RevisionRecord::RAW );
                $id = $id ?: '';
 
                if ( $content === null ) {
index f759c13..5f7f9d5 100644 (file)
@@ -45,7 +45,6 @@ class UserDupes {
        private $outputCallback;
 
        /**
-        * UserDupes constructor.
         * @param IMaintainableDatabase &$database
         * @param callback $outputCallback
         */
index 671369a..8683eed 100644 (file)
@@ -21,6 +21,8 @@
  * @ingroup Maintenance
  */
 
+use MediaWiki\Revision\RevisionRecord;
+
 require_once __DIR__ . '/Maintenance.php';
 
 /**
@@ -43,7 +45,7 @@ class ViewCLI extends Maintenance {
 
                $page = WikiPage::factory( $title );
 
-               $content = $page->getContent( Revision::RAW );
+               $content = $page->getContent( RevisionRecord::RAW );
                if ( !$content ) {
                        $this->fatalError( "Page has no content" );
                }
index d4dc663..0c2df6b 100644 (file)
@@ -1526,7 +1526,9 @@ return [
                'remoteBasePath' => "$wgResourceBasePath/resources/src/mediawiki.jqueryMsg",
                'packageFiles' => [
                        'mediawiki.jqueryMsg.js',
-                       [ 'name' => 'parserDefaults.json', 'callback' => function ( ResourceLoaderContext $context ) {
+                       [ 'name' => 'parserDefaults.json', 'callback' => function (
+                               ResourceLoaderContext $context, Config $config
+                       ) {
                                $tagData = Sanitizer::getRecognizedTagData();
                                $allowedHtmlElements = array_merge(
                                        array_keys( $tagData['htmlpairs'] ),
@@ -1537,7 +1539,7 @@ return [
                                );
 
                                $magicWords = [
-                                       'SITENAME' => $context->getConfig()->get( 'Sitename' ),
+                                       'SITENAME' => $config->get( 'Sitename' ),
                                ];
                                Hooks::run( 'ResourceLoaderJqueryMsgModuleMagicWords', [ $context, &$magicWords ] );
 
index 0613023..1772152 100644 (file)
                }
        };
 
-       /**
-        * @static
-        * @property {Array}
-        * Very incomplete and outdated list of errors we might receive from the API. Do not use.
-        * @deprecated since 1.29
-        */
-       mw.Api.errors = [
-               // occurs when POST aborted
-               // jQuery 1.4 can't distinguish abort or lost connection from 200 OK + empty result
-               'ok-but-empty',
-
-               // timeout
-               'timeout',
-
-               // really a warning, but we treat it like an error
-               'duplicate',
-               'duplicate-archive',
-
-               // upload succeeded, but no image info.
-               // this is probably impossible, but might as well check for it
-               'noimageinfo',
-               // remote errors, defined in API
-               'uploaddisabled',
-               'nomodule',
-               'mustbeposted',
-               'badaccess-groups',
-               'missingresult',
-               'missingparam',
-               'invalid-file-key',
-               'copyuploaddisabled',
-               'mustbeloggedin',
-               'empty-file',
-               'file-too-large',
-               'filetype-missing',
-               'filetype-banned',
-               'filetype-banned-type',
-               'filename-tooshort',
-               'illegal-filename',
-               'verification-error',
-               'hookaborted',
-               'unknown-error',
-               'internal-error',
-               'overwrite',
-               'badtoken',
-               'fetchfileerror',
-               'fileexists-shared-forbidden',
-               'invalidtitle',
-               'notloggedin',
-               'autoblocked',
-               'blocked',
-
-               // Stash-specific errors - expanded
-               'stashfailed',
-               'stasherror',
-               'stashedfilenotfound',
-               'stashpathinvalid',
-               'stashfilestorage',
-               'stashzerolength',
-               'stashnotloggedin',
-               'stashwrongowner',
-               'stashnosuchfilekey'
-       ];
-       mw.log.deprecate( mw.Api, 'errors', mw.Api.errors, null, 'mw.Api.errors' );
-
-       /**
-        * @static
-        * @property {Array}
-        * Very incomplete and outdated list of warnings we might receive from the API. Do not use.
-        * @deprecated since 1.29
-        */
-       mw.Api.warnings = [
-               'duplicate',
-               'exists'
-       ];
-       mw.log.deprecate( mw.Api, 'warnings', mw.Api.warnings, null, 'mw.Api.warnings' );
-
 }() );
index 6d003aa..d1bada0 100644 (file)
                        pageRestrictionsWidget, namespaceRestrictionsWidget, createAccountWidget, data,
                        enablePartialBlocks, blockAllowsUTEdit, userChangedCreateAccount, updatingBlockOptions;
 
+               function preserveSelectedStateOnDisable( widget ) {
+                       var widgetWasSelected;
+
+                       if ( !widget ) {
+                               return;
+                       }
+
+                       // 'disable' event fires if disabled state changes
+                       widget.on( 'disable', function ( disabled ) {
+                               if ( disabled ) {
+                                       // Disabling an enabled widget
+                                       // Save selected and set selected to false
+                                       widgetWasSelected = widget.isSelected();
+                                       widget.setSelected( false );
+                               } else {
+                                       // Enabling a disabled widget
+                                       // Set selected to the saved value
+                                       if ( widgetWasSelected !== undefined ) {
+                                               widget.setSelected( widgetWasSelected );
+                                       }
+                                       widgetWasSelected = undefined;
+                               }
+                       } );
+               }
+
                function updateBlockOptions() {
                        var blocktarget = blockTargetWidget.getValue().trim(),
                                isEmpty = blocktarget === '',
                                isSitewide = editingIsSelected && editingRestrictionValue === 'sitewide';
 
                        enableAutoblockWidget.setDisabled( isNonEmptyIp );
-                       if ( enableAutoblockWidget.isDisabled() ) {
-                               enableAutoblockWidget.setSelected( false );
-                       }
 
                        anonOnlyWidget.setDisabled( !isIp && !isEmpty );
-                       if ( anonOnlyWidget.isDisabled() ) {
-                               anonOnlyWidget.setSelected( false );
-                       }
 
                        if ( hideUserWidget ) {
                                hideUserWidget.setDisabled( isNonEmptyIp || !isIndefinite || !isSitewide );
-                               if ( hideUserWidget.isDisabled() ) {
-                                       hideUserWidget.setSelected( false );
-                               }
                        }
 
                        if ( watchUserWidget ) {
                                watchUserWidget.setDisabled( isIpRange && !isEmpty );
-                               if ( watchUserWidget.isDisabled() ) {
-                                       watchUserWidget.setSelected( false );
-                               }
                        }
 
                        if ( enablePartialBlocks ) {
                                preventTalkPageEditWidget = infuseIfExists( $( '#mw-input-wpDisableUTEdit' ) );
                        }
 
+                       // When disabling checkboxes, preserve their selected state in case they are re-enabled
+                       preserveSelectedStateOnDisable( enableAutoblockWidget );
+                       preserveSelectedStateOnDisable( anonOnlyWidget );
+                       preserveSelectedStateOnDisable( watchUserWidget );
+                       preserveSelectedStateOnDisable( hideUserWidget );
+                       preserveSelectedStateOnDisable( preventTalkPageEditWidget );
+
                        updateBlockOptions();
                }
        } );
index f29b0d7..ba85027 100644 (file)
@@ -25,6 +25,7 @@
  * @file
  * @ingroup Testing
  */
+
 use Wikimedia\Rdbms\IDatabase;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Tidy\TidyDriverBase;
@@ -129,6 +130,9 @@ class ParserTestRunner {
         */
        private $keepUploads;
 
+       /** @var Title */
+       private $defaultTitle;
+
        /**
         * @param TestRecorder $recorder
         * @param array $options
@@ -165,6 +169,8 @@ class ParserTestRunner {
                if ( isset( $options['upload-dir'] ) ) {
                        $this->uploadDir = $options['upload-dir'];
                }
+
+               $this->defaultTitle = Title::newFromText( 'Parser test' );
        }
 
        /**
@@ -839,10 +845,43 @@ class ParserTestRunner {
                        $options->setTidy( true );
                }
 
-               if ( isset( $opts['title'] ) ) {
-                       $titleText = $opts['title'];
-               } else {
-                       $titleText = 'Parser test';
+               $revId = 1337; // see Parser::getRevisionId()
+               $title = isset( $opts['title'] )
+                       ? Title::newFromText( $opts['title'] )
+                       : $this->defaultTitle;
+
+               if ( isset( $opts['lastsavedrevision'] ) ) {
+                       $content = new WikitextContent( $test['input'] );
+                       $title = Title::newFromRow( (object)[
+                               'page_id' => 187,
+                               'page_len' => $content->getSize(),
+                               'page_latest' => 1337,
+                               'page_namespace' => $title->getNamespace(),
+                               'page_title' => $title->getDBkey(),
+                               'page_is_redirect' => 0
+                       ] );
+                       $rev = new Revision(
+                               [
+                                       'id' => $title->getLatestRevID(),
+                                       'page' => $title->getArticleID(),
+                                       'user' => $user,
+                                       'content' => $content,
+                                       'timestamp' => $this->getFakeTimestamp(),
+                                       'title' => $title
+                               ],
+                               Revision::READ_LATEST,
+                               $title
+                       );
+                       $oldCallback = $options->getCurrentRevisionCallback();
+                       $options->setCurrentRevisionCallback(
+                               function ( Title $t, $parser ) use ( $title, $rev, $oldCallback ) {
+                                       if ( $t->equals( $title ) ) {
+                                               return $rev;
+                                       } else {
+                                               return call_user_func( $oldCallback, $t, $parser );
+                                       }
+                               }
+                       );
                }
 
                if ( isset( $opts['maxincludesize'] ) ) {
@@ -855,7 +894,6 @@ class ParserTestRunner {
                $local = isset( $opts['local'] );
                $preprocessor = $opts['preprocessor'] ?? null;
                $parser = $this->getParser( $preprocessor );
-               $title = Title::newFromText( $titleText );
 
                if ( isset( $opts['styletag'] ) ) {
                        // For testing the behavior of <style> (including those deduplicated
@@ -887,7 +925,7 @@ class ParserTestRunner {
                } elseif ( isset( $opts['preload'] ) ) {
                        $out = $parser->getPreloadText( $test['input'], $title, $options );
                } else {
-                       $output = $parser->parse( $test['input'], $title, $options, true, true, 1337 );
+                       $output = $parser->parse( $test['input'], $title, $options, true, true, $revId );
                        $out = $output->getText( [
                                'allowTOC' => !isset( $opts['notoc'] ),
                                'unwrap' => !isset( $opts['wrap'] ),
index 0facec2..7046a7f 100644 (file)
@@ -10813,8 +10813,9 @@ parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
 !! end
 
 !! test
-Magic Word: {{REVISIONID}}
+Magic Word: {{REVISIONID}} on latest revision
 !! options
+lastsavedrevision
 parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
 showflags
 !! wikitext
@@ -10825,6 +10826,156 @@ showflags
 flags=vary-revision-id
 !! end
 
+!! test
+Magic Word: {{REVISIONID}} on non-latest revision
+!! options
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONID}}
+!! html/*
+<p>1337
+</p>
+flags=vary-revision-id
+!! end
+
+!! test
+Magic Word: {{REVISIONTIMESTAMP}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONTIMESTAMP}}
+!! html/*
+<p>19700101000203
+</p>
+flags=
+!! end
+
+!! test
+Magic Word: {{REVISIONTIMESTAMP}} on non-existing page
+!! options
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONTIMESTAMP}}
+!! html/*
+<p>123
+</p>
+flags=vary-revision-timestamp
+!! end
+
+!! test
+Magic Word: {{REVISIONUSER}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONUSER}}
+!! html/*
+<p>127.0.0.1
+</p>
+flags=vary-user
+!! end
+
+!! test
+Parser Function: {{REVISIONID:{{PAGENAME}}}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONID:{{PAGENAME}}}}
+!! html/*
+<p>1337
+</p>
+flags=vary-revision-id
+!! end
+
+!! test
+Parser Function: {{REVISIONID:{{PAGENAME}}}} on non-saved revision
+!! options
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONID:{{PAGENAME}}}}
+!! html/*
+
+flags=vary-revision-id
+!! end
+
+!! test
+Parser Function: {{REVISIONTIMESTAMP:{{PAGENAME}}}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONTIMESTAMP:{{PAGENAME}}}}
+!! html/*
+<p>19700101000203
+</p>
+flags=vary-revision-timestamp
+!! end
+
+!! test
+Parser Function: {{REVISIONDAY:{{PAGENAME}}}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONDAY:{{PAGENAME}}}}
+!! html/*
+<p>1
+</p>
+flags=vary-revision-timestamp
+!! end
+
+!! test
+Parser Function: {{REVISIONMONTH:{{PAGENAME}}}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONMONTH:{{PAGENAME}}}}
+!! html/*
+<p>01
+</p>
+flags=vary-revision-timestamp
+!! end
+
+!! test
+Parser Function: {{REVISIONYEAR:{{PAGENAME}}}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{REVISIONYEAR:{{PAGENAME}}}}
+!! html/*
+<p>1970
+</p>
+flags=vary-revision-timestamp
+!! end
+
+!! test
+Parser Function: {{PAGESIZE:{{PAGENAME}}}} on latest revision
+!! options
+lastsavedrevision
+parsoid={ "modes": ["wt2html","wt2wt"], "normalizePhp": true }
+showflags
+!! wikitext
+{{PAGESIZE:{{PAGENAME}}}}
+!! html/*
+<p>25
+</p>
+flags=vary-revision-sha1
+!! end
+
 !! test
 Magic Word: {{SCRIPTPATH}}
 !! options
index 43a333c..5f7746b 100644 (file)
@@ -36,7 +36,8 @@ abstract class MediaWikiUnitTestCase extends TestCase {
        protected function setUp() {
                parent::setUp();
                $reflection = new ReflectionClass( $this );
-               if ( strpos( $reflection->getFilename(), '/unit/' ) === false ) {
+               $dirSeparator = DIRECTORY_SEPARATOR;
+               if ( strpos( $reflection->getFilename(), "${dirSeparator}unit${dirSeparator}" ) === false ) {
                        $this->fail( 'This unit test needs to be in "tests/phpunit/unit" !' );
                }
                $this->unitGlobals = $GLOBALS;
index f555812..4a0857f 100644 (file)
@@ -1,12 +1,12 @@
 == MediaWiki PHPUnit Tests ==
 
-The unit tests for MediaWiki are implemented using the PHPUnit testing
+The unit and integration tests for MediaWiki are implemented using the PHPUnit testing
 framework and require PHPUnit to run.
 
 
 === WARNING ===
 
-Some of the unit tests are DESTRUCTIVE and WILL ALTER YOUR WIKI'S CONTENTS.
+Some of the integration tests are DESTRUCTIVE and WILL ALTER YOUR WIKI'S CONTENTS.
 
 DO NOT RUN THESE TESTS ON A PRODUCTION SYSTEM OR ON ANY SYSTEM WHERE YOU NEED
 TO RETAIN YOUR DATA.
diff --git a/tests/phpunit/TODO b/tests/phpunit/TODO
deleted file mode 100644 (file)
index cd9b9e2..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-== Things To Do ==
-
-* Most of the tests are named poorly;
-  naming should describe a use case in story-like language,
-  not simply identify the unit under test.
-  An example would be the difference between "testCalculate"
-  and "testAddingIntegersTogetherWorks".
-
-* Many of the tests make multiple assertions, and are thus not unitary tests.
-  By using data-providers and more use-case oriented test selection
-  nearly all of these cases can be easily resolved.
-
-* Some of the test files are either incorrectly named or in the wrong folder.
-  Tests should be organized in a mirrored structure to the source they are testing,
-  and named the same, with the exception of the word "Test" at the end.
-
-* Shared set-up code or base classes are present,
-  but usually named improperly or appear to be poorly factored.
-  Support code should share as much of the same naming  as the code it's supporting,
-  and test and test-case depenencies should be considered to resolve other shared needs.
index 1210a50..660734e 100644 (file)
@@ -716,6 +716,7 @@ class GlobalTest extends MediaWikiTestCase {
         */
        public function testWfGlobalCacheKey() {
                $cache = ObjectCache::getLocalClusterInstance();
+               $this->hideDeprecated( 'wfGlobalCacheKey' );
                $this->assertEquals(
                        $cache->makeGlobalKey( 'foo', 123, 'bar' ),
                        wfGlobalCacheKey( 'foo', 123, 'bar' )
index 03b35b5..8a98217 100644 (file)
@@ -3,21 +3,25 @@
 namespace MediaWiki\Tests\Permissions;
 
 use Action;
+use ContentHandler;
 use FauxRequest;
-use MediaWiki\Session\SessionId;
-use MediaWiki\Session\TestUtils;
-use MediaWikiLangTestCase;
-use RequestContext;
-use stdClass;
-use Title;
-use User;
 use MediaWiki\Block\DatabaseBlock;
 use MediaWiki\Block\Restriction\NamespaceRestriction;
 use MediaWiki\Block\Restriction\PageRestriction;
 use MediaWiki\Block\SystemBlock;
+use MediaWiki\Linker\LinkTarget;
 use MediaWiki\MediaWikiServices;
 use MediaWiki\Permissions\PermissionManager;
+use MediaWiki\Revision\MutableRevisionRecord;
+use MediaWiki\Revision\RevisionLookup;
 use Wikimedia\ScopedCallback;
+use MediaWiki\Session\SessionId;
+use MediaWiki\Session\TestUtils;
+use MediaWikiLangTestCase;
+use RequestContext;
+use stdClass;
+use Title;
+use User;
 use Wikimedia\TestingAccessWrapper;
 
 /**
@@ -698,6 +702,64 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
                );
        }
 
+       public function testJsConfigRedirectEditPermissions() {
+               $revision = null;
+               $user = $this->getTestUser()->getUser();
+               $otherUser = $this->getTestUser( 'sysop' )->getUser();
+               $localJsTitle = Title::newFromText( 'User:' . $user->getName() . '/foo.js' );
+               $otherLocalJsTitle = Title::newFromText( 'User:' . $user->getName() . '/foo2.js' );
+               $nonlocalJsTitle = Title::newFromText( 'User:' . $otherUser->getName() . '/foo.js' );
+
+               $services = MediaWikiServices::getInstance();
+               $revisionLookup = $this->getMockBuilder( RevisionLookup::class )
+                       ->setMethods( [ 'getRevisionByTitle' ] )
+                       ->getMockForAbstractClass();
+               $revisionLookup->method( 'getRevisionByTitle' )
+                       ->willReturnCallback( function ( LinkTarget $page ) use (
+                               $services, &$revision, $localJsTitle
+                       ) {
+                               if ( $localJsTitle->equals( Title::newFromLinkTarget( $page ) ) ) {
+                                       return $revision;
+                               } else {
+                                       return $services->getRevisionLookup()->getRevisionByTitle( $page );
+                               }
+                       } );
+               $permissionManager = new PermissionManager(
+                       $services->getSpecialPageFactory(),
+                       $revisionLookup,
+                       [],
+                       [],
+                       false,
+                       false,
+                       [],
+                       [],
+                       [],
+                       MediaWikiServices::getInstance()->getNamespaceInfo()
+               );
+               $this->setService( 'PermissionManager', $permissionManager );
+
+               $permissionManager->overrideUserRightsForTesting( $user, [ 'edit', 'editmyuserjs' ] );
+
+               $revision = $this->getJavascriptRevision( $localJsTitle, $user, '/* script */' );
+               $errors = $permissionManager->getPermissionErrors( 'edit', $user, $localJsTitle );
+               $this->assertSame( [], $errors );
+
+               $revision = $this->getJavascriptRedirectRevision( $localJsTitle, $otherLocalJsTitle, $user );
+               $errors = $permissionManager->getPermissionErrors( 'edit', $user, $localJsTitle );
+               $this->assertSame( [], $errors );
+
+               $revision = $this->getJavascriptRedirectRevision( $localJsTitle, $nonlocalJsTitle, $user );
+               $errors = $permissionManager->getPermissionErrors( 'edit', $user, $localJsTitle );
+               $this->assertSame( [ [ 'mycustomjsredirectprotected', 'edit' ] ], $errors );
+
+               $permissionManager->overrideUserRightsForTesting( $user,
+                       [ 'edit', 'editmyuserjs', 'editmyuserjsredirect' ] );
+
+               $revision = $this->getJavascriptRedirectRevision( $localJsTitle, $nonlocalJsTitle, $user );
+               $errors = $permissionManager->getPermissionErrors( 'edit', $user, $localJsTitle );
+               $this->assertSame( [], $errors );
+       }
+
        /**
         * @todo This test method should be split up into separate test methods and
         * data providers
@@ -1651,9 +1713,8 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
 
        /**
         * @covers \MediaWiki\Permissions\PermissionManager::addTemporaryUserRights
-        * @covers \MediaWiki\Permissions\PermissionManager::revokeTemporaryUserRights
         */
-       public function testTemporaryUserRights() {
+       public function testAddTemporaryUserRights() {
                $permissionManager = MediaWikiServices::getInstance()->getPermissionManager();
                $this->overrideUserPermissions( $this->user, [ 'read', 'edit' ] );
                // sanity checks
@@ -1684,4 +1745,35 @@ class PermissionManagerTest extends MediaWikiLangTestCase {
                $this->assertFalse( $permissionManager->userHasRight( $this->user, 'move' ) );
        }
 
+       /**
+        * Create a RevisionRecord with a single Javascript main slot.
+        * @param Title $title
+        * @param User $user
+        * @param string $text
+        * @return MutableRevisionRecord
+        */
+       private function getJavascriptRevision( Title $title, User $user, $text ) {
+               $content = ContentHandler::makeContent( $text, $title, CONTENT_MODEL_JAVASCRIPT );
+               $revision = new MutableRevisionRecord( $title );
+               $revision->setContent( 'main', $content );
+               return $revision;
+       }
+
+       /**
+        * Create a RevisionRecord with a single Javascript redirect main slot.
+        * @param Title $title
+        * @param Title $redirectTargetTitle
+        * @param User $user
+        * @return MutableRevisionRecord
+        */
+       private function getJavascriptRedirectRevision(
+               Title $title, Title $redirectTargetTitle, User $user
+       ) {
+               $content = ContentHandler::getForModelID( CONTENT_MODEL_JAVASCRIPT )
+                       ->makeRedirectContent( $redirectTargetTitle );
+               $revision = new MutableRevisionRecord( $title );
+               $revision->setContent( 'main', $content );
+               return $revision;
+       }
+
 }
index 2d141e6..83e8d85 100644 (file)
@@ -1468,7 +1468,7 @@ abstract class RevisionDbTestBase extends MediaWikiTestCase {
                        $rev->getPage(),
                        $rev->getId()
                );
-               $cache->delete( $key, WANObjectCache::HOLDOFF_NONE );
+               $cache->delete( $key, WANObjectCache::HOLDOFF_TTL_NONE );
                $this->assertFalse( $cache->get( $key ) );
 
                ++$now;
diff --git a/tests/phpunit/includes/api/ApiCSPReportTest.php b/tests/phpunit/includes/api/ApiCSPReportTest.php
new file mode 100644 (file)
index 0000000..b3e0543
--- /dev/null
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @group API
+ * @group medium
+ * @covers ApiCSPReport
+ */
+class ApiCSPReportTest extends MediaWikiIntegrationTestCase {
+
+       public function setUp() {
+               parent::setUp();
+               $this->setMwGlobals( [
+                       'CSPFalsePositiveUrls' => [],
+               ] );
+       }
+
+       public function testInternalReportonly() {
+               $params = [
+                       'reportonly' => '1',
+                       'source' => 'internal',
+               ];
+               $cspReport = [
+                       'document-uri' => 'https://doc.test/path',
+                       'referrer' => 'https://referrer.test/path',
+                       'violated-directive' => 'connet-src',
+                       'disposition' => 'report',
+                       'blocked-uri' => 'https://blocked.test/path?query',
+                       'line-number' => 4,
+                       'column-number' => 2,
+                       'source-file' => 'https://source.test/path?query',
+               ];
+
+               $log = $this->doExecute( $params, $cspReport );
+
+               $this->assertEquals(
+                       [
+                               [
+                                       '[report-only] Received CSP report: ' .
+                                               '<https://blocked.test> blocked from being loaded on <https://doc.test/path>:4',
+                                       [
+                                               'method' => 'ApiCSPReport::execute',
+                                               'user_id' => 'logged-out',
+                                               'user-agent' => 'Test/0.0',
+                                               'source' => 'internal'
+                                       ]
+                               ],
+                       ],
+                       $log,
+                       'logged messages'
+               );
+       }
+
+       public function testFalsePositiveOriginMatch() {
+               $params = [
+                       'reportonly' => '1',
+                       'source' => 'internal',
+               ];
+               $cspReport = [
+                       'document-uri' => 'https://doc.test/path',
+                       'referrer' => 'https://referrer.test/path',
+                       'violated-directive' => 'connet-src',
+                       'disposition' => 'report',
+                       'blocked-uri' => 'https://blocked.test/path/file?query',
+                       'line-number' => 4,
+                       'column-number' => 2,
+                       'source-file' => 'https://source.test/path/file?query',
+               ];
+
+               $this->setMwGlobals( [
+                       'wgCSPFalsePositiveUrls' => [
+                               'https://blocked.test/path/' => true,
+                       ],
+               ] );
+               $log = $this->doExecute( $params, $cspReport );
+
+               $this->assertSame(
+                       [],
+                       $log,
+                       'logged messages'
+               );
+       }
+
+       private function doExecute( array $params, array $cspReport ) {
+               $log = [];
+               $logger = $this->createMock( Psr\Log\AbstractLogger::class );
+               $logger->method( 'warning' )->will( $this->returnCallback(
+                       function ( $msg, $ctx ) use ( &$log ) {
+                               unset( $ctx['csp-report'] );
+                               $log[] = [ $msg, $ctx ];
+                       }
+               ) );
+               $this->setLogger( 'csp-report-only', $logger );
+
+               $postBody = json_encode( [ 'csp-report' => $cspReport ] );
+               $req = $this->getMockBuilder( FauxRequest::class )
+                       ->setMethods( [ 'getRawInput' ] )
+                       ->setConstructorArgs( [ $params, /* $wasPosted */ true ] )
+                       ->getMock();
+               $req->method( 'getRawInput' )->willReturn( $postBody );
+               $req->setHeaders( [
+                       'Content-Type' => 'application/csp-report',
+                       'User-Agent' => 'Test/0.0'
+               ] );
+
+               $api = $this->getMockBuilder( ApiCSPReport::class )
+                       ->disableOriginalConstructor()
+                       ->setMethods( [ 'getParameter', 'getRequest', 'getResult' ] )
+                       ->getMock();
+               $api->method( 'getParameter' )->will( $this->returnCallback(
+                       function ( $key ) use ( $req ) {
+                               return $req->getRawVal( $key );
+                       }
+               ) );
+               $api->method( 'getRequest' )->willReturn( $req );
+               $api->method( 'getResult' )->willReturn( new ApiResult( false ) );
+
+               $api->execute();
+               return $log;
+       }
+}
index fe3bb88..b8f60c4 100644 (file)
@@ -2,9 +2,11 @@
 
 use MediaWiki\Block\BlockManager;
 use MediaWiki\Block\DatabaseBlock;
+use MediaWiki\Block\CompositeBlock;
 use MediaWiki\Block\SystemBlock;
 use MediaWiki\Config\ServiceOptions;
 use MediaWiki\MediaWikiServices;
+use Wikimedia\TestingAccessWrapper;
 
 /**
  * @group Blocking
@@ -63,26 +65,23 @@ class BlockManagerTest extends MediaWikiTestCase {
         * @covers ::shouldApplyCookieBlock
         */
        public function testGetBlockFromCookieValue( $options, $expected ) {
-               $blockManager = $this->getBlockManager( [
-                       'wgCookieSetOnAutoblock' => true,
-                       'wgCookieSetOnIpBlock' => true,
-               ] );
+               $blockManager = TestingAccessWrapper::newFromObject(
+                       $this->getBlockManager( [
+                               'wgCookieSetOnAutoblock' => true,
+                               'wgCookieSetOnIpBlock' => true,
+                       ] )
+               );
 
                $block = new DatabaseBlock( array_merge( [
-                       'address' => $options[ 'target' ] ?: $this->user,
+                       'address' => $options['target'] ?: $this->user,
                        'by' => $this->sysopId,
-               ], $options[ 'blockOptions' ] ) );
+               ], $options['blockOptions'] ) );
                $block->insert();
 
-               $class = new ReflectionClass( BlockManager::class );
-               $method = $class->getMethod( 'getBlockFromCookieValue' );
-               $method->setAccessible( true );
-
-               $user = $options[ 'loggedIn' ] ? $this->user : new User();
+               $user = $options['loggedIn'] ? $this->user : new User();
                $user->getRequest()->setCookie( 'BlockID', $block->getCookieValue() );
 
-               $this->assertSame( $expected, (bool)$method->invoke(
-                       $blockManager,
+               $this->assertSame( $expected, (bool)$blockManager->getBlockFromCookieValue(
                        $user,
                        $user->getRequest()
                ) );
@@ -142,16 +141,14 @@ class BlockManagerTest extends MediaWikiTestCase {
         * @covers ::isLocallyBlockedProxy
         */
        public function testIsLocallyBlockedProxy( $proxyList, $expected ) {
-               $class = new ReflectionClass( BlockManager::class );
-               $method = $class->getMethod( 'isLocallyBlockedProxy' );
-               $method->setAccessible( true );
-
-               $blockManager = $this->getBlockManager( [
-                       'wgProxyList' => $proxyList
-               ] );
+               $blockManager = TestingAccessWrapper::newFromObject(
+                       $this->getBlockManager( [
+                               'wgProxyList' => $proxyList
+                       ] )
+               );
 
                $ip = '1.2.3.4';
-               $this->assertSame( $expected, $method->invoke( $blockManager, $ip ) );
+               $this->assertSame( $expected, $blockManager->isLocallyBlockedProxy( $ip ) );
        }
 
        public static function provideIsLocallyBlockedProxy() {
@@ -174,16 +171,14 @@ class BlockManagerTest extends MediaWikiTestCase {
                        'addresses in keys: ' . $proxy . ', please move them to values)'
                );
 
-               $class = new ReflectionClass( BlockManager::class );
-               $method = $class->getMethod( 'isLocallyBlockedProxy' );
-               $method->setAccessible( true );
-
-               $blockManager = $this->getBlockManager( [
-                       'wgProxyList' => [ $proxy => 'test' ]
-               ] );
+               $blockManager = TestingAccessWrapper::newFromObject(
+                       $this->getBlockManager( [
+                               'wgProxyList' => [ $proxy => 'test' ]
+                       ] )
+               );
 
                $ip = '1.2.3.4';
-               $this->assertSame( true, $method->invoke( $blockManager, $ip ) );
+               $this->assertTrue( $blockManager->isLocallyBlockedProxy( $ip ) );
        }
 
        /**
@@ -202,9 +197,7 @@ class BlockManagerTest extends MediaWikiTestCase {
                        ->setConstructorArgs( $this->getBlockManagerConstructorArgs( $blockManagerConfig ) )
                        ->setMethods( [ 'checkHost' ] )
                        ->getMock();
-
-               $blockManager->expects( $this->any() )
-                       ->method( 'checkHost' )
+               $blockManager->method( 'checkHost' )
                        ->will( $this->returnValueMap( [ [
                                $options['dnsblQuery'],
                                $options['dnsblResponse'],
@@ -306,89 +299,406 @@ class BlockManagerTest extends MediaWikiTestCase {
        public function testGetUniqueBlocks() {
                $blockId = 100;
 
-               $class = new ReflectionClass( BlockManager::class );
-               $method = $class->getMethod( 'getUniqueBlocks' );
-               $method->setAccessible( true );
-
-               $blockManager = $this->getBlockManager( [] );
+               $blockManager = TestingAccessWrapper::newFromObject( $this->getBlockManager( [] ) );
 
                $block = $this->getMockBuilder( DatabaseBlock::class )
                        ->setMethods( [ 'getId' ] )
                        ->getMock();
-               $block->expects( $this->any() )
-                       ->method( 'getId' )
+               $block->method( 'getId' )
                        ->willReturn( $blockId );
 
                $autoblock = $this->getMockBuilder( DatabaseBlock::class )
                        ->setMethods( [ 'getParentBlockId', 'getType' ] )
                        ->getMock();
-               $autoblock->expects( $this->any() )
-                       ->method( 'getParentBlockId' )
+               $autoblock->method( 'getParentBlockId' )
                        ->willReturn( $blockId );
-               $autoblock->expects( $this->any() )
-                       ->method( 'getType' )
+               $autoblock->method( 'getType' )
                        ->willReturn( DatabaseBlock::TYPE_AUTO );
 
                $blocks = [ $block, $block, $autoblock, new SystemBlock() ];
 
-               $this->assertSame( 2, count( $method->invoke( $blockManager, $blocks ) ) );
+               $this->assertSame( 2, count( $blockManager->getUniqueBlocks( $blocks ) ) );
        }
 
        /**
-        * @covers ::trackBlockWithCookie
         * @dataProvider provideTrackBlockWithCookie
-        * @param bool $expectCookieSet
-        * @param bool $hasCookie
-        * @param bool $isBlocked
+        * @covers ::trackBlockWithCookie
         */
-       public function testTrackBlockWithCookie( $expectCookieSet, $hasCookie, $isBlocked ) {
-               $blockID = 123;
+       public function testTrackBlockWithCookie( $options, $expectedVal ) {
                $this->setMwGlobals( 'wgCookiePrefix', '' );
 
                $request = new FauxRequest();
-               if ( $hasCookie ) {
+               if ( $options['cookieSet'] ) {
                        $request->setCookie( 'BlockID', 'the value does not matter' );
                }
 
-               if ( $isBlocked ) {
-                       $block = $this->getMockBuilder( DatabaseBlock::class )
-                               ->setMethods( [ 'getType', 'getId' ] )
-                               ->getMock();
-                       $block->method( 'getType' )
-                               ->willReturn( DatabaseBlock::TYPE_IP );
-                       $block->method( 'getId' )
-                               ->willReturn( $blockID );
-               } else {
-                       $block = null;
-               }
-
                $user = $this->getMockBuilder( User::class )
                        ->setMethods( [ 'getBlock', 'getRequest' ] )
                        ->getMock();
                $user->method( 'getBlock' )
-                       ->willReturn( $block );
+                       ->willReturn( $options['block'] );
                $user->method( 'getRequest' )
                        ->willReturn( $request );
-               /** @var User $user */
 
                // Although the block cookie is set via DeferredUpdates, in command line mode updates are
                // processed immediately
-               $blockManager = $this->getBlockManager( [] );
+               $blockManager = $this->getBlockManager( [
+                       'wgSecretKey' => '',
+                       'wgCookieSetOnIpBlock' => true,
+               ] );
                $blockManager->trackBlockWithCookie( $user );
 
                /** @var FauxResponse $response */
                $response = $request->response();
-               $this->assertCount( $expectCookieSet ? 1 : 0, $response->getCookies() );
-               $this->assertEquals( $expectCookieSet ? $blockID : null, $response->getCookie( 'BlockID' ) );
+               $this->assertCount( $expectedVal ? 1 : 0, $response->getCookies() );
+               $this->assertEquals( $expectedVal ?: null, $response->getCookie( 'BlockID' ) );
        }
 
        public function provideTrackBlockWithCookie() {
+               $blockId = 123;
+               return [
+                       'Block cookie is already set; there is a trackable block' => [
+                               [
+                                       'cookieSet' => true,
+                                       'block' => $this->getTrackableBlock( $blockId ),
+                               ],
+                               null,
+                       ],
+                       'Block cookie is already set; there is no block' => [
+                               [
+                                       'cookieSet' => true,
+                                       'block' => null,
+                               ],
+                               null,
+                       ],
+                       'Block cookie is not yet set; there is no block' => [
+                               [
+                                       'cookieSet' => false,
+                                       'block' => null,
+                               ],
+                               null,
+                       ],
+                       'Block cookie is not yet set; there is a trackable block' => [
+                               [
+                                       'cookieSet' => false,
+                                       'block' => $this->getTrackableBlock( $blockId ),
+                               ],
+                               $blockId,
+                       ],
+                       'Block cookie is not yet set; there is a composite block with a trackable block' => [
+                               [
+                                       'cookieSet' => false,
+                                       'block' => new CompositeBlock( [
+                                               'originalBlocks' => [
+                                                       new SystemBlock(),
+                                                       $this->getTrackableBlock( $blockId ),
+                                               ]
+                                       ] ),
+                               ],
+                               $blockId,
+                       ],
+                       'Block cookie is not yet set; there is a composite block but no trackable block' => [
+                               [
+                                       'cookieSet' => false,
+                                       'block' => new CompositeBlock( [
+                                               'originalBlocks' => [
+                                                       new SystemBlock(),
+                                                       new SystemBlock(),
+                                               ]
+                                       ] ),
+                               ],
+                               null,
+                       ],
+               ];
+       }
+
+       private function getTrackableBlock( $blockId ) {
+               $block = $this->getMockBuilder( DatabaseBlock::class )
+                       ->setMethods( [ 'getType', 'getId' ] )
+                       ->getMock();
+               $block->method( 'getType' )
+                       ->willReturn( DatabaseBlock::TYPE_IP );
+               $block->method( 'getId' )
+                       ->willReturn( $blockId );
+               return $block;
+       }
+
+       /**
+        * @dataProvider provideSetBlockCookie
+        * @covers ::setBlockCookie
+        */
+       public function testSetBlockCookie( $expiryDelta, $expectedExpiryDelta ) {
+               $this->setMwGlobals( [
+                       'wgCookiePrefix' => '',
+               ] );
+
+               $request = new FauxRequest();
+               $response = $request->response();
+
+               $blockManager = $this->getBlockManager( [
+                       'wgSecretKey' => '',
+                       'wgCookieSetOnIpBlock' => true,
+               ] );
+
+               $now = wfTimestamp();
+
+               $block = new DatabaseBlock( [
+                       'expiry' => $expiryDelta === '' ? '' : $now + $expiryDelta
+               ] );
+               $blockManager->setBlockCookie( $block, $response );
+               $cookies = $response->getCookies();
+
+               $this->assertEquals(
+                       $now + $expectedExpiryDelta,
+                       $cookies['BlockID']['expire'],
+                       '',
+                       60 // Allow actual to be up to 60 seconds later than expected
+               );
+       }
+
+       public static function provideSetBlockCookie() {
+               // Maximum length of a block cookie, defined in BlockManager::setBlockCookie
+               $maxExpiryDelta = ( 24 * 60 * 60 );
+
+               $longExpiryDelta = ( 48 * 60 * 60 );
+               $shortExpiryDelta = ( 12 * 60 * 60 );
+
+               return [
+                       'Block has indefinite expiry' => [
+                               '',
+                               $maxExpiryDelta,
+                       ],
+                       'Block expiry is later than maximum cookie block expiry' => [
+                               $longExpiryDelta,
+                               $maxExpiryDelta,
+                       ],
+                       'Block expiry is sooner than maximum cookie block expiry' => [
+                               $shortExpiryDelta,
+                               $shortExpiryDelta,
+                       ],
+               ];
+       }
+
+       /**
+        * @covers ::shouldTrackBlockWithCookie
+        */
+       public function testShouldTrackBlockWithCookieSystemBlock() {
+               $blockManager = TestingAccessWrapper::newFromObject( $this->getBlockManager( [] ) );
+               $this->assertFalse( $blockManager->shouldTrackBlockWithCookie(
+                       new SystemBlock(),
+                       true
+               ) );
+       }
+
+       /**
+        * @dataProvider provideShouldTrackBlockWithCookie
+        * @covers ::shouldTrackBlockWithCookie
+        */
+       public function testShouldTrackBlockWithCookie( $options, $expected ) {
+               $block = $this->getMockBuilder( DatabaseBlock::class )
+                       ->setMethods( [ 'getType', 'isAutoblocking' ] )
+                       ->getMock();
+               $block->method( 'getType' )
+                       ->willReturn( $options['type'] );
+               if ( isset( $options['autoblocking'] ) ) {
+                       $block->method( 'isAutoblocking' )
+                               ->willReturn( $options['autoblocking'] );
+               }
+
+               $blockManager = TestingAccessWrapper::newFromObject(
+                       $this->getBlockManager( $options['blockManagerConfig'] )
+               );
+
+               $this->assertSame(
+                       $expected,
+                       $blockManager->shouldTrackBlockWithCookie( $block, $options['isAnon'] )
+               );
+       }
+
+       public static function provideShouldTrackBlockWithCookie() {
                return [
-                       // $expectCookieSet, $hasCookie, $isBlocked
-                       [ false, false, false ],
-                       [ false, true, false ],
-                       [ true, false, true ],
-                       [ false, true, true ],
+                       'IP block, anonymous user, IP block cookies enabled' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_IP,
+                                       'isAnon' => true,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnIpBlock' => true ],
+                               ],
+                               true
+                       ],
+                       'IP range block, anonymous user, IP block cookies enabled' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_RANGE,
+                                       'isAnon' => true,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnIpBlock' => true ],
+                               ],
+                               true
+                       ],
+                       'IP block, anonymous user, IP block cookies disabled' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_IP,
+                                       'isAnon' => true,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnIpBlock' => false ],
+                               ],
+                               false
+                       ],
+                       'IP block, logged in user, IP block cookies enabled' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_IP,
+                                       'isAnon' => false,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnIpBlock' => true ],
+                               ],
+                               false
+                       ],
+                       'User block, anonymous, autoblock cookies enabled, block is autoblocking' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_USER,
+                                       'isAnon' => true,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnAutoblock' => true ],
+                                       'autoblocking' => true,
+                               ],
+                               false
+                       ],
+                       'User block, logged in, autoblock cookies enabled, block is autoblocking' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_USER,
+                                       'isAnon' => false,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnAutoblock' => true ],
+                                       'autoblocking' => true,
+                               ],
+                               true
+                       ],
+                       'User block, logged in, autoblock cookies disabled, block is autoblocking' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_USER,
+                                       'isAnon' => false,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnAutoblock' => false ],
+                                       'autoblocking' => true,
+                               ],
+                               false
+                       ],
+                       'User block, logged in, autoblock cookies enabled, block is not autoblocking' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_USER,
+                                       'isAnon' => false,
+                                       'blockManagerConfig' => [ 'wgCookieSetOnAutoblock' => true ],
+                                       'autoblocking' => false,
+                               ],
+                               false
+                       ],
+                       'Block type is autoblock' => [
+                               [
+                                       'type' => DatabaseBlock::TYPE_AUTO,
+                                       'isAnon' => true,
+                                       'blockManagerConfig' => [],
+                               ],
+                               false
+                       ]
                ];
        }
+
+       /**
+        * @covers ::clearBlockCookie
+        */
+       public function testClearBlockCookie() {
+               $this->setMwGlobals( [
+                       'wgCookiePrefix' => '',
+               ] );
+
+               $request = new FauxRequest();
+               $response = $request->response();
+               $response->setCookie( 'BlockID', '100' );
+               $this->assertSame( '100', $response->getCookie( 'BlockID' ) );
+
+               BlockManager::clearBlockCookie( $response );
+               $this->assertSame( '', $response->getCookie( 'BlockID' ) );
+       }
+
+       /**
+        * @dataProvider provideGetIdFromCookieValue
+        * @covers ::getIdFromCookieValue
+        */
+       public function testGetIdFromCookieValue( $options, $expected ) {
+               $blockManager = $this->getBlockManager( [
+                       'wgSecretKey' => $options['secretKey']
+               ] );
+               $this->assertEquals(
+                       $expected,
+                       $blockManager->getIdFromCookieValue( $options['cookieValue'] )
+               );
+       }
+
+       public static function provideGetIdFromCookieValue() {
+               $blockId = 100;
+               $secretKey = '123';
+               $hmac = MWCryptHash::hmac( $blockId, $secretKey, false );
+               return [
+                       'No secret key is set' => [
+                               [
+                                       'secretKey' => '',
+                                       'cookieValue' => $blockId,
+                                       'calculatedHmac' => MWCryptHash::hmac( $blockId, '', false ),
+                               ],
+                               $blockId,
+                       ],
+                       'Secret key is set and stored hmac is correct' => [
+                               [
+                                       'secretKey' => $secretKey,
+                                       'cookieValue' => $blockId . '!' . $hmac,
+                                       'calculatedHmac' => $hmac,
+                               ],
+                               $blockId,
+                       ],
+                       'Secret key is set and stored hmac is incorrect' => [
+                               [
+                                       'secretKey' => $secretKey,
+                                       'cookieValue' => $blockId . '!xyz',
+                                       'calculatedHmac' => $hmac,
+                               ],
+                               null,
+                       ],
+               ];
+       }
+
+       /**
+        * @dataProvider provideGetCookieValue
+        * @covers ::getCookieValue
+        */
+       public function testGetCookieValue( $options, $expected ) {
+               $blockManager = $this->getBlockManager( [
+                       'wgSecretKey' => $options['secretKey']
+               ] );
+
+               $block = $this->getMockBuilder( DatabaseBlock::class )
+                       ->setMethods( [ 'getId' ] )
+                       ->getMock();
+               $block->method( 'getId' )
+                       ->willReturn( $options['blockId'] );
+
+               $this->assertEquals(
+                       $expected,
+                       $blockManager->getCookieValue( $block )
+               );
+       }
+
+       public static function provideGetCookieValue() {
+               $blockId = 100;
+               return [
+                       'Secret key not set' => [
+                               [
+                                       'secretKey' => '',
+                                       'blockId' => $blockId,
+                                       'hmac' => MWCryptHash::hmac( $blockId, '', false ),
+                               ],
+                               $blockId,
+                       ],
+                       'Secret key set' => [
+                               [
+                                       'secretKey' => '123',
+                                       'blockId' => $blockId,
+                                       'hmac' => MWCryptHash::hmac( $blockId, '123', false ),
+                               ],
+                               $blockId . '!' . MWCryptHash::hmac( $blockId, '123', false ) ],
+               ];
+       }
+
 }
index 2fa662b..35dacac 100644 (file)
@@ -204,7 +204,7 @@ class MessageCacheTest extends MediaWikiLangTestCase {
                ];
        }
 
-       public function testNoDBAccess() {
+       public function testNoDBAccessContentLanguage() {
                global $wgContLanguageCode;
 
                $dbr = wfGetDB( DB_REPLICA );
@@ -218,7 +218,22 @@ class MessageCacheTest extends MediaWikiLangTestCase {
 
                $dbr->restoreFlags();
 
-               $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries" );
+               $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries (content language)" );
+       }
+
+       public function testNoDBAccessNonContentLanguage() {
+               $dbr = wfGetDB( DB_REPLICA );
+
+               MessageCache::singleton()->getMsgFromNamespace( 'allpages/nl', 'nl' );
+
+               $this->assertEquals( 0, $dbr->trxLevel() );
+               $dbr->setFlag( DBO_TRX, $dbr::REMEMBER_PRIOR ); // make queries trigger TRX
+
+               MessageCache::singleton()->getMsgFromNamespace( 'go/nl', 'nl' );
+
+               $dbr->restoreFlags();
+
+               $this->assertEquals( 0, $dbr->trxLevel(), "No DB read queries (non-content language)" );
        }
 
        /**
index 9884987..522af43 100644 (file)
@@ -6,6 +6,7 @@ use Wikimedia\TestingAccessWrapper;
 /**
  * @author Matthias Mullie <mmullie@wikimedia.org>
  * @group BagOStuff
+ * @covers BagOStuff
  */
 class BagOStuffTest extends MediaWikiTestCase {
        /** @var BagOStuff */
@@ -31,8 +32,8 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::makeGlobalKey
-        * @covers BagOStuff::makeKeyInternal
+        * @covers MediumSpecificBagOStuff::makeGlobalKey
+        * @covers MediumSpecificBagOStuff::makeKeyInternal
         */
        public function testMakeKey() {
                $cache = ObjectCache::newFromId( 'hash' );
@@ -65,8 +66,8 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::merge
-        * @covers BagOStuff::mergeViaCas
+        * @covers MediumSpecificBagOStuff::merge
+        * @covers MediumSpecificBagOStuff::mergeViaCas
         */
        public function testMerge() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -109,7 +110,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::changeTTL
+        * @covers MediumSpecificBagOStuff::changeTTL
         */
        public function testChangeTTL() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -130,7 +131,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::changeTTLMulti
+        * @covers MediumSpecificBagOStuff::changeTTLMulti
         */
        public function testChangeTTLMulti() {
                $key1 = $this->cache->makeKey( 'test-key1' );
@@ -150,11 +151,14 @@ class BagOStuffTest extends MediaWikiTestCase {
                $this->assertFalse( $this->cache->get( $key2 ) );
                $this->assertFalse( $this->cache->get( $key3 ) );
 
-               $this->cache->setMulti( [
-                       $key1 => 1,
-                       $key2 => 2,
-                       $key3 => 3
-               ] );
+               $ok = $this->cache->setMulti( [ $key1 => 1, $key2 => 2, $key3 => 3 ] );
+
+               $this->assertTrue( $ok, "setMulti() succeeded" );
+               $this->assertEquals(
+                       3,
+                       count( $this->cache->getMulti( [ $key1, $key2, $key3 ] ) ),
+                       "setMulti() succeeded via getMulti() check"
+               );
 
                $ok = $this->cache->changeTTLMulti( [ $key1, $key2, $key3 ], 300 );
                $this->assertTrue( $ok, "TTL bumped for all keys" );
@@ -180,7 +184,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::add
+        * @covers MediumSpecificBagOStuff::add
         */
        public function testAdd() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -190,7 +194,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::get
+        * @covers MediumSpecificBagOStuff::get
         */
        public function testGet() {
                $value = [ 'this' => 'is', 'a' => 'test' ];
@@ -201,9 +205,9 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::get
-        * @covers BagOStuff::set
-        * @covers BagOStuff::getWithSetCallback
+        * @covers MediumSpecificBagOStuff::get
+        * @covers MediumSpecificBagOStuff::set
+        * @covers MediumSpecificBagOStuff::getWithSetCallback
         */
        public function testGetWithSetCallback() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -220,7 +224,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::incr
+        * @covers MediumSpecificBagOStuff::incr
         */
        public function testIncr() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -232,7 +236,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::incrWithInit
+        * @covers MediumSpecificBagOStuff::incrWithInit
         */
        public function testIncrWithInit() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -244,7 +248,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::getMulti
+        * @covers MediumSpecificBagOStuff::getMulti
         */
        public function testGetMulti() {
                $value1 = [ 'this' => 'is', 'a' => 'test' ];
@@ -284,8 +288,8 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::setMulti
-        * @covers BagOStuff::deleteMulti
+        * @covers MediumSpecificBagOStuff::setMulti
+        * @covers MediumSpecificBagOStuff::deleteMulti
         */
        public function testSetDeleteMulti() {
                $map = [
@@ -316,10 +320,10 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::get
-        * @covers BagOStuff::getMulti
-        * @covers BagOStuff::merge
-        * @covers BagOStuff::delete
+        * @covers MediumSpecificBagOStuff::get
+        * @covers MediumSpecificBagOStuff::getMulti
+        * @covers MediumSpecificBagOStuff::merge
+        * @covers MediumSpecificBagOStuff::delete
         */
        public function testSetSegmentable() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -366,7 +370,7 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::getScopedLock
+        * @covers MediumSpecificBagOStuff::getScopedLock
         */
        public function testGetScopedLock() {
                $key = $this->cache->makeKey( self::TEST_KEY );
@@ -390,8 +394,8 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::__construct
-        * @covers BagOStuff::trackDuplicateKeys
+        * @covers MediumSpecificBagOStuff::__construct
+        * @covers MediumSpecificBagOStuff::trackDuplicateKeys
         */
        public function testReportDupes() {
                $logger = $this->createMock( Psr\Log\NullLogger::class );
@@ -415,8 +419,8 @@ class BagOStuffTest extends MediaWikiTestCase {
        }
 
        /**
-        * @covers BagOStuff::lock()
-        * @covers BagOStuff::unlock()
+        * @covers MediumSpecificBagOStuff::lock()
+        * @covers MediumSpecificBagOStuff::unlock()
         */
        public function testLocking() {
                $key = 'test';
index 890218c..76bfea2 100644 (file)
@@ -767,8 +767,8 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $localBag = $this->getMockBuilder( HashBagOStuff::class )
                        ->setMethods( [ 'getMulti' ] )->getMock();
                $localBag->expects( $this->exactly( 1 ) )->method( 'getMulti' )->willReturn( [
-                       WANObjectCache::VALUE_KEY_PREFIX . 'k1' => 'val-id1',
-                       WANObjectCache::VALUE_KEY_PREFIX . 'k2' => 'val-id2'
+                       'WANCache:v:' . 'k1' => 'val-id1',
+                       'WANCache:v:' . 'k2' => 'val-id2'
                ] );
                $wanCache = new WANObjectCache( [ 'cache' => $localBag ] );
 
@@ -964,7 +964,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertEquals( 1, $calls, 'Value was populated' );
 
                // Acquire the mutex to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $checkKeys = [ wfRandomString() ]; // new check keys => force misses
                $ret = $cache->getWithSetCallback( $key, 30, $func,
@@ -1026,7 +1026,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $mockWallClock += 2; // low logical TTL expired
                // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $ret = $cache->getWithSetCallback( $key, 300, $func, [ 'lockTSE' => 5 ] );
                $this->assertEquals( $value, $ret );
@@ -1034,7 +1034,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                $mockWallClock += 301; // physical TTL expired
                // Acquire a lock to verify that getWithSetCallback uses lockTSE properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $ret = $cache->getWithSetCallback( $key, 300, $func, [ 'lockTSE' => 5 ] );
                $this->assertEquals( $value, $ret );
@@ -1092,7 +1092,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $mockWallClock += 0.2; // interim keys not brand new
 
                // Acquire a lock to verify that getWithSetCallback uses busyValue properly
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
 
                $checkKeys = [ wfRandomString() ]; // new check keys => force misses
                $ret = $cache->getWithSetCallback( $key, 30, $func,
@@ -1111,14 +1111,14 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertEquals( $busyValue, $ret, 'Callback was not used; used busy value' );
                $this->assertEquals( 2, $calls, 'Callback was not used; used busy value' );
 
-               $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
+               $this->internalCache->delete( 'WANCache:m:' . $key );
                $mockWallClock += 0.001; // cached values will be newer than tombstone
                $ret = $cache->getWithSetCallback( $key, 30, $func,
                        [ 'lockTSE' => 30, 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
                $this->assertEquals( $value, $ret, 'Callback was used; saved interim' );
                $this->assertEquals( 3, $calls, 'Callback was used; saved interim' );
 
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
                $ret = $cache->getWithSetCallback( $key, 30, $func,
                        [ 'busyValue' => $busyValue, 'checkKeys' => $checkKeys ] );
                $this->assertEquals( $value, $ret, 'Callback was not used; used interim' );
@@ -1208,7 +1208,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                // Fake initial check key to be set in the past. Otherwise we'd have to sleep for
                // several seconds during the test to assert the behaviour.
                foreach ( [ $checkAll, $check1, $check2 ] as $checkKey ) {
-                       $cache->touchCheckKey( $checkKey, WANObjectCache::HOLDOFF_NONE );
+                       $cache->touchCheckKey( $checkKey, WANObjectCache::HOLDOFF_TTL_NONE );
                }
 
                $mockWallClock += 0.100;
@@ -1330,7 +1330,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $this->assertLessThan( 0, $curTTL, "Deleted key is tombstoned and has current TTL < 0" );
 
                $this->cache->set( $key, $value );
-               $this->cache->delete( $key, WANObjectCache::HOLDOFF_NONE );
+               $this->cache->delete( $key, WANObjectCache::HOLDOFF_TTL_NONE );
 
                $curTTL = null;
                $v = $this->cache->get( $key, $curTTL );
@@ -1462,10 +1462,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 3, $wasCalled, 'Value regenerated (got mutex)' ); // sets interim
                // Lock up the mutex so interim cache is used
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 3, $wasCalled, 'Value interim cached (failed mutex)' );
-               $this->internalCache->delete( $cache::MUTEX_KEY_PREFIX . $key );
+               $this->internalCache->delete( 'WANCache:m:' . $key );
 
                $cache->useInterimHoldOffCaching( false );
 
@@ -1482,7 +1482,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 4, $wasCalled, 'Value still regenerated (got mutex)' );
                // Lock up the mutex so interim cache is used
-               $this->internalCache->add( $cache::MUTEX_KEY_PREFIX . $key, 1, 0 );
+               $this->internalCache->add( 'WANCache:m:' . $key, 1, 0 );
                $v = $cache->getWithSetCallback( $key, 60, $func );
                $this->assertEquals( 5, $wasCalled, 'Value still regenerated (failed mutex)' );
        }
@@ -1548,16 +1548,16 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
 
                // Two check keys are newer (given hold-off) than $key, another is older
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey2,
-                       WANObjectCache::PURGE_VAL_PREFIX . ( $priorTime - 3 )
+                       'WANCache:t:' . $tKey2,
+                       'PURGED:' . ( $priorTime - 3 )
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey2,
-                       WANObjectCache::PURGE_VAL_PREFIX . ( $priorTime - 5 )
+                       'WANCache:t:' . $tKey2,
+                       'PURGED:' . ( $priorTime - 5 )
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey1,
-                       WANObjectCache::PURGE_VAL_PREFIX . ( $priorTime - 30 )
+                       'WANCache:t:' . $tKey1,
+                       'PURGED:' . ( $priorTime - 30 )
                );
                $this->cache->set( $key, $value, 30 );
 
@@ -1584,30 +1584,30 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                $badTime = microtime( true ) - 300;
 
                $this->internalCache->set(
-                       WANObjectCache::VALUE_KEY_PREFIX . $vKey1,
+                       'WANCache:v:' . $vKey1,
                        [
-                               WANObjectCache::FLD_FORMAT_VERSION => WANObjectCache::VERSION,
-                               WANObjectCache::FLD_VALUE => $value,
-                               WANObjectCache::FLD_TTL => 3600,
-                               WANObjectCache::FLD_TIME => $goodTime
+                               0 => 1,
+                               1 => $value,
+                               2 => 3600,
+                               3 => $goodTime
                        ]
                );
                $this->internalCache->set(
-                       WANObjectCache::VALUE_KEY_PREFIX . $vKey2,
+                       'WANCache:v:' . $vKey2,
                        [
-                               WANObjectCache::FLD_FORMAT_VERSION => WANObjectCache::VERSION,
-                               WANObjectCache::FLD_VALUE => $value,
-                               WANObjectCache::FLD_TTL => 3600,
-                               WANObjectCache::FLD_TIME => $badTime
+                               0 => 1,
+                               1 => $value,
+                               2 => 3600,
+                               3 => $badTime
                        ]
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey1,
-                       WANObjectCache::PURGE_VAL_PREFIX . $goodTime
+                       'WANCache:t:' . $tKey1,
+                       'PURGED:' . $goodTime
                );
                $this->internalCache->set(
-                       WANObjectCache::TIME_KEY_PREFIX . $tKey2,
-                       WANObjectCache::PURGE_VAL_PREFIX . $badTime
+                       'WANCache:t:' . $tKey2,
+                       'PURGED:' . $badTime
                );
 
                $this->assertEquals( $value, $this->cache->get( $vKey1 ) );
@@ -1632,10 +1632,10 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                        ->setMethods( [ 'get', 'changeTTL' ] )->getMock();
                $backend->expects( $this->once() )->method( 'get' )
                        ->willReturn( [
-                               WANObjectCache::FLD_FORMAT_VERSION => WANObjectCache::VERSION,
-                               WANObjectCache::FLD_VALUE => 'value',
-                               WANObjectCache::FLD_TTL => 3600,
-                               WANObjectCache::FLD_TIME => 300,
+                               0 => 1,
+                               1 => 'value',
+                               2 => 3600,
+                               3 => 300,
                        ] );
                $backend->expects( $this->once() )->method( 'changeTTL' )
                        ->willReturn( false );
@@ -1721,7 +1721,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                ] );
 
                $localBag->expects( $this->once() )->method( 'set' )
-                       ->with( "/*/mw-wan/" . $wanCache::VALUE_KEY_PREFIX . "test" );
+                       ->with( "/*/mw-wan/" . 'WANCache:v:' . "test" );
 
                $wanCache->delete( 'test' );
        }
@@ -1737,7 +1737,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                ] );
 
                $localBag->expects( $this->once() )->method( 'set' )
-                       ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" );
+                       ->with( "/*/mw-wan/" . 'WANCache:t:' . "test" );
 
                $wanCache->touchCheckKey( 'test' );
        }
@@ -1753,7 +1753,7 @@ class WANObjectCacheTest extends PHPUnit\Framework\TestCase {
                ] );
 
                $localBag->expects( $this->once() )->method( 'delete' )
-                       ->with( "/*/mw-wan/" . $wanCache::TIME_KEY_PREFIX . "test" );
+                       ->with( "/*/mw-wan/" . 'WANCache:t:' . "test" );
 
                $wanCache->resetCheckKey( 'test' );
        }
diff --git a/tests/phpunit/includes/resourceloader/ResourceLoaderFilePathTest.php b/tests/phpunit/includes/resourceloader/ResourceLoaderFilePathTest.php
deleted file mode 100644 (file)
index 292340b..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-class ResourceLoaderFilePathTest extends PHPUnit\Framework\TestCase {
-       /**
-        * @covers ResourceLoaderFilePath::__construct
-        */
-       public function testConstructor() {
-               $resourceLoaderFilePath = new ResourceLoaderFilePath(
-                       'dummy/path', 'localBasePath', 'remoteBasePath'
-               );
-
-               $this->assertInstanceOf( ResourceLoaderFilePath::class, $resourceLoaderFilePath );
-       }
-
-       /**
-        * @covers ResourceLoaderFilePath::getLocalPath
-        */
-       public function testGetLocalPath() {
-               $resourceLoaderFilePath = new ResourceLoaderFilePath(
-                       'dummy/path', 'localBasePath', 'remoteBasePath'
-               );
-
-               $this->assertSame(
-                       'localBasePath/dummy/path', $resourceLoaderFilePath->getLocalPath()
-               );
-       }
-
-       /**
-        * @covers ResourceLoaderFilePath::getRemotePath
-        */
-       public function testGetRemotePath() {
-               $resourceLoaderFilePath = new ResourceLoaderFilePath(
-                       'dummy/path', 'localBasePath', 'remoteBasePath'
-               );
-
-               $this->assertSame(
-                       'remoteBasePath/dummy/path', $resourceLoaderFilePath->getRemotePath()
-               );
-       }
-
-       /**
-        * @covers ResourceLoaderFilePath::getPath
-        */
-       public function testGetPath() {
-               $resourceLoaderFilePath = new ResourceLoaderFilePath(
-                       'dummy/path', 'localBasePath', 'remoteBasePath'
-               );
-
-               $this->assertSame(
-                       'dummy/path', $resourceLoaderFilePath->getPath()
-               );
-       }
-}
index f9e30f0..64148b0 100644 (file)
@@ -2,13 +2,17 @@
 
 namespace MediaWiki\Session;
 
+use CachedBagOStuff;
+use HashBagOStuff;
+use RequestContext;
+
 /**
  * BagOStuff with utility functions for MediaWiki\\Session\\* testing
  */
-class TestBagOStuff extends \CachedBagOStuff {
+class TestBagOStuff extends CachedBagOStuff {
 
        public function __construct() {
-               parent::__construct( new \HashBagOStuff );
+               parent::__construct( new HashBagOStuff );
        }
 
        /**
@@ -51,7 +55,7 @@ class TestBagOStuff extends \CachedBagOStuff {
         * @param array|mixed $blob Session metadata and data
         */
        public function setRawSession( $id, $blob ) {
-               $expiry = \RequestContext::getMain()->getConfig()->get( 'ObjectCacheSessionExpiry' );
+               $expiry = RequestContext::getMain()->getConfig()->get( 'ObjectCacheSessionExpiry' );
                $this->set( $this->makeKey( 'MWSession', $id ), $blob, $expiry );
        }
 
index e31357c..a57745b 100644 (file)
@@ -35,10 +35,15 @@ class SpecialMuteTest extends SpecialPageTestBase {
 
        /**
         * @covers SpecialMute::execute
-        * @expectedExceptionMessage Muting users from sending you emails is not enabled
+        * @expectedExceptionMessage Mute features are unavailable
         * @expectedException ErrorPageError
         */
        public function testEmailBlacklistNotEnabled() {
+               $this->setTemporaryHook(
+                       'SpecialMuteModifyFormFields',
+                       null
+               );
+
                $this->setMwGlobals( [
                        'wgEnableUserEmailBlacklist' => false
                ] );
@@ -72,7 +77,7 @@ class SpecialMuteTest extends SpecialPageTestBase {
                $loggedInUser->confirmEmail();
                $loggedInUser->saveSettings();
 
-               $fauxRequest = new FauxRequest( [ 'wpMuteEmail' => 1 ], true );
+               $fauxRequest = new FauxRequest( [ 'wpemail-blacklist' => true ], true );
                list( $html, ) = $this->executeSpecialPage(
                        $targetUser->getName(), $fauxRequest, 'qqx', $loggedInUser
                );
@@ -99,7 +104,7 @@ class SpecialMuteTest extends SpecialPageTestBase {
                $loggedInUser->confirmEmail();
                $loggedInUser->saveSettings();
 
-               $fauxRequest = new FauxRequest( [ 'wpMuteEmail' => false ], true );
+               $fauxRequest = new FauxRequest( [ 'wpemail-blacklist' => false ], true );
                list( $html, ) = $this->executeSpecialPage(
                        $targetUser->getName(), $fauxRequest, 'qqx', $loggedInUser
                );
index b992a86..d7901be 100644 (file)
@@ -171,7 +171,7 @@ class SiteConfigurationTest extends \MediaWikiUnitTestCase {
                $this->assertEquals(
                        'wiki',
                        $this->mConf->get( 'SimpleKey', 'eswiki', 'wiki' ),
-                       'get(): simple setting on an non-existing wiki'
+                       'get(): simple setting on a non-existing wiki'
                );
 
                // Fallback
@@ -209,12 +209,12 @@ class SiteConfigurationTest extends \MediaWikiUnitTestCase {
                $this->assertEquals(
                        'wiki',
                        $this->mConf->get( 'Fallback', 'eswiki', 'wiki' ),
-                       'get(): fallback setting on an non-existing wiki'
+                       'get(): fallback setting on a non-existing wiki'
                );
                $this->assertEquals(
                        'tag',
                        $this->mConf->get( 'Fallback', 'eswiki', 'wiki', [], [ 'tag' ] ),
-                       'get(): fallback setting on an non-existing wiki (with wiki tag)'
+                       'get(): fallback setting on a non-existing wiki (with wiki tag)'
                );
 
                // Merging
@@ -263,12 +263,12 @@ class SiteConfigurationTest extends \MediaWikiUnitTestCase {
                $this->assertEquals(
                        $common,
                        $this->mConf->get( 'MergeIt', 'eswiki', 'wiki' ),
-                       'get(): merging setting on an non-existing wiki'
+                       'get(): merging setting on a non-existing wiki'
                );
                $this->assertEquals(
                        $commonTag,
                        $this->mConf->get( 'MergeIt', 'eswiki', 'wiki', [], [ 'tag' ] ),
-                       'get(): merging setting on an non-existing wiki (with tag)'
+                       'get(): merging setting on a non-existing wiki (with tag)'
                );
        }
 
@@ -324,7 +324,7 @@ class SiteConfigurationTest extends \MediaWikiUnitTestCase {
                $this->assertEquals(
                        'es wiki eswiki',
                        $this->mConf->get( 'WithParams', 'eswiki', 'wiki' ),
-                       'get(): parameter replacement on an non-existing wiki'
+                       'get(): parameter replacement on a non-existing wiki'
                );
        }
 
diff --git a/tests/phpunit/unit/includes/resourceloader/ResourceLoaderFilePathTest.php b/tests/phpunit/unit/includes/resourceloader/ResourceLoaderFilePathTest.php
new file mode 100644 (file)
index 0000000..b1db383
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * @covers ResourceLoaderFilePath
+ */
+class ResourceLoaderFilePathTest extends MediaWikiUnitTestCase {
+
+       public function testConstructor() {
+               $path = new ResourceLoaderFilePath( 'dummy/path', '/local', '/remote' );
+
+               $this->assertInstanceOf( ResourceLoaderFilePath::class, $path );
+       }
+
+       public function testGetters() {
+               $path = new ResourceLoaderFilePath( 'dummy/path', '/local', '/remote' );
+
+               $this->assertSame( '/local/dummy/path', $path->getLocalPath() );
+               $this->assertSame( '/remote/dummy/path', $path->getRemotePath() );
+               $this->assertSame( '/local', $path->getLocalBasePath() );
+               $this->assertSame( '/remote', $path->getRemoteBasePath() );
+               $this->assertSame( 'dummy/path', $path->getPath() );
+       }
+}
index 521eee6..ed6c78a 100644 (file)
@@ -1,3 +1,9 @@
+## 0.4.0 / 2019-07-18
+
+* Util: Added a `waitForModuleState()` method.
+* Api: Added optional `username`, `password` and `baseUrl` parameters to `edit()` method.
+* RunJobs: Unpublished `getJobCount()`, `log()`, `runThroughMainPageRequests()` methods.
+
 ## 0.3.0 / 2019-01-25
 
 * RunJobs: Added initial version.
index a52d594..dc16e81 100644 (file)
@@ -22,7 +22,7 @@ Utilities to interact with the MediaWiki API. Uses the [mwbot](https://github.co
 Actions are performed logged-in using `browser.options.username` and `browser.options.password`,
 which typically come from `MEDIAWIKI_USER` and `MEDIAWIKI_PASSWORD` environment variables.
 
-* `edit(title, content)`
+* `edit(title, content [, string username [, string password [, string baseUrl ] ] ])`
 * `delete(title, reason)`
 * `createAccount(username, password)`
 * `blockUser(username, expiry)`
@@ -33,6 +33,13 @@ which typically come from `MEDIAWIKI_USER` and `MEDIAWIKI_PASSWORD` environment
 Use the static `RunJobs.run()` method to ensure that any queued jobs are executed before
 making assertions that depend on its outcome.
 
+### Util
+
+`Util` is a collection of popular utility methods.
+
+* `getTestString([ string prefix ])`
+* `waitForModuleState(string moduleName [, string moduleStatus [, number timeout ] ])`
+
 ## Versioning
 
 This package follows [Semantic Versioning guidelines](https://semver.org/) for its releases. In
index 4babd08..423487f 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "wdio-mediawiki",
-  "version": "0.3.0",
+  "version": "0.4.0",
   "description": "WebdriverIO plugin for testing a MediaWiki site.",
   "homepage": "https://gerrit.wikimedia.org/g/mediawiki/core/+/master/tests/selenium/wdio-mediawiki/",
   "license": "MIT",